diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 40277dc18..5de2397f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ defaults: jobs: cmake-builds: + name: CMake uses: ./.github/workflows/cmake-builds.yml makefile: @@ -40,28 +41,26 @@ jobs: - scelbi 3b2 i701 i704 i7010 i7070 i7080 i7090 sigma uc15 i650 sel32 intel-mds ibm1130 steps: - uses: actions/checkout@v4 - ## Workaround for remnant symlinks in /usr/local pointing back to - ## macOS frameworks. - ## - ## Future: Will have to keep an eye on SDL_ttf's Python dependency - ## so that the correct/appropriate Python version is removed. - - name: Remnant symlink cleanup (macOS) + + ## Nuke homebrew and start with a clean instance: + - name: Reinstall HomeBrew (macOS) if: ${{runner.os == 'macOS'}} run: | - brew unlink python@3 || true - brew uninstall --ignore-dependencies python@3 || true - brew unlink python@3.12 || true - brew uninstall --ignore-dependencies python@3.12 || true - for f in $(find /usr/local/bin -type l -print); do \ - (readlink $f | grep -q -s "/Library") && echo Removing "$f" && rm -f "$f"; \ - done || exit 0 + /usr/bin/sudo /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)" + [ -d /opt/homebrew ] && ( sudo rm -rf /opt/homebrew/* || true ) + [ -d /usr/local ] && ( sudo rm -rf /usr/local/* || true ) + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" + ## Install our regular dependencies. - name: Install dependencies (macOS) if: ${{runner.os == 'macOS'}} - run: sh -ex .travis/deps.sh osx + run: | + sh -ex .travis/deps.sh osx + - name: Install dependencies (Linux) if: ${{runner.os == 'Linux'}} run: sh -ex .travis/deps.sh linux + - name: makefile build env: SIM: ${{matrix.simulators}} diff --git a/.github/workflows/cmake-builds.yml b/.github/workflows/cmake-builds.yml index d489f5987..fded2ecd7 100644 --- a/.github/workflows/cmake-builds.yml +++ b/.github/workflows/cmake-builds.yml @@ -24,6 +24,7 @@ jobs: run: | sh -ex .travis/deps.sh linux sudo apt install -ym ninja-build + - name: cmake-builder.sh run: | cmake/cmake-builder.sh --config Release --flavor ninja --lto --notest --parallel --verbose --cpack_suffix x86_64-${{matrix.os}} @@ -47,7 +48,7 @@ jobs: cmake-macOS: - name: macOS 12+ + name: macOS runs-on: ${{ matrix.os }} strategy: #- @@ -57,8 +58,10 @@ jobs: # that the build produces. # # (*) "artefact" for the rest of the Anglosphere - + # + # As of 18 NOV 2024, Github deprecated macos-12. matrix: - os: [macos-12, macos-13, macos-14] + os: [macos-13, macos-14] env: CPACK_SUFFIX: ${{matrix.os != 'macos-14' && 'x86_64' || 'm1'}}.${{matrix.os}} @@ -66,23 +69,24 @@ jobs: steps: - uses: actions/checkout@v4 - ## Workaround for remnant symlinks in /usr/local pointing back to - ## macOS frameworks. - ## - ## Future: Will have to keep an eye on SDL_ttf's Python dependency - ## so that the correct/appropriate Python version is removed. - - name: Remnant symlink cleanup - run: | - brew unlink python@3 || true - brew uninstall --ignore-dependencies python@3 || true - brew unlink python@3.12 || true - brew uninstall --ignore-dependencies python@3.12 || true - for f in $(find /usr/local/bin -type l -print); do \ - (readlink $f | grep -q -s "/Library") && echo Removing "$f" && rm -f "$f"; \ - done || exit 0 + ## Nuke homebrew and start with a clean instance. + - name: Reinstall HomeBrew (macOS) + run: | + /usr/bin/sudo /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh)" + [ -d /opt/homebrew ] && ( sudo rm -rf /opt/homebrew/* || true ) + [ -d /usr/local ] && ( sudo rm -rf /usr/local/* || true ) + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" - name: Install dependencies - run: sh -ex .travis/deps.sh osx + run: | + sh -ex .travis/deps.sh osx + + - name: libslirp module + run: | + git submodule sync + git submodule set-url -- libslirp https://gitlab.freedesktop.org/bscottmichel/libslirp-minimal.git + git submodule set-branch --branch incr-minimal libslirp + git submodule update --init --recursive --remote - name: cmake-builder.sh run: | @@ -123,6 +127,12 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v4 + - name: libslirp module + run: | + git submodule sync + git submodule set-url -- libslirp https://gitlab.freedesktop.org/bscottmichel/libslirp-minimal.git + git submodule set-branch --branch incr-minimal libslirp + git submodule update --init --recursive --remote - name: Install v141_xp (XP toolkit) and build SIMH shell: pwsh run: | @@ -189,8 +199,25 @@ jobs: cmake-vs2022: name: VS 2022 Win10 native VCPKG runs-on: windows-latest + strategy: + #- + # The CMake builds produce artifacts (*) and the runner image's name is + # used in the artifact's name, simh-4.1.0-x86_64-ubuntu-20.04.deb. + # Consequently, each runner image is enumerated for each artifact (*) + # that the build produces. + # + # (*) "artefact" for the rest of the Anglosphere + #- + matrix: + build: [vs2022, vs2022-x64] steps: - uses: actions/checkout@v4 + - name: libslirp module + run: | + git submodule sync + git submodule set-url -- libslirp https://gitlab.freedesktop.org/bscottmichel/libslirp-minimal.git + git submodule set-branch --branch incr-minimal libslirp + git submodule update --init --recursive --remote - name: vs2022 build shell: pwsh run: | @@ -206,34 +233,36 @@ jobs: Push-Location $env:VCPKG_ROOT git pull Pop-Location - ./cmake/cmake-builder.ps1 -flavor vs2022 -config Release -clean -lto -verbose -notest -cpack_suffix win32-native + ./cmake/cmake-builder.ps1 -flavor ${{matrix.build}} -config Release -clean -lto -verbose ` + -notest ` + -cpack_suffix ${{matrix.build}} - name: SIMH simulator suite test shell: pwsh run: | - ./cmake/cmake-builder.ps1 -flavor vs2022 -config Release -testOnly + ./cmake/cmake-builder.ps1 -flavor ${{matrix.build}} -config Release -testOnly ## Install isn't strictly necessary, but it's a good way to see what dependencies ## (IMPORTED_RUNTIME_ARTIFACTS) get installed. - name: Install shell: pwsh run: | - cmake/cmake-builder.ps1 -config Release -flavor vs2022 -installOnly + cmake/cmake-builder.ps1 -config Release -flavor ${{matrix.build}} -installOnly - name: SIMH packaging shell: pwsh run: | - cd cmake\build-vs2022 + cd cmake\build-${{matrix.build}} cpack -G "NSIS;WIX;ZIP" -C Release - name: Upload ZIP uses: actions/upload-artifact@v4 with: - name: simh-4.1.0-win32-vs2022.zip - path: cmake/build-vs2022/simh-4.1.0-win32-native.zip + name: simh-4.1.0-${{matrix.build}}.zip + path: cmake/build-${{matrix.build}}/simh-4.1.0-${{matrix.build}}.zip - name: Upload EXE installer uses: actions/upload-artifact@v4 with: - name: simh-4.1.0-win32-vs2022.exe - path: cmake/build-vs2022/simh-4.1.0-win32-native.exe + name: simh-4.1.0-${{matrix.build}}.exe + path: cmake/build-${{matrix.build}}/simh-4.1.0-${{matrix.build}}.exe - name: Upload MSI installer uses: actions/upload-artifact@v4 with: - name: simh-4.1.0-win32-vs2022.msi - path: cmake/build-vs2022/simh-4.1.0-win32-native.msi + name: simh-4.1.0-${{matrix.build}}.msi + path: cmake/build-${{matrix.build}}/simh-4.1.0-${{matrix.build}}.msi diff --git a/.travis/deps.sh b/.travis/deps.sh index dc08ec7fc..69449108b 100755 --- a/.travis/deps.sh +++ b/.travis/deps.sh @@ -30,6 +30,19 @@ install_linux() { sudo apt-get install -ym cmake cmake-data } +install_mingw32() { + ## Doesn't have libpcap or cmake's extra modules. Not that this + ## makes much of a difference. + pacman -S --needed mingw-w64-i686-ninja \ + mingw-w64-i686-cmake \ + mingw-w64-i686-gcc \ + mingw-w64-i686-make \ + mingw-w64-i686-pcre \ + mingw-w64-i686-freetype \ + mingw-w64-i686-SDL2 \ + mingw-w64-i686-SDL2_ttf +} + install_mingw64() { pacman -S --needed mingw-w64-x86_64-ninja \ mingw-w64-x86_64-cmake \ @@ -71,7 +84,7 @@ install_clang64() { case "$1" in - osx|macports|linux|mingw64|ucrt64|clang64) + osx|macports|linux|mingw32|mingw64|ucrt64|clang64) install_"$1" ;; arch-linux) diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 62357a7ca..61abad7b7 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -50,6 +50,107 @@ bridge it with a physical network interface). The following steps were performed to get a working SIMH vax simulator sharing a physical NIC and allowing Host<->SIMH vax communications: +Linux (iproute2 utilities, /etc/network/interfaces): + + You have the iproute2 utility family installed if typing "ip addr show" + at the shell prompt shows you a list of network interfaces and their + associated IP addresses: + + $ ip addr show + 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever + 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 + ... + + The following example /etc/network/interfaces script creates a bridge + interface "br-main" and attaches the "eth0" and "tap0" interfaces to + the bridge. + + Linux networking reads the /etc/network/intefaces script to configure your + network at boot time, so this bridge is "one and done". The tap0 interface + will persist until such time as you decide it's no longer useful and remove + the bridge configuration from the /etc/network/interfaces script. + + ## /etc/network/interfaces bridge configuration example: + ## + ## THIS IS AN EXAMPLE CONFIGURATION. ADAPT IT TO FIT AND CONFORM + ## TO YOUR NETWORK CONFIGURATION. + + auto lo br-main # Configure lo0, br-main at boot time + + iface lo inet loopback + + iface br-main inet manual + ##------------------------------------------------------------- + ## If you don't use iptables to firewall your Linux, you can + ## comment out the next line. + ##------------------------------------------------------------- + pre-up modprobe br_netfilter + pre-up ip link add name br-main type bridge + up ip link set dev br-main up + ##------------------------------------------------------------- + ## Static IPv4 address configuration (you don't use DHCP): + ## + ## Replace HOSTIP4/NETBITS4 with your IPv4 address and bits in + ## your network mask, e.g., 192.168.1.20/24. + ## + ## Replace GATEWAY4 with your router's IPv4 address. + ## + ## If you use DHCP for IPv4 address configuration, comment out + ## the next two lines. + ##------------------------------------------------------------- + up ip address add HOSTIP4/NETBITS4 dev br-main + up ip route append default via GATEWAY4 dev br-main + ##------------------------------------------------------------- + ## IPv6: Replace HOSTIP6/NETBITS6 with your IPv6 address and + ## bits in your network mask, e.g., 2001:db8::/64. + ## + ## Replace GATEWAY6 with your router's IPv6 address. + ## + ## If your network sends Router Advertisements, comment out the + ## next two lines. + ##------------------------------------------------------------- + up ip address HOSTIP6/NETBITS6 dev br-main + up ip route append ::0 via GATEWAY6 + ##------------------------------------------------------------- + ## DHCP address configuration: + ## + ## If you prefer static IPv4 configuration, comment out the next + ## line and refer above. + ##------------------------------------------------------------- + up dhclient br-main + ##------------------------------------------------------------- + ## Attach eth0 and tap0: + ##------------------------------------------------------------- + up ifup eth0 + up ifup tap0 + ##------------------------------------------------------------- + ## If you don't use iptables to firewall your Linux, you can + ## comment out the next line. + ##------------------------------------------------------------- + post-up echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables + pre-down ifdown tap0 + pre-down ifdown eth0 + down ip link del dev br-main + + iface eth0 inet manual + up ip link set eth0 master br-main + down ip link set eth0 nomaster + + iface tap0 inet manual + pre-up ip tuntap add tap0 mode tap user USER + up ip link set tap0 master br-main + down ip link set dev tap0 nomaster + post-down ip link del dev tap0 + ## /etc/network/interfaces bridge configuration example ends... + + When you run the simulator: "attach xq tap:tap0" + + Linux (Ubuntu 10.04): apt-get install make apt-get install libpcap-dev @@ -181,37 +282,127 @@ OSX: install the net/vde2 package. ------------------------------------------------------------------------------- -Another alternative to direct pcap and tun/tap networking on all environments is -NAT (SLiRP) networking. NAT networking is limited to only IP network protocols -so DECnet, LAT and Clusting can't work on a NAT connected interface, but this may -be the easiest solution for many folks. +NAT (libslirp) networking is another network alternative to direct pcap and +tun/tap networking on all environments. NAT (libslirp) is always available as a +network option. + +NAT is limited to IP network protocols only, so DECnet, LAT and Clusting can't +work on a NAT-connected interface. + +Basic usage: sim> attach xq nat: -The simulator can use static IP addresses of 10.0.2.4 thru 10.0.2.14 with a -netmask of 255.255.255.0 and a gateway of 10.0.2.2 and a nameserver of 10.0.2.3. -If the simulated machine uses DHCP it will get the address 10.0.2.15. Various -NAT based parameters can be configured on the attach command. HELP XQ ATTACH -will provide useful information. Host to simulator connectivity can be -achieved for a simulator which gets its IP address via DHCP with the following -command: +This attaches NAT to the VAX xq Ethernet interface with the following default +IP parameters: + + - Simulator's IP: 10.0.2.4 - 10.0.2.14 are available as static IPv4 + addresses. DHCP is also available with IPv4 addresses starting at + 10.0.2.15. + - Gateway/Router IPv4 address: 10.0.2.2 (i.e., the simulator's O/S + should use this as the default route.) + - DNS resolver's IPv4 address: 10.0.2.3 + +Advanced usage: + + sim> attach xq nat:network=172.16.1.4/24,gateway=172.16.1.2,dns=172.16.1.3 + + This example overrides the NAT default and uses the following parameters, + where the NAT network uses the set of IPv4 addresses belonging to the + 172.16.1.0/24 network: + + - "network": Specifies the IPv4 aaddress nd network mask (CIDR format, in bits) + for the simulator interface. This basically tells libslirp which IPv4 address + the simulator uses (but you still have to configure the simulator's interface + with the required IPv4 configuration.) + - "gateway": Specifies the default router's IPv4 address. + - "dns": The DNS resolver's IPv4 address + +Mapping host ports to simulator ports has the general format: + + sim> attach intf nat:tcp=:: + +For example, to redirect the simulator's telnet port (port 23) to the host's +port 2323: + + sim> attach xq nat:tcp=2323:10.0.2.15:23,tcp=2121:10.0.2.15:21 + + Note 1: 10.0.2.15 is the first libslirp DHCP-assigned address, so this + example assumes that the VAX simulator uses DHCP to configure the + xq device's IPv4 configuration. + + Note 2: Unless the simulator runs with root or administrator privileges, + ports below 1024 are reserved. Mappings should use host port + numbers greater than 1024. - sim> attach xq nat:tcp=2323:10.0.2.15:23,tcp=2121:10.0.2.15:21 +Users can subsequently telnet to localhost:2323 to reach the simulator via +telnet. -The host computer can telnet to localhost:2323 to reach the simulator via -telnet, etc. +For the PDP10-KL simulator, the following IMP configuration is a useful Turism +starting point: -Additionally NAT based networking is useful to allow host systems with WiFi -networking to a) reach the simulated system and b) allow the simulated system -to reach out to the Internet. + set imp enabled + set imp mac=e2:6c:84:1d:34:a3 + set imp host=192.168.1.100 + set imp ip=192.168.2.4/24 + set imp gw=192.168.2.2 + at imp nat:dns=192.168.2.3,gateway=192.168.2.2,network=192.168.2.0/24,tcp=2023:192.168.2.4:23,tcp=2021:192.168.2.4:21,tcp=9595:192.168.2.4:95 + + Note: Ensure that ITS was built with a <255,255,0,0> network mask in the + PDP-10 KL configuration (See SYSTEM;CONFIG, search for "MCOND KL," and + change NM%IMP's definition following "MCOND KL,".) Then rebuild ITS. + + Rebuilding ITS: https://github.com/PDP-10/its/blob/master/doc/NITS.md + +Connect a SUPDUP client to port 9595 on localhost, such as Putty's SUPDUP +support or the user-maintained SUPDUP client (https://github.com/PDP-10/supdup) +to discover that Today Is A Good Day To Be A Turist. Users can also connect to +localhost:2023 for telnet Turism. (If ITS is unresponsive -- check the KL's +network mask and rebuild ITS if necessary!!!) Note: As mentioned above, NAT networking is specifically capable of providing TCP/IP connectivity. Only expect TCP and UDP traffic to pass through the interface. Do not expect ICMP traffic (ping mostly) to traverse - the NAT boundary. This restriction is a consequence of host platform + past the NAT boundary. This restriction is a consequence of host platform and network limitations regarding direct user mode code generating ICMP packets. +Debugging NAT: If you need to turn on NAT debugging, the following flags exist: + + POLL Outputs poll()/select() activities when libslirp polls the active + sockets for I/O activity. + + SOCKET Trace sockets as they are added and removed from libslirp. + +These debugging flags can only be set after "attach": + + sim> set debug -n "vax-xq.out" + sim> attach xq nat:tcp=2323:10.0.2.15:23,tcp=2121:10.0.2.15:21 + sim> set xq debug=socket;poll + +If you want REALLY verbose output and want to trace NAT's Ethernet traffic, you +have to set that debugging flag before "attach" (the "ether" debug flag is part +of the underlying sim_ether.c Ethernet support, not part of NAT): + + sim> set debug -n "vax-xq.out" + sim> set xq debug=ether + sim> attach xq nat:tcp=2323:10.0.2.15:23,tcp=2121:10.0.2.15:21 + sim> set xq debug=socket;poll + +Internal libslirp debugging is set by the "slirp=" argument to "attach": + + sim> attach xq nat:tcp=2323:10.0.2.15:23,tcp=2121:10.0.2.15:21,slirp=call;misc;error;verbose_call + +where: + + CALL Outputs arguments to libslirp's functions + MISC Outputs generally useful libslirp debugging info + ERROR Outputs error diagnostics when they occur + VERBOSE_CALL Like CALL, but selectively more verbose + ALL All of the above flags + +Once turned on, however, libslirp debugging cannot be turned off until the +simulator exits. ------------------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 120da3960..1a893bec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,7 +261,12 @@ endif() ## The default runtime output directory is the BIN subdirectory in the source tree's top set(SIMH_LEGACY_INSTALL "${CMAKE_SOURCE_DIR}/BIN") if (WIN32) - string(APPEND SIMH_LEGACY_INSTALL "/Win32") + # MinGW64 environment (MinGW64, Clang64, UCRT64, ...) + if (DEFINED ENV{MSYSTEM}) + string(APPEND SIMH_LEGACY_INSTALL "/$ENV{MSYSTEM}") + else () + string(APPEND SIMH_LEGACY_INSTALL "/Win32") + endif () endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${SIMH_LEGACY_INSTALL}) @@ -365,6 +370,48 @@ if (ENABLE_CPPCHECK) set(CPPCHECK_STATUS "'cppcheck' not installed.") endif () endif () +unset(ROM_STATUS) + +if (NO_DEP_BUILD AND SIMH_BUILD_DEPS) + ## Don't build dependencies. Just wail about them. + message("") + message("Missing SIMH dependencies:") + foreach (dep ${SIMH_BUILD_DEPS}) + message(STATUS " ${dep}") + endforeach() + message("") + message("Please install the above dependencies via your platform package management or") + message("software installation system and reconfigure.") + message("") + message("Also see the .travis/deps.h file for Brew and apt packages installed during") + message("github.com workflow actions.") + message(FATAL_ERROR "Missing dependencies, cannot continue.") + + ## TODO: Assume that these dependencies are optional? +endif () + +if (NOT DEFINED DO_DEPENDENCY_BUILD OR SIMH_BUILD_DEPS) + if (DEFINED DO_DEPENDENCY_BUILD AND NOT DO_DEPENDENCY_BUILD AND SIMH_BUILD_DEPS) + message(FATAL_ERROR "Dependency libraries did not build successfully!!??") + endif() + + if (SIMH_BUILD_DEPS) + message(STATUS "Building dependency libraries as a superbuild") + set(DO_DEPENDENCY_BUILD ON CACHE BOOL "Superbuild flag" FORCE) + else () + set(DO_DEPENDENCY_BUILD OFF CACHE BOOL "Superbuild flag" FORCE) + endif () +else () + set(DO_DEPENDENCY_BUILD ${DO_DEPENDENCY_BUILD} CACHE BOOL "Superbuild flag" FORCE) +endif () + +if (NOT DO_DEPENDENCY_BUILD) + include (add_simulator) + if (WITH_NETWORK AND WITH_SLIRP) + ## Set the "Don't use GLIB" option here so that the libslirp subdirectory sees it. + set(NOGLIB TRUE CACHE BOOL "" FORCE) + add_subdirectory(libslirp) + endif () set(_feature_text "Libraries and features:\n") string(APPEND _feature_text "\n * Build with video/graphics support. ${BUILD_WITH_VIDEO}") @@ -411,47 +458,6 @@ string(APPEND _feature_text "\n") message(STATUS ${_feature_text}) unset(_feature_text) -unset(ROM_STATUS) - -if (NO_DEP_BUILD AND SIMH_BUILD_DEPS) - ## Don't build dependencies. Just wail about them. - message("") - message("Missing SIMH dependencies:") - foreach (dep ${SIMH_BUILD_DEPS}) - message(STATUS " ${dep}") - endforeach() - message("") - message("Please install the above dependencies via your platform package management or") - message("software installation system and reconfigure.") - message("") - message("Also see the .travis/deps.h file for Brew and apt packages installed during") - message("github.com workflow actions.") - message(FATAL_ERROR "Missing dependencies, cannot continue.") - - ## TODO: Assume that these dependencies are optional? -endif () - -if (NOT DEFINED DO_DEPENDENCY_BUILD OR SIMH_BUILD_DEPS) - if (DEFINED DO_DEPENDENCY_BUILD AND NOT DO_DEPENDENCY_BUILD AND SIMH_BUILD_DEPS) - message(FATAL_ERROR "Dependency libraries did not build successfully!!??") - endif() - - if (SIMH_BUILD_DEPS) - message(STATUS "Building dependency libraries as a superbuild") - set(DO_DEPENDENCY_BUILD ON CACHE BOOL "Superbuild flag" FORCE) - else () - set(DO_DEPENDENCY_BUILD OFF CACHE BOOL "Superbuild flag" FORCE) - endif () -else () - set(DO_DEPENDENCY_BUILD ${DO_DEPENDENCY_BUILD} CACHE BOOL "Superbuild flag" FORCE) -endif () - -if (NOT DO_DEPENDENCY_BUILD) - include (add_simulator) - if (WITH_SLIRP) - add_subdirectory(slirp) - endif (WITH_SLIRP) - ## Don't delete yet ## set(Python_ADDITIONAL_VERSIONS 3) ## Don't delete yet ## include(FindPythonInterp) ## Don't delete yet ## if (PYTHONINTERP_FOUND AND PYTHON_VERSION_MAJOR GREATER_EQUAL 3) @@ -508,6 +514,7 @@ else () -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} + -DCOMPILE_DEFINITIONS=${COMPILE_DEFINITIONS} INSTALL_COMMAND "" BINARY_DIR ${CMAKE_BINARY_DIR} diff --git a/I650/CMakeLists.txt b/I650/CMakeLists.txt index 3ad491982..aac3c4c9a 100644 --- a/I650/CMakeLists.txt +++ b/I650/CMakeLists.txt @@ -32,6 +32,8 @@ add_simulator(i650 if (WIN32) if (MSVC) set(I650_STACK_FLAG "/STACK:8388608") + elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang") + set(I650_STACK_FLAG "-Xlinker" "/STACK:8388608") else () set(I650_STACK_FLAG "-Wl,--stack,8388608") endif () diff --git a/PDP10/CMakeLists.txt b/PDP10/CMakeLists.txt index 197c1a30d..4980df15a 100644 --- a/PDP10/CMakeLists.txt +++ b/PDP10/CMakeLists.txt @@ -42,6 +42,7 @@ add_simulator(pdp10 DEFINES VM_PDP10 FEATURE_INT64 + USES_AIO LABEL PDP10 NO_INSTALL TEST pdp10) diff --git a/README-CMake.md b/README-CMake.md index f5778fdd8..d0db86785 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -3,9 +3,13 @@ - [Build `simh` using CMake](#build-simh-using-cmake) - [Why CMake?](#why-cmake) + - [Quickstart for the Impatient](#quickstart-for-the-impatient) + - [Unix and macOS](#unix-and-macos) + - [Windows](#windows) + - [MinGW64](#mingw64) - [Before You Begin Building...](#before-you-begin-building) - [Toolchains and Tools](#toolchains-and-tools) - - [Ninja: "failed recompaction: Permission denied"](#ninja-file-recompaction-permission-denied) + - [Ninja: "failed recompaction: Permission denied"](#ninja-failed-recompaction-permission-denied) - [Windows XP-compatible/Server 2003 binaries](#windows-xp-compatibleserver-2003-binaries) - [Feature Libraries](#feature-libraries) - [Linux, macOS and MinGW-w64](#linux-macos-and-mingw-w64) @@ -47,12 +51,84 @@ framework. A sample of the supported build environments include: - MS Visual Studio solutions (2015, 2017, 2019, 2022) - IDE build wrappers ([Sublime Text][sublime] and [CodeBlocks][codeblocks]) -Making the Windows build process less complex by automatically downloading, -building and installing dependency feature libraries, and consistent cross -platform support were the initial motivations behind a [CMake][cmake]-based -build infrastructure. Since then, that motivation expanded to supporting a wider -variety of platforms and compiler combinations, streamlining the overall compile -process, enhanced IDE integration and SIMH packaging. +In addition to enabling choices across a diverse set of development +environments, [CMake][cmake] also handles packaging artifacts (artefacts) for +installers and archives. + +## Quickstart for the Impatient + +### Unix and macOS + +```shell +$ git clone https://github.com/open-simh/simh.git +$ cd simh +$ sh .travis/deps.sh linux +$ cmake/cmake-builder.sh --flavor ninja --config Release --target pdp10-kl +$ BIN/pdp10-kl +``` + +- Replace `linux` in `sh .travis/deps.sh linux` with `arch_linux` (Arch Linux), + `osx` (macOS Brew system), or `macports` (macOS MacPorts system). + +- Replace `pdp10-kl` with your preferred or desired simulator, e.g., `vax`, + `pdp11`, etc. + +- You can find the resulting simulator binaries in the BIN/ subdirectory. + +### Windows + +`cmd`-based console: + +```cmd +C:\...> git clone https://github.com/microsoft/vcpkg.git +C:\...> cd vcpkg +C:\...> .\bootstrap-vcpkg.bat +C:\...> set VCPKG_ROOT=%cd% +C:\...> cd .. +C:\...> git clone https://github.com/open-simh/simh.git +C:\...> cd simh +C:\...> cmake\cmake-builder.ps1 vs2022-x64 Release -target pdp10-kl +C:\...> BIN\Win32\Release\pdp10-kl +``` + +PowerShell-based console: + +```powershell +PS> git clone https://github.com/microsoft/vcpkg.git +PS> cd vcpkg; .\bootstrap-vcpkg.bat +PS> $env:VCPKG_ROOT=$(get-location) +PS> cd .. +PS> git clone https://github.com/open-simh/simh.git +PS> cd simh +PS> cmake\cmake-builder.ps1 vs2022-x64 Release -target pdp10-kl +PS> BIN\Win32\Release\pdp10-kl +``` + +Replace `pdp10-kl` with your preferred or desired simulator, e.g., `vax`, +`pdp11`, etc. + +You can find the resulting simulator binaries in the BIN/Win32/_config_ +subdirectory, where _config_ is "Release", "Debug" or "RelWithDebInfo". In the +above examples, _config_ is "Release", i.e., BIN/Win32/Release. + +### MinGW64 + +```shell +$ git clone https://github.com/open-simh/simh.git +$ cd simh +$ sh .travis/deps.sh mingw64 +$ cmake/cmake-builder.sh --flavor ninja --config Release --target pdp10-kl +$ BIN/Win32/pdp10-kl +``` + +- Replace `mingw64` in `sh .travis/deps.sh mingw64` with `clang64`, `ucrt64` or + `mingw32`, consistent with the MinGW64 environment in which you are compiling. + +- Replace `pdp10-kl` with your preferred or desired simulator, e.g., `vax`, + `pdp11`, etc. + +- You can find the resulting simulator binaries in the BIN/`$MSYSTEM` + subdirectory, where `$MSYSTEM` is one of MINGW64, MINGW32, CLANG64 or UCRT64. ## Before You Begin Building... @@ -67,8 +143,7 @@ Before you begin building the simulators, you need the following: builds on Windows. - _Microsoft Visual C/C++_: Visual Studio 2022, 2019, 2017 and 2015 are - supported. The [appveyor CI/CD][appveyor] pipeline builds using these four - Microsoft toolchains in _Release_ and _Debug_ configurations. + supported. - _VS 2022_: The Community Edition can be downloaded from the [Microsoft Visual Studio Community][vs_community] page. @@ -105,12 +180,12 @@ Before you begin building the simulators, you need the following: - _Visual Studio IDE, Developer command or PowerShell console windows_: No additional software installation needed. Microsoft provides `cmake` that can be invoked from the command prompt or from within the VS IDE. - Microsoft has bundled various version of `cmake` into Visual Studio since + Microsoft has bundled various versions of `cmake` into Visual Studio since VS 2015. - Otherwise, install `cmake` using your preferred Windows software package manager, such as [Chocolatey][chocolatey] or [Scoop][scoop]. You can also - [download and install the `cmake` binary distribution][cmake_downloads] + download and install [the `cmake` binary distribution][cmake_downloads] directly. - The [Git source control system][gitscm]. @@ -144,7 +219,7 @@ Before you begin building the simulators, you need the following: - Otherwise, install `git` using your preferred Windows software package manager, such as [Chocolatey][chocolatey] or [Scoop][scoop]. You can also - [download and install the `git` client][gitscm_downloads] directly. + download and install [the `git` client][gitscm_downloads] directly. - GNU Make: @@ -156,8 +231,8 @@ Before you begin building the simulators, you need the following: - Ninja: - [Ninja][ninja] is an optional, but useful/faster parallel build alternative to - Unix Makefiles and Visual Studio's `msbuild`. + [Ninja][ninja] is an optional parallel build alternative to Unix Makefiles and + Visual Studio's `msbuild`. #### Ninja: "failed recompaction: Permission denied" @@ -279,6 +354,12 @@ binaries. $ sudo sh .travis/deps.sh linux ``` + - Linux Arch: + + ```bash + $ sudo sh .travis/deps.sh arch-linux + ``` + - macOS Homebrew: ```bash @@ -299,7 +380,7 @@ binaries. $ .travis/deps.sh mingw64 ``` - - MinGW-w64 UCRT console: + - MinGW-w64 UCRT64 console: ```bash $ echo $MSYSTEM @@ -307,6 +388,14 @@ binaries. $ .travis/deps.sh ucrt64 ``` + - MinGW-w64 CLANG64 console: + + ```bash + $ echo $MSYSTEM + CLANG64 + $ .travis/deps.sh clang64 + ``` + #### Windows: "Legacy" superbuild or `vcpkg` The SIMH CMake infrastructure has two distinct feature library dependency @@ -376,10 +465,10 @@ Setup and Usage: PS C:\...> git clone https://github.com/Microsoft/vcpkg.git PS C:\...> cd vcpkg PS C:\...\vcpkg> .\vcpkg\bootstrap-vcpkg.bat + PS C:\...\vcpkg> $env:VCPKG_ROOT=$(get-location) PS C:\...\vcpkg> cd ..\open-simh PS C:\...\open-simh> ``` - Then set the `VCPKG_ROOT` environment variable to the `vcpkg` installation directory. [^1]: `vcpkg` does not support the `v141_xp` toolkit required to compile Windows XP binaries. Windows XP is a target platform that SIMH can hopefully deprecate diff --git a/build_mingw.bat b/build_mingw.bat deleted file mode 100644 index 7807128e1..000000000 --- a/build_mingw.bat +++ /dev/null @@ -1,19 +0,0 @@ -@echo off -rem Compile all of SIMH using MINGW make and gcc environment -rem -rem The makefile will determine if the needed WinPcap build -rem components are available and the resulting simulators will -rem run with networking support when the WinPcap environment -rem is installed on the running system. -rem -rem Individual simulator sources are in .\simulator_name -rem Individual simulator executables are to .\BIN -rem -rem If needed, define the path for the MINGW bin directory. -rem -gcc -v 1>NUL 2>NUL -if ERRORLEVEL 1 path C:\MinGW\bin;%path% -if not exist BIN mkdir BIN -gcc -v 1>NUL 2>NUL -if ERRORLEVEL 1 echo "MinGW Environment Unavailable" -mingw32-make -f makefile %* diff --git a/build_mingw_ether.bat b/build_mingw_ether.bat deleted file mode 100644 index 5a9771420..000000000 --- a/build_mingw_ether.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -rem Built in Ethernet support (requires WinPcap installed). -rem The normal Windows build always builds with Ethernet support -rem so, this procedure is un-necessary. Just call the normal build -rem -%~p0\build_mingw.bat %* diff --git a/build_mingw_noasync.bat b/build_mingw_noasync.bat deleted file mode 100644 index ca2eb9c79..000000000 --- a/build_mingw_noasync.bat +++ /dev/null @@ -1,14 +0,0 @@ -@echo off -rem Build without SIM_ASYNCH_IO defined (avoiding the use of pthreads) -rem Compile all of SIMH using MINGW make and gcc environment -rem Individual simulator sources are in .\simulator_name -rem Individual simulator executables are to .\BIN -rem -rem If needed, define the path for the MINGW bin directory. -rem -gcc -v 1>NUL 2>NUL -if ERRORLEVEL 1 path C:\MinGW\bin;%path% -if not exist BIN mkdir BIN -gcc -v 1>NUL 2>NUL -if ERRORLEVEL 1 echo "MinGW Environment Unavailable" -mingw32-make NOASYNCH=1 -f makefile %* diff --git a/cmake/add_simulator.cmake b/cmake/add_simulator.cmake index e87ef16c1..6837b9719 100644 --- a/cmake/add_simulator.cmake +++ b/cmake/add_simulator.cmake @@ -24,6 +24,7 @@ add_custom_target(update_sim_commit ALL ## Simulator sources and library: set(SIM_SOURCES ${CMAKE_SOURCE_DIR}/scp.c + ${CMAKE_SOURCE_DIR}/sim_debtab.c ${CMAKE_SOURCE_DIR}/sim_card.c ${CMAKE_SOURCE_DIR}/sim_console.c ${CMAKE_SOURCE_DIR}/sim_disk.c @@ -36,12 +37,20 @@ set(SIM_SOURCES ${CMAKE_SOURCE_DIR}/sim_tape.c ${CMAKE_SOURCE_DIR}/sim_timer.c ${CMAKE_SOURCE_DIR}/sim_tmxr.c - ${CMAKE_SOURCE_DIR}/sim_video.c) + ${CMAKE_SOURCE_DIR}/sim_video.c + ${CMAKE_SOURCE_DIR}/sim_debtab.c) set(SIM_VIDEO_SOURCES ${CMAKE_SOURCE_DIR}/display/display.c ${CMAKE_SOURCE_DIR}/display/sim_ws.c) +if (WITH_NETWORK AND WITH_SLIRP) + list(APPEND SIM_SOURCES + sim_slirp/sim_slirp.c + sim_slirp/slirp_poll.c + ) +endif () + ## Build a simulator core library, with and without AIO support. The AIO variant ## has "_aio" appended to its name, e.g., "simhz64_aio" or "simhz64_video_aio". function(build_simcore _targ) @@ -60,8 +69,38 @@ function(build_simcore _targ) C_STANDARD 99 EXCLUDE_FROM_ALL True ) + + if (TARGET_WINVER) + target_compile_definitions(${lib} PUBLIC WINVER=${TARGET_WINVER} _WIN32_WINNT=${TARGET_WINVER}) + endif () + target_compile_definitions(${lib} PRIVATE USE_SIM_CARD USE_SIM_IMD) target_compile_options(${lib} PRIVATE ${EXTRA_TARGET_CFLAGS}) + + if (WITH_NETWORK AND WITH_SLIRP) + target_compile_definitions( + ${lib} + PUBLIC + HAVE_SLIRP_NETWORK + LIBSLIRP_STATIC + ) + + if (HAVE_INET_PTON) + ## libslirp detects HAVE_INET_PTON for us. + target_compile_definitions(${lib} PUBLIC HAVE_INET_PTON) + endif() + + target_include_directories( + ${lib} + PUBLIC + ${CMAKE_SOURCE_DIR}/sim_slirp + PRIVATE + ${CMAKE_SOURCE_DIR}/libslirp/minimal + ${CMAKE_SOURCE_DIR}/libslirp/src + ${CMAKE_BINARY_DIR}/libslirp/build-include + ) + endif () + target_link_options(${lib} PRIVATE ${EXTRA_TARGET_LFLAGS}) # Make sure that the top-level directory is part of the libary's include path: diff --git a/cmake/cmake-builder.ps1 b/cmake/cmake-builder.ps1 index 2e67676da..0e36a0bc6 100644 --- a/cmake/cmake-builder.ps1 +++ b/cmake/cmake-builder.ps1 @@ -85,7 +85,7 @@ param ( ## mingw-make MinGW GCC/mingw32-make ## mingw-ninja MinGW GCC/ninja [Parameter(Mandatory=$false)] - [string] $flavor = "vs2022", + [string] $flavor = "vs2022-x64", ## The target build configuration. Valid values: "Release" and "Debug" [Parameter(Mandatory=$false)] @@ -206,18 +206,21 @@ $multiConfig = $false $singleConfig = $true $cmakeGenMap = @{ - "vs2022" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "Win32")); - "vs2022-xp" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "Win32", "-T", "v141_xp")); - "vs2022-x64" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); - "vs2019" = [GeneratorInfo]::new("Visual Studio 16 2019", $multiConfig, $false, "", @("-A", "Win32")); - "vs2019-xp" = [GeneratorInfo]::new("Visual Studio 16 2019", $multiConfig, $false, "", @("-A", "Win32", "-T", "v141_xp")); - "vs2019-x64" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); - "vs2017" = [GeneratorInfo]::new("Visual Studio 15 2017", $multiConfig, $false, "", @("-A", "Win32")); - "vs2017-xp" = [GeneratorInfo]::new("Visual Studio 15 2017", $multiConfig, $false, "", @("-A", "Win32", "-T", "v141_xp")); - "vs2017-x64" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); - "vs2015" = [GeneratorInfo]::new("Visual Studio 14 2015", $multiConfig, $false, "", @()); - "mingw-make" = [GeneratorInfo]::new("MinGW Makefiles", $singleConfig, $false, "", @()); - "mingw-ninja" = [GeneratorInfo]::new("Ninja", $singleConfig, $false, "", @()) + "vs2022" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "Win32")); + "vs2022-x64" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); + "vs2022-xp" = [GeneratorInfo]::new("Visual Studio 17 2022", $multiConfig, $false, "", + @("-A", "Win32", "-T", "v141_xp", "-DTARGET_WINVER=0x0501")); + "vs2019" = [GeneratorInfo]::new("Visual Studio 16 2019", $multiConfig, $false, "", @("-A", "Win32")); + "vs2019-x64" = [GeneratorInfo]::new("Visual Studio 17 2019", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); + "vs2019-xp" = [GeneratorInfo]::new("Visual Studio 16 2019", $multiConfig, $false, "", + @("-A", "Win32", "-T", "v141_xp", "-DTARGET_WINVER=0x0501")); + "vs2017" = [GeneratorInfo]::new("Visual Studio 15 2017", $multiConfig, $false, "", @("-A", "Win32")); + "vs2017-x64" = [GeneratorInfo]::new("Visual Studio 17 2017", $multiConfig, $false, "", @("-A", "x64", "-T", "host=x64")); + "vs2017-xp" = [GeneratorInfo]::new("Visual Studio 15 2017", $multiConfig, $false, "", + @("-A", "Win32", "-T", "v141_xp", "-DTARGET_WINVER=0x0501")); + "vs2015" = [GeneratorInfo]::new("Visual Studio 14 2015", $multiConfig, $false, "", @()); + "mingw-make" = [GeneratorInfo]::new("MinGW Makefiles", $singleConfig, $false, "", @()); + "mingw-ninja" = [GeneratorInfo]::new("Ninja", $singleConfig, $false, "", @()) } @@ -297,7 +300,7 @@ if (!$testonly) } ## Validate the requested configuration. -if (!@("Release", "Debug").Contains($config)) +if (!@("Release", "Debug", "RelWithDebInfo").Contains($config)) { @" ${scriptName}: Invalid configuration: "${config}". diff --git a/cmake/cmake-builder.sh b/cmake/cmake-builder.sh index fc9015f1c..19c9d66ae 100755 --- a/cmake/cmake-builder.sh +++ b/cmake/cmake-builder.sh @@ -27,7 +27,8 @@ Configure and build simh simulators on Linux and *nix-like platforms. msys2 mingw ucrt ---config (-c) Specifies the build configuration: 'Release' or 'Debug' +--config (-c) Specifies the build configuration: 'Release', 'Debug', or + 'RelWithDebInfo' --target Build a specific simulator or simulators. Separate multiple targets with a comma, e.g. "--target pdp8,pdp11,vax750,altairz80,3b2" @@ -211,7 +212,7 @@ while true; do ;; -c | --config) case "$2" in - Release|Debug) + Release|Debug|RelWithDebInfo) buildConfig=$2 shift 2 ;; diff --git a/cmake/dep-link.cmake b/cmake/dep-link.cmake index 90c7707e0..cfc342ba2 100644 --- a/cmake/dep-link.cmake +++ b/cmake/dep-link.cmake @@ -346,10 +346,10 @@ if (WITH_NETWORK) endif (HAVE_TAP_NETWORK) endif (WITH_TAP) - if (WITH_SLIRP) - target_link_libraries(simh_network INTERFACE slirp) + if (WITH_NETWORK AND WITH_SLIRP) + target_link_libraries(simh_network INTERFACE slirp_static) list(APPEND NETWORK_PKG_STATUS "NAT(SLiRP)") - endif (WITH_SLIRP) + endif () ## Finally, set the network runtime if (NOT network_runtime) diff --git a/cmake/os-features.cmake b/cmake/os-features.cmake index d0741b4fd..c9eebdce9 100644 --- a/cmake/os-features.cmake +++ b/cmake/os-features.cmake @@ -190,7 +190,6 @@ if (WIN32) target_compile_definitions(os_features INTERFACE HAVE_WINMM) if (NOT WINAPI_DEPRECATION) target_compile_definitions(os_features INTERFACE - _WINSOCK_DEPRECATED_NO_WARNINGS _CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_WARNINGS ) @@ -205,3 +204,39 @@ if (CYGWIN) target_compile_definitions(os_features INTERFACE HAVE_WINMM) endif () endif () + +## inttypes.h for print formats: +check_include_file(inttypes.h have_inttypes_h) +if (have_inttypes_h) + target_compile_definitions(os_features INTERFACE HAVE_INTTYPES_H) +endif () + +# libslirp poll/select/etc. detection +set(sim_use_select 0) +set(sim_use_poll 0) + +if (NOT WIN32) + check_include_file(poll.h have_poll_h) + if (have_poll_h) + set(sim_use_poll 1) + else () + set(sim_use_select 1) + endif () +else () + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") + + check_symbol_exists(WSAPoll "winsock2.h;windows.h" have_wsa_poll) + if (have_wsa_poll) + set(sim_use_poll 1) + else () + set(sim_use_select 1) + endif () + + cmake_pop_check_state() +endif() + +target_compile_definitions(os_features INTERFACE + SIM_USE_POLL=${sim_use_poll} + SIM_USE_SELECT=${sim_use_select} +) diff --git a/cmake/platform-quirks.cmake b/cmake/platform-quirks.cmake index 2113fec9b..277cb700b 100644 --- a/cmake/platform-quirks.cmake +++ b/cmake/platform-quirks.cmake @@ -75,11 +75,13 @@ if (WIN32) ## /Ot: Favor fast code ## /Oy: Suppress generating a stack frame (??? why?) add_compile_options("$<$:/EHsc;/GF;/Gy;/Oi;/Ot;/Oy;/Zi>") + add_compile_options("$<$:/EHsc;/GF;/Gy;/Oi;/Ot;/Oy;/Zi>") add_compile_options("$<$:/EHsc;/FC>") if (RELEASE_LTO) ## /LTCG: Link-Time Code Generation. Pair with /GL at compile time. add_compile_options("$<$:/GL>") + add_compile_options("$<$:/GL>") add_link_options("$<$:/LTCG>") message(STATUS "Adding LTO to Release compiler and linker flags") endif () @@ -95,7 +97,7 @@ if (WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>${use_rtll}") ## Disable automagic add for _MBCS: - add_definitions(-D_SBCS) + add_compile_definitions(_SBCS) if (CMAKE_VERSION VERSION_LESS "3.23") ## -5 Evil hack to ensure that find_package() can match against an empty @@ -105,6 +107,7 @@ if (WIN32) list(APPEND EXTRA_TARGET_CFLAGS "$<$:$<$:/W4>>" + "$<$:$<$:/W4>>" "$<$:/W3>" ) @@ -119,6 +122,13 @@ if (WIN32) list(APPEND EXTRA_TARGET_CFLAGS "/WX") endif () endif () + + ## And there's a target Windows version? + if (TARGET_WINVER) + message(STATUS "Target Windows version set to ${TARGET_WINVER}") + add_compile_definitions(WINVER=${TARGET_WINVER} _WIN32_WINNT=${TARGET_WINVER}) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWINVER=${TARGET_WINVER}" "-D_WIN32_WINNT=${TARGET_WINVER}") + endif () elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") # The MSVC solution builds as 32-bit, but none of the *nix platforms do. # @@ -132,11 +142,10 @@ endif () if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") # include(fpintrin) - # Turn on warnings about strict overflow/potential overflows. - ## LIST(APPEND EXTRA_TARGET_CFLAGS "-Wall" "-fno-inline" "-fstrict-overflow" "-Wstrict-overflow=3") LIST(APPEND EXTRA_TARGET_CFLAGS "-U__STRICT_ANSI__" "$<$:$<$:-Wall>>" + "$<$:$<$:-Wall>>" ## Only add if WARNINGS_FATAL set; has undesirable consequences with LTO. "$<$:-Wall>" ) @@ -147,7 +156,8 @@ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") # the VAX simulators. Reduce optimization and ensure strict overflow is turned off. if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - set(update_o2 TRUE) + set(add_werror ${WARNINGS_FATAL}) + if (NOT MINGW) if (RELEASE_LTO AND (NOT DEFINED CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "Release")) check_c_compiler_flag("-flto" GCC_LTO_FLAG) @@ -156,7 +166,7 @@ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") set(lto_flag "$<$:-flto>") list(APPEND EXTRA_TARGET_CFLAGS "${lto_flag}") list(APPEND EXTRA_TARGET_LFLAGS "${lto_flag}") - set(update_o2 FALSE) + set(add_werror TRUE) else () message(STATUS "Compiler does not support Link Time Optimization.") endif () @@ -167,13 +177,7 @@ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") message(STATUS "MinGW: Link Time Optimization BROKEN, not added to Release flags") endif () - if (update_o2) - message(STATUS "Replacing '-O3' with '-O2'") - string(REGEX REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - string(REGEX REPLACE "-O3" "-O2" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") - endif () - - if (WARNINGS_FATAL` OR RELEASE_LTO) + if (add_werror) check_c_compiler_flag("-Werror" GCC_W_ERROR_FLAG) if (GCC_W_ERROR_FLAG) if (WARNINGS_FATAL) @@ -193,12 +197,25 @@ if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang") elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang") message(STATUS "Adding Clang-specific optimizations to CMAKE_C_FLAGS_RELEASE") list(APPEND opt_flags "-fno-strict-overflow") + + if (WIN32) + list(APPEND EXTRA_TARGET_CFLAGS "-Wmicrosoft") + ## IBM1130 has a copyright symbol (8-bit character), so need to specify the code + ## page to the llvm-rc resource compiler. + if (CMAKE_RC_COMPILER MATCHES ".*windres.exe") + string(APPEND CMAKE_RC_FLAGS " -c 1252") + else () + string(APPEND CMAKE_RC_FLAGS " /C 1252") + endif () + endif () endif() foreach (opt_flag ${opt_flags}) message(STATUS " ${opt_flag}") string(REGEX REPLACE "${opt_flag}[ \t\r\n]*" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") string(APPEND CMAKE_C_FLAGS_RELEASE " ${opt_flag}") + string(REGEX REPLACE "${opt_flag}[ \t\r\n]*" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " ${opt_flag}") string(REGEX REPLACE "${opt_flag}[ \t\r\n]*" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}") string(APPEND CMAKE_C_FLAGS_MINSIZEREL " ${opt_flag}") endforeach () diff --git a/cmake/pthreads-dep.cmake b/cmake/pthreads-dep.cmake index 81cae146b..c83b2b434 100644 --- a/cmake/pthreads-dep.cmake +++ b/cmake/pthreads-dep.cmake @@ -12,7 +12,7 @@ set(AIO_FLAGS) if (WITH_ASYNC) include(ExternalProject) - if (MSVC OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES ".*Clang.*with MSVC-like command-line.*")) + if (MSVC OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES ".*Clang" AND NOT DEFINED ENV{MSYSTEM})) # Pthreads4w: pthreads for windows. if (USING_VCPKG) find_package(PThreads4W REQUIRED) diff --git a/cmake/simgen/basic_simulator.py b/cmake/simgen/basic_simulator.py index 9d3eb9d6a..d42c73454 100644 --- a/cmake/simgen/basic_simulator.py +++ b/cmake/simgen/basic_simulator.py @@ -279,6 +279,8 @@ def write_simulator(self, stream, indent, test_label='ibm650'): 'if (WIN32)', ' if (MSVC)', ' set(I650_STACK_FLAG "/STACK:{0}")'.format(self.stack_size), + ' elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang")', + ' set(I650_STACK_FLAG "-Xlinker" "/STACK:{0}")'.format(self.stack_size), ' else ()', ' set(I650_STACK_FLAG "-Wl,--stack,{0}")'.format(self.stack_size), ' endif ()', diff --git a/cmake/simgen/cmake_container.py b/cmake/simgen/cmake_container.py index 053a75492..1348acacd 100644 --- a/cmake/simgen/cmake_container.py +++ b/cmake/simgen/cmake_container.py @@ -75,6 +75,7 @@ class CMakeBuildSystem: '${NETWORK_OPT}', '$(NETWORK_OPT)', '${SCSI}', '$(SCSI)', '${DISPLAY_OPT}', '$(DISPLAY_OPT)', + '${NETWORK_LDFLAGS}', '$(NETWORK_LDFLAGS)', '${VIDEO_CCDEFS}', '$(VIDEO_CCDEFS)', '${VIDEO_LDFLAGS}', '$(VIDEO_LDFLAGS)', '${BESM6_PANEL_OPT}', '$(BESM6_PANEL_OPT)']) @@ -156,7 +157,7 @@ def extract(self, compile_action, test_name, sim_dir, sim_name, defs, buildrom, def compile_elems_to_ignore(self, elem): - return (elem in self._ignore_compile_elems or elem.endswith('_LDFLAGS')) + return (elem in self._ignore_compile_elems or elem.endswith('_LDFLAGS)') or elem.endswith('_LDFLAGS}')) def source_elems_to_ignore(self, elem): return self.compile_elems_to_ignore(elem) or elem in _special_sources diff --git a/cmake/simh-packaging.cmake b/cmake/simh-packaging.cmake index 6a3ac3978..fdcc14481 100644 --- a/cmake/simh-packaging.cmake +++ b/cmake/simh-packaging.cmake @@ -1,103 +1,103 @@ -## The default runtime support component/family: -cpack_add_component(runtime_support - DISPLAY_NAME "Runtime support" - DESCRIPTION "Required SIMH runtime support (documentation, shared libraries)" - REQUIRED -) - -## Basic documentation for SIMH -install(FILES doc/simh.doc TYPE DOC COMPONENT runtime_support) - -cpack_add_component(altairz80_family - DISPLAY_NAME "Altair Z80 simulator." - DESCRIPTION "The Altair Z80 simulator with M68000 support. Simulators: altairz80" -) -cpack_add_component(att3b2_family - DISPLAY_NAME "ATT&T 3b2 collection" - DESCRIPTION "The AT&T 3b2 simulator family. Simulators: 3b2, 3b2-700" -) -cpack_add_component(b5500_family - DISPLAY_NAME "Burroughs 5500" - DESCRIPTION "The Burroughs 5500 system simulator. Simulators: b5500" -) -cpack_add_component(cdc1700_family - DISPLAY_NAME "CDC 1700" - DESCRIPTION "The Control Data Corporation's systems. Simulators: cdc1700" -) -cpack_add_component(decpdp_family - DISPLAY_NAME "DEC PDP family" - DESCRIPTION "Digital Equipment Corporation PDP systems. Simulators: pdp1, pdp15, pdp4, pdp6, pdp7, pdp8, pdp9" -) -cpack_add_component(default_family - DISPLAY_NAME "Default SIMH simulator family." - DESCRIPTION "The SIMH simulator collection of historical processors and computing systems that do not belong to -any other simulated system family. Simulators: altair, besm6, ssem, tt2500, tx-0" -) -cpack_add_component(dgnova_family - DISPLAY_NAME "DG Nova and Eclipse" - DESCRIPTION "Data General NOVA and Eclipse system simulators. Simulators: eclipse, nova" -) -cpack_add_component(experimental - DISPLAY_NAME "Experimental (work-in-progress) simulators" - DESCRIPTION "Experimental or work-in-progress simulators not in the SIMH mainline simulator suite. Simulators: alpha, pdq3, sage" -) -cpack_add_component(gould_family - DISPLAY_NAME "Gould simulators" - DESCRIPTION "Gould Systems simulators. Simulators: sel32" -) -cpack_add_component(grisys_family - DISPLAY_NAME "GRI Systems GRI-909" - DESCRIPTION "GRI Systems GRI-909 system simulator. Simulators: gri" -) -cpack_add_component(honeywell_family - DISPLAY_NAME "Honeywell H316" - DESCRIPTION "Honeywell H-316 system simulator. Simulators: h316" -) -cpack_add_component(hp_family - DISPLAY_NAME "HP 2100, 3000" - DESCRIPTION "Hewlett-Packard H2100 and H3000 simulators. Simulators: hp2100, hp3000" -) -cpack_add_component(ibm_family - DISPLAY_NAME "IBM" - DESCRIPTION "IBM system simulators: i650. Simulators: i1401, i1620, i650, i701, i7010, i704, i7070, i7080, i7090, i7094, ibm1130, s3" -) -cpack_add_component(imlac_family - DISPLAY_NAME "IMLAC" - DESCRIPTION "IMLAC system simulators. Simulators: imlac" -) -cpack_add_component(intel_family - DISPLAY_NAME "Intel" - DESCRIPTION "Intel system simulators. Simulators: intel-mds, scelbi" -) -cpack_add_component(interdata_family - DISPLAY_NAME "Interdata" - DESCRIPTION "Interdata systems simulators. Simulators: id16, id32" -) -cpack_add_component(lgp_family - DISPLAY_NAME "LGP" - DESCRIPTION "Librascope systems. Simulators: lgp" -) -cpack_add_component(norsk_family - DISPLAY_NAME "ND simulators" - DESCRIPTION "Norsk Data systems simulator family. Simulators: nd100" -) -cpack_add_component(pdp10_family - DISPLAY_NAME "DEC PDP-10 collection" - DESCRIPTION "DEC PDP-10 architecture simulators and variants. Simulators: pdp10, pdp10-ka, pdp10-ki, pdp10-kl, pdp10-ks" -) -cpack_add_component(pdp11_family - DISPLAY_NAME "DEC PDP-11 collection." - DESCRIPTION "DEC PDP-11 and PDP-11-derived architecture simulators. Simulators: pdp11, uc15" -) -cpack_add_component(sds_family - DISPLAY_NAME "SDS simulators" - DESCRIPTION "Scientific Data Systems (SDS) systems. Simulators: sds, sigma" -) -cpack_add_component(swtp_family - DISPLAY_NAME "SWTP simulators" - DESCRIPTION "Southwest Technical Products (SWTP) system simulators. Simulators: swtp6800mp-a, swtp6800mp-a2" -) -cpack_add_component(vax_family - DISPLAY_NAME "DEC VAX simulator collection" - DESCRIPTION "The Digital Equipment Corporation VAX (plural: VAXen) simulator family. Simulators: infoserver100, infoserver1000, infoserver150vxt, microvax1, microvax2, microvax2000, microvax3100, microvax3100e, microvax3100m80, rtvax1000, vax, vax730, vax750, vax780, vax8200, vax8600, vaxstation3100m30, vaxstation3100m38, vaxstation3100m76, vaxstation4000m60, vaxstation4000vlc" -) +## The default runtime support component/family: +cpack_add_component(runtime_support + DISPLAY_NAME "Runtime support" + DESCRIPTION "Required SIMH runtime support (documentation, shared libraries)" + REQUIRED +) + +## Basic documentation for SIMH +install(FILES doc/simh.doc TYPE DOC COMPONENT runtime_support) + +cpack_add_component(altairz80_family + DISPLAY_NAME "Altair Z80 simulator." + DESCRIPTION "The Altair Z80 simulator with M68000 support. Simulators: altairz80" +) +cpack_add_component(att3b2_family + DISPLAY_NAME "ATT&T 3b2 collection" + DESCRIPTION "The AT&T 3b2 simulator family. Simulators: 3b2, 3b2-700" +) +cpack_add_component(b5500_family + DISPLAY_NAME "Burroughs 5500" + DESCRIPTION "The Burroughs 5500 system simulator. Simulators: b5500" +) +cpack_add_component(cdc1700_family + DISPLAY_NAME "CDC 1700" + DESCRIPTION "The Control Data Corporation's systems. Simulators: cdc1700" +) +cpack_add_component(decpdp_family + DISPLAY_NAME "DEC PDP family" + DESCRIPTION "Digital Equipment Corporation PDP systems. Simulators: pdp1, pdp15, pdp4, pdp6, pdp7, pdp8, pdp9" +) +cpack_add_component(default_family + DISPLAY_NAME "Default SIMH simulator family." + DESCRIPTION "The SIMH simulator collection of historical processors and computing systems that do not belong to +any other simulated system family. Simulators: altair, besm6, ssem, tt2500, tx-0" +) +cpack_add_component(dgnova_family + DISPLAY_NAME "DG Nova and Eclipse" + DESCRIPTION "Data General NOVA and Eclipse system simulators. Simulators: eclipse, nova" +) +cpack_add_component(experimental + DISPLAY_NAME "Experimental (work-in-progress) simulators" + DESCRIPTION "Experimental or work-in-progress simulators not in the SIMH mainline simulator suite. Simulators: alpha, pdq3, sage" +) +cpack_add_component(gould_family + DISPLAY_NAME "Gould simulators" + DESCRIPTION "Gould Systems simulators. Simulators: sel32" +) +cpack_add_component(grisys_family + DISPLAY_NAME "GRI Systems GRI-909" + DESCRIPTION "GRI Systems GRI-909 system simulator. Simulators: gri" +) +cpack_add_component(honeywell_family + DISPLAY_NAME "Honeywell H316" + DESCRIPTION "Honeywell H-316 system simulator. Simulators: h316" +) +cpack_add_component(hp_family + DISPLAY_NAME "HP 2100, 3000" + DESCRIPTION "Hewlett-Packard H2100 and H3000 simulators. Simulators: hp2100, hp3000" +) +cpack_add_component(ibm_family + DISPLAY_NAME "IBM" + DESCRIPTION "IBM system simulators: i650. Simulators: i1401, i1620, i650, i701, i7010, i704, i7070, i7080, i7090, i7094, ibm1130, s3" +) +cpack_add_component(imlac_family + DISPLAY_NAME "IMLAC" + DESCRIPTION "IMLAC system simulators. Simulators: imlac" +) +cpack_add_component(intel_family + DISPLAY_NAME "Intel" + DESCRIPTION "Intel system simulators. Simulators: intel-mds, scelbi" +) +cpack_add_component(interdata_family + DISPLAY_NAME "Interdata" + DESCRIPTION "Interdata systems simulators. Simulators: id16, id32" +) +cpack_add_component(lgp_family + DISPLAY_NAME "LGP" + DESCRIPTION "Librascope systems. Simulators: lgp" +) +cpack_add_component(norsk_family + DISPLAY_NAME "ND simulators" + DESCRIPTION "Norsk Data systems simulator family. Simulators: nd100" +) +cpack_add_component(pdp10_family + DISPLAY_NAME "DEC PDP-10 collection" + DESCRIPTION "DEC PDP-10 architecture simulators and variants. Simulators: pdp10, pdp10-ka, pdp10-ki, pdp10-kl, pdp10-ks" +) +cpack_add_component(pdp11_family + DISPLAY_NAME "DEC PDP-11 collection." + DESCRIPTION "DEC PDP-11 and PDP-11-derived architecture simulators. Simulators: pdp11, uc15" +) +cpack_add_component(sds_family + DISPLAY_NAME "SDS simulators" + DESCRIPTION "Scientific Data Systems (SDS) systems. Simulators: sds, sigma" +) +cpack_add_component(swtp_family + DISPLAY_NAME "SWTP simulators" + DESCRIPTION "Southwest Technical Products (SWTP) system simulators. Simulators: swtp6800mp-a, swtp6800mp-a2" +) +cpack_add_component(vax_family + DISPLAY_NAME "DEC VAX simulator collection" + DESCRIPTION "The Digital Equipment Corporation VAX (plural: VAXen) simulator family. Simulators: infoserver100, infoserver1000, infoserver150vxt, microvax1, microvax2, microvax2000, microvax3100, microvax3100e, microvax3100m80, rtvax1000, vax, vax730, vax750, vax780, vax8200, vax8600, vaxstation3100m30, vaxstation3100m38, vaxstation3100m76, vaxstation4000m60, vaxstation4000vlc" +) diff --git a/cmake/simh-simulators.cmake b/cmake/simh-simulators.cmake index 15d58c3b8..bd456fa5e 100644 --- a/cmake/simh-simulators.cmake +++ b/cmake/simh-simulators.cmake @@ -1,123 +1,123 @@ -## -## This is an automagically generated file. Do NOT EDIT. -## Any changes you make will be overwritten!! -## -## Make changes to the SIMH top-level makefile and then run the -## "cmake/generate.py" script to regenerate these files. -## -## cd cmake; python -m generate --help -## -## ------------------------------------------------------------ -set(ALPHAD "${CMAKE_SOURCE_DIR}/alpha") -set(ALTAIRD "${CMAKE_SOURCE_DIR}/ALTAIR") -set(ALTAIRZ80D "${CMAKE_SOURCE_DIR}/AltairZ80") -set(ATT3B2D "${CMAKE_SOURCE_DIR}/3B2") -set(B5500D "${CMAKE_SOURCE_DIR}/B5500") -set(BESM6D "${CMAKE_SOURCE_DIR}/BESM6") -set(CDC1700D "${CMAKE_SOURCE_DIR}/CDC1700") -set(GRID "${CMAKE_SOURCE_DIR}/GRI") -set(H316D "${CMAKE_SOURCE_DIR}/H316") -set(HP2100D "${CMAKE_SOURCE_DIR}/HP2100") -set(HP3000D "${CMAKE_SOURCE_DIR}/HP3000") -set(I1401D "${CMAKE_SOURCE_DIR}/I1401") -set(I1620D "${CMAKE_SOURCE_DIR}/I1620") -set(I650D "${CMAKE_SOURCE_DIR}/I650") -set(I7000D "${CMAKE_SOURCE_DIR}/I7000") -set(I7010D "${CMAKE_SOURCE_DIR}/I7000") -set(I7094D "${CMAKE_SOURCE_DIR}/I7094") -set(IBM1130D "${CMAKE_SOURCE_DIR}/Ibm1130") -set(ID16D "${CMAKE_SOURCE_DIR}/Interdata") -set(ID32D "${CMAKE_SOURCE_DIR}/Interdata") -set(IMLACD "${CMAKE_SOURCE_DIR}/imlac") -set(INTELSYSC "${CMAKE_SOURCE_DIR}/Intel-Systems/common") -set(KA10D "${CMAKE_SOURCE_DIR}/PDP10") -set(KI10D "${CMAKE_SOURCE_DIR}/PDP10") -set(KL10D "${CMAKE_SOURCE_DIR}/PDP10") -set(KS10D "${CMAKE_SOURCE_DIR}/PDP10") -set(LGPD "${CMAKE_SOURCE_DIR}/LGP") -set(ND100D "${CMAKE_SOURCE_DIR}/ND100") -set(NOVAD "${CMAKE_SOURCE_DIR}/NOVA") -set(PDP10D "${CMAKE_SOURCE_DIR}/PDP10") -set(PDP11D "${CMAKE_SOURCE_DIR}/PDP11") -set(PDP18BD "${CMAKE_SOURCE_DIR}/PDP18B") -set(PDP1D "${CMAKE_SOURCE_DIR}/PDP1") -set(PDP6D "${CMAKE_SOURCE_DIR}/PDP10") -set(PDP8D "${CMAKE_SOURCE_DIR}/PDP8") -set(PDQ3D "${CMAKE_SOURCE_DIR}/PDQ-3") -set(S3D "${CMAKE_SOURCE_DIR}/S3") -set(SAGED "${CMAKE_SOURCE_DIR}/SAGE") -set(SDSD "${CMAKE_SOURCE_DIR}/SDS") -set(SEL32D "${CMAKE_SOURCE_DIR}/SEL32") -set(SIGMAD "${CMAKE_SOURCE_DIR}/sigma") -set(SSEMD "${CMAKE_SOURCE_DIR}/SSEM") -set(SWTP6800C "${CMAKE_SOURCE_DIR}/swtp6800/common") -set(SWTP6800D "${CMAKE_SOURCE_DIR}/swtp6800/swtp6800") -set(TT2500D "${CMAKE_SOURCE_DIR}/tt2500") -set(TX0D "${CMAKE_SOURCE_DIR}/TX-0") -set(UC15D "${CMAKE_SOURCE_DIR}/PDP11") -set(VAXD "${CMAKE_SOURCE_DIR}/VAX") - -set(DISPLAYD "${CMAKE_SOURCE_DIR}/display") -set(DISPLAY340 "${DISPLAYD}/type340.c") -set(DISPLAYIII "${DISPLAYD}/iii.c") -set(DISPLAYNG "${DISPLAYD}/ng.c") -set(DISPLAYVT "${DISPLAYD}/vt11.c") - -set(INTELSYSD "${CMAKE_SOURCE_DIR}/Intel-Systems") -set(INTEL_MDSD "${INTELSYSD}/Intel-MDS") -set(SCELBIC "${INTELSYSD}/common") -set(SCELBID "${INTELSYSD}/scelbi") - -## ---------------------------------------- - -if (NOT WITH_VIDEO) - ### Hack: Unset these variables so that they don't expand if - ### not building with video: - set(DISPLAY340 "") - set(DISPLAYIII "") - set(DISPLAYNG "") - set(DISPLAYVT "") -endif () - -## ---------------------------------------- - -add_subdirectory(3B2) -add_subdirectory(ALTAIR) -add_subdirectory(AltairZ80) -add_subdirectory(B5500) -add_subdirectory(BESM6) -add_subdirectory(CDC1700) -add_subdirectory(GRI) -add_subdirectory(H316) -add_subdirectory(HP2100) -add_subdirectory(HP3000) -add_subdirectory(I1401) -add_subdirectory(I1620) -add_subdirectory(I650) -add_subdirectory(I7000) -add_subdirectory(I7094) -add_subdirectory(Ibm1130) -add_subdirectory(Intel-Systems/Intel-MDS) -add_subdirectory(Intel-Systems/scelbi) -add_subdirectory(Interdata) -add_subdirectory(LGP) -add_subdirectory(ND100) -add_subdirectory(NOVA) -add_subdirectory(PDP1) -add_subdirectory(PDP10) -add_subdirectory(PDP11) -add_subdirectory(PDP18B) -add_subdirectory(PDP8) -add_subdirectory(PDQ-3) -add_subdirectory(S3) -add_subdirectory(SAGE) -add_subdirectory(SDS) -add_subdirectory(SEL32) -add_subdirectory(SSEM) -add_subdirectory(TX-0) -add_subdirectory(VAX) -add_subdirectory(alpha) -add_subdirectory(imlac) -add_subdirectory(sigma) -add_subdirectory(swtp6800/swtp6800) -add_subdirectory(tt2500) +## +## This is an automagically generated file. Do NOT EDIT. +## Any changes you make will be overwritten!! +## +## Make changes to the SIMH top-level makefile and then run the +## "cmake/generate.py" script to regenerate these files. +## +## cd cmake; python -m generate --help +## +## ------------------------------------------------------------ +set(ALPHAD "${CMAKE_SOURCE_DIR}/alpha") +set(ALTAIRD "${CMAKE_SOURCE_DIR}/ALTAIR") +set(ALTAIRZ80D "${CMAKE_SOURCE_DIR}/AltairZ80") +set(ATT3B2D "${CMAKE_SOURCE_DIR}/3B2") +set(B5500D "${CMAKE_SOURCE_DIR}/B5500") +set(BESM6D "${CMAKE_SOURCE_DIR}/BESM6") +set(CDC1700D "${CMAKE_SOURCE_DIR}/CDC1700") +set(GRID "${CMAKE_SOURCE_DIR}/GRI") +set(H316D "${CMAKE_SOURCE_DIR}/H316") +set(HP2100D "${CMAKE_SOURCE_DIR}/HP2100") +set(HP3000D "${CMAKE_SOURCE_DIR}/HP3000") +set(I1401D "${CMAKE_SOURCE_DIR}/I1401") +set(I1620D "${CMAKE_SOURCE_DIR}/I1620") +set(I650D "${CMAKE_SOURCE_DIR}/I650") +set(I7000D "${CMAKE_SOURCE_DIR}/I7000") +set(I7010D "${CMAKE_SOURCE_DIR}/I7000") +set(I7094D "${CMAKE_SOURCE_DIR}/I7094") +set(IBM1130D "${CMAKE_SOURCE_DIR}/Ibm1130") +set(ID16D "${CMAKE_SOURCE_DIR}/Interdata") +set(ID32D "${CMAKE_SOURCE_DIR}/Interdata") +set(IMLACD "${CMAKE_SOURCE_DIR}/imlac") +set(INTELSYSC "${CMAKE_SOURCE_DIR}/Intel-Systems/common") +set(KA10D "${CMAKE_SOURCE_DIR}/PDP10") +set(KI10D "${CMAKE_SOURCE_DIR}/PDP10") +set(KL10D "${CMAKE_SOURCE_DIR}/PDP10") +set(KS10D "${CMAKE_SOURCE_DIR}/PDP10") +set(LGPD "${CMAKE_SOURCE_DIR}/LGP") +set(ND100D "${CMAKE_SOURCE_DIR}/ND100") +set(NOVAD "${CMAKE_SOURCE_DIR}/NOVA") +set(PDP10D "${CMAKE_SOURCE_DIR}/PDP10") +set(PDP11D "${CMAKE_SOURCE_DIR}/PDP11") +set(PDP18BD "${CMAKE_SOURCE_DIR}/PDP18B") +set(PDP1D "${CMAKE_SOURCE_DIR}/PDP1") +set(PDP6D "${CMAKE_SOURCE_DIR}/PDP10") +set(PDP8D "${CMAKE_SOURCE_DIR}/PDP8") +set(PDQ3D "${CMAKE_SOURCE_DIR}/PDQ-3") +set(S3D "${CMAKE_SOURCE_DIR}/S3") +set(SAGED "${CMAKE_SOURCE_DIR}/SAGE") +set(SDSD "${CMAKE_SOURCE_DIR}/SDS") +set(SEL32D "${CMAKE_SOURCE_DIR}/SEL32") +set(SIGMAD "${CMAKE_SOURCE_DIR}/sigma") +set(SSEMD "${CMAKE_SOURCE_DIR}/SSEM") +set(SWTP6800C "${CMAKE_SOURCE_DIR}/swtp6800/common") +set(SWTP6800D "${CMAKE_SOURCE_DIR}/swtp6800/swtp6800") +set(TT2500D "${CMAKE_SOURCE_DIR}/tt2500") +set(TX0D "${CMAKE_SOURCE_DIR}/TX-0") +set(UC15D "${CMAKE_SOURCE_DIR}/PDP11") +set(VAXD "${CMAKE_SOURCE_DIR}/VAX") + +set(DISPLAYD "${CMAKE_SOURCE_DIR}/display") +set(DISPLAY340 "${DISPLAYD}/type340.c") +set(DISPLAYIII "${DISPLAYD}/iii.c") +set(DISPLAYNG "${DISPLAYD}/ng.c") +set(DISPLAYVT "${DISPLAYD}/vt11.c") + +set(INTELSYSD "${CMAKE_SOURCE_DIR}/Intel-Systems") +set(INTEL_MDSD "${INTELSYSD}/Intel-MDS") +set(SCELBIC "${INTELSYSD}/common") +set(SCELBID "${INTELSYSD}/scelbi") + +## ---------------------------------------- + +if (NOT WITH_VIDEO) + ### Hack: Unset these variables so that they don't expand if + ### not building with video: + set(DISPLAY340 "") + set(DISPLAYIII "") + set(DISPLAYNG "") + set(DISPLAYVT "") +endif () + +## ---------------------------------------- + +add_subdirectory(3B2) +add_subdirectory(ALTAIR) +add_subdirectory(AltairZ80) +add_subdirectory(B5500) +add_subdirectory(BESM6) +add_subdirectory(CDC1700) +add_subdirectory(GRI) +add_subdirectory(H316) +add_subdirectory(HP2100) +add_subdirectory(HP3000) +add_subdirectory(I1401) +add_subdirectory(I1620) +add_subdirectory(I650) +add_subdirectory(I7000) +add_subdirectory(I7094) +add_subdirectory(Ibm1130) +add_subdirectory(Intel-Systems/Intel-MDS) +add_subdirectory(Intel-Systems/scelbi) +add_subdirectory(Interdata) +add_subdirectory(LGP) +add_subdirectory(ND100) +add_subdirectory(NOVA) +add_subdirectory(PDP1) +add_subdirectory(PDP10) +add_subdirectory(PDP11) +add_subdirectory(PDP18B) +add_subdirectory(PDP8) +add_subdirectory(PDQ-3) +add_subdirectory(S3) +add_subdirectory(SAGE) +add_subdirectory(SDS) +add_subdirectory(SEL32) +add_subdirectory(SSEM) +add_subdirectory(TX-0) +add_subdirectory(VAX) +add_subdirectory(alpha) +add_subdirectory(imlac) +add_subdirectory(sigma) +add_subdirectory(swtp6800/swtp6800) +add_subdirectory(tt2500) diff --git a/libslirp/.clang-format b/libslirp/.clang-format new file mode 100644 index 000000000..17fb49fe6 --- /dev/null +++ b/libslirp/.clang-format @@ -0,0 +1,58 @@ +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- +Language: Cpp +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false # although we like it, it creates churn +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false # churn +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterStruct: false + AfterUnion: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: false +BreakStringLiterals: true +ColumnLimit: 80 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? +MacroBlockEnd: '.*_END$' +MaxEmptyLinesToKeep: 2 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +UseTab: Never +... diff --git a/libslirp/.gitignore b/libslirp/.gitignore new file mode 100644 index 000000000..d83947860 --- /dev/null +++ b/libslirp/.gitignore @@ -0,0 +1,12 @@ +*.[aod] +*.gcda +*.gcno +*.gcov +*.lib +*.obj +/build/ +/cmake/build*/ +/TAGS +/cscope* +/src/libslirp-version.h +/tags diff --git a/libslirp/.gitlab-ci.yml b/libslirp/.gitlab-ci.yml new file mode 100644 index 000000000..7c4584eb9 --- /dev/null +++ b/libslirp/.gitlab-ci.yml @@ -0,0 +1,110 @@ +image: fedora:latest + +variables: + DEPS: meson ninja-build + gcc libasan liblsan libubsan pkg-config glib2-devel + mingw64-gcc mingw64-pkg-config mingw64-glib2 + clang-analyzer git-core + +before_script: + - dnf install -y $DEPS + - git fetch --tags https://gitlab.freedesktop.org/slirp/libslirp.git + - git describe + +build: + script: + - meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) + - ninja -C build scan-build + +build-asan: + script: + - CFLAGS=-fsanitize=address meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && ASAN_OPTIONS=detect_leaks=0 meson test) || (cat build/meson-logs/testlog.txt && exit 1) + +build-lsan: + script: + - CFLAGS=-fsanitize=leak meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) + +build-usan: + script: + - CFLAGS=-fsanitize=undefined meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) + +fuzz: + parallel: + matrix: + - TARGET: [arp, ip-header, udp, udp-h, tftp, dhcp, icmp, tcp, tcp-h, ndp, ip6-header, udp6, udp6-h, tftp6, icmp6, tcp6, tcp6-h] + script: + - CC=clang CXX=clang++ meson build -Dllvm-fuzz=true || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - build/fuzzing/fuzz-$TARGET -seed=1234 -runs=1000000 fuzzing/IN_$TARGET + artifacts: + when: on_failure + paths: + - crash-* + - leak-* + - oom-* + - timeout-* + +build-mingw64: + script: + - (mkdir buildw && cd buildw && mingw64-meson --werror) || (cat buildw/meson-logs/meson-log.txt && exit 1) + - ninja -C buildw + +Coverity: + only: + refs: + - master + - coverity + script: + - dnf update -y + - dnf install -y curl clang + - curl -o /tmp/cov-analysis-linux64.tgz https://scan.coverity.com/download/linux64 + --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$COVERITY_SCAN_TOKEN + - tar xfz /tmp/cov-analysis-linux64.tgz + - CC=clang meson build + - cov-analysis-linux64-*/bin/cov-build --dir cov-int ninja -C build + - tar cfz cov-int.tar.gz cov-int + - curl https://scan.coverity.com/builds?project=$COVERITY_SCAN_PROJECT_NAME + --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL + --form file=@cov-int.tar.gz --form version="`git describe --tags`" + --form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID " + +integration-slirp4netns: + variables: + SLIRP4NETNS_VERSION: "v1.1.12" + # Consumed by `make benchmark` + BENCHMARK_IPERF3_DURATION: "10" + script: + # Install libslirp + - meson build + - ninja -C build install + # Register the path of libslirp.so.0 + - echo /usr/local/lib64 >/etc/ld.so.conf.d/libslirp.conf + - ldconfig + # Install the dependencies of slirp4netns and its test suite + # TODO: install udhcpc for `slirp4netns/tests/test-slirp4netns-dhcp.sh` (currently skipped, due to lack of udhcpc) + - dnf install -y autoconf automake findutils iperf3 iproute iputils jq libcap-devel libseccomp-devel nmap-ncat util-linux + # Check whether the runner environment is configured correctly + - unshare -rn true || (echo Make sure you have relaxed seccomp and appamor && exit 1) + - unshare -rn ip tap add tap0 mode tap || (echo Make sure you have /dev/net/tun && exit 1) + # Install slirp4netns + - git clone https://github.com/rootless-containers/slirp4netns -b "${SLIRP4NETNS_VERSION}" + - cd slirp4netns + - ./autogen.sh + - ./configure + - make + - make install + - slirp4netns --version + # Run slirp4netns integration test + - make distcheck || (cat $(find . -name 'test-suite.log' ) && exit 1) + # Run benchmark test to ensure that libslirp can actually handle packets, with several MTU configurations + - make benchmark MTU=1500 + - make benchmark MTU=512 + - make benchmark MTU=65520 diff --git a/libslirp/.gitpublish b/libslirp/.gitpublish new file mode 100644 index 000000000..7b852951b --- /dev/null +++ b/libslirp/.gitpublish @@ -0,0 +1,3 @@ +[gitpublishprofile "default"] +base = master +to = slirp@lists.freedesktop.org diff --git a/libslirp/CHANGELOG.md b/libslirp/CHANGELOG.md new file mode 100644 index 000000000..464bd3772 --- /dev/null +++ b/libslirp/CHANGELOG.md @@ -0,0 +1,245 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [4.9.0] - TODO + +### Added + + - Add SlirpAddPollSocketCb and {,un}register_poll_socket that can be used from + SLIRP_CONFIG_VERSION_MAX 6 to properly support socket handles on win64. + +## [4.8.0] - 2024-05-09 + +## Security + + - tcp: Fix testing for last fragment + - tftp: Fix use-after-free + +### Added + + - Add support for Haiku !123 + - ncsi: Add manufacturer's ID !122 + - ncsi: Add Get Version ID command !122 + - ncsi: Add out-of-band ethernet address !125 + - ncsi: Add Mellanox Get Mac Address handler !125 + - icmp6: Add echo request forwarding support + - Add fuzzing infrastructure + +### Fixed + + - Fix missing cleanups + - windows: Build fixes + - ipv6: Use target address from Neighbor Advertisement !129 + - dns: Reject domain-search when any entry ends with ".." + - dns: Use localhost as dns when /etc/resolv.conf empty !130 + - icmp: Handle ICMP packets as IPPROTO_IP on BSD !133 + - eth: pad ethernet frames to 60 bytes #34 + +### Removed + + - windows: Bump the minimum Windows version to Windows 7 + +## [4.7.0] - 2022-04-26 + +### Added + + - Allow disabling the internal DHCP server !22 + - icmp: Support falling back on trying a SOCK_RAW socket !92 + - Support Unix sockets in hostfwd !103 + - IPv6 DNS proxying support !110 + - bootp: add support for UEFI HTTP boot !111 + - New callback that supports CFI better !117 + +### Fixed + + - dhcp: Always send DHCP_OPT_LEN bytes in options !97 + - Fix Haiku build !98 !99 + - Fix memory leak when using libresolv !100 + - Ensure sin6_scope_id is zero for global addresses !102 + - resolv: fix IPv6 resolution on Darwin !104 + - socket: Initialize so_type in socreate !109 + - Handle ECONNABORTED from recv !116 + +## [4.6.1] - 2021-06-18 + +### Fixed + + - Fix DHCP regression introduced in 4.6.0. !95 + +## [4.6.0] - 2021-06-14 + +### Added + + - mbuf: Add debugging helpers for allocation. !90 + +### Changed + + - Revert "Set macOS deployment target to macOS 10.4". !93 + +### Fixed + + - mtod()-related buffer overflows (CVE-2021-3592 #44, CVE-2021-3593 #45, + CVE-2021-3594 #47, CVE-2021-3595 #46). + - poll_fd: add missing fd registration for UDP and ICMP + - ncsi: make ncsi_calculate_checksum work with unaligned data. !89 + - Various typos and doc fixes. !88 + +## [4.5.0] - 2021-05-18 + +### Added + + - IPv6 forwarding. !62 !75 !77 + - slirp_neighbor_info() to dump the ARP/NDP tables. !71 + +### Changed + + - Lazy guest address resolution for IPv6. !81 + - Improve signal handling when spawning a child. !61 + - Set macOS deployment target to macOS 10.4. !72 + - slirp_add_hostfwd: Ensure all error paths set errno. !80 + - More API documentation. + +### Fixed + + - Assertion failure on unspecified IPv6 address. !86 + - Disable polling for PRI on MacOS, fixing some closing streams issues. !73 + - Various memory leak fixes on fastq/batchq. !68 + - Memory leak on IPv6 fast-send. !67 + - Slow socket response on Windows. !64 + - Misc build and code cleanups. !60 !63 !76 !79 !84 + +## [4.4.0] - 2020-12-02 + +### Added + + - udp, udp6, icmp: handle TTL value. !48 + - Enable forwarding ICMP errors. !49 + - Add DNS resolving for iOS. !54 + +### Changed + + - Improve meson subproject() support. !53 + - Removed Makefile-based build system. !56 + +### Fixed + + - socket: consume empty packets. !55 + - check pkt_len before reading protocol header (CVE-2020-29129). !57 + - ip_stripoptions use memmove (fixes undefined behaviour). !47 + - various Coverity-related changes/fixes. + +## [4.3.1] - 2020-07-08 + +### Changed + + - A silent truncation could occur in `slirp_fmt()`, which will now print a + critical message. See also #22. + +### Fixed + + - CVE-2020-10756 - Drop bogus IPv6 messages that could lead to data leakage. + See !44 and !42. + - Fix win32 builds by using the SLIRP_PACKED definition. + - Various coverity scan errors fixed. !41 + - Fix new GCC warnings. !43 + +## [4.3.0] - 2020-04-22 + +### Added + + - `SLIRP_VERSION_STRING` macro, with the git sha suffix when building from git + - `SlirpConfig.disable_dns`, to disable DNS redirection #16 + +### Changed + + - `slirp_version_string()` now has the git sha suffix when building form git + - Limit DNS redirection to port 53 #16 + +### Fixed + + - Fix build regression with mingw & NetBSD + - Fix use-afte-free in `ip_reass()` (CVE-2020-1983) + +## [4.2.0] - 2020-03-17 + +### Added + + - New API function `slirp_add_unix`: add a forward rule to a Unix socket. + - New API function `slirp_remove_guestfwd`: remove a forward rule previously + added by `slirp_add_exec`, `slirp_add_unix` or `slirp_add_guestfwd` + - New `SlirpConfig.outbound_addr{,6}` fields to bind output socket to a + specific address + +### Changed + + - socket: do not fallback on host loopback if `get_dns_addr()` failed + or the address is in slirp network + +### Fixed + + - ncsi: fix checksum OOB memory access + - `tcp_emu()`: fix OOB accesses + - tftp: restrict relative path access + - state: fix loading of guestfwd state + +## [4.1.0] - 2019-12-02 + +### Added + + - The `slirp_new()` API, simpler and more extensible than `slirp_init()`. + - Allow custom MTU configuration. + - Option to disable host loopback connections. + - CI now runs scan-build too. + +### Changed + + - Disable `tcp_emu()` by default. `tcp_emu()` is known to have caused + several CVEs, and not useful today in most cases. The feature can + be still enabled by setting `SlirpConfig.enable_emu` to true. + - meson build system is now `subproject()` friendly. + - Replace remaining `malloc()`/`free()` with glib (which aborts on OOM) + - Various code cleanups. + +### Deprecated + + - The `slirp_init()` API. + +### Fixed + + - `getpeername()` error after `shutdown(SHUT_WR)`. + - Exec forward: correctly parse command lines that contain spaces. + - Allow 0.0.0.0 destination address. + - Make host receive broadcast packets. + - Various memory related fixes (heap overflow, leaks, NULL + dereference). + - Compilation warnings, dead code. + +## [4.0.0] - 2019-05-24 + +### Added + + - Installable as a shared library. + - meson build system + (& make build system for in-tree QEMU integration) + +### Changed + + - Standalone project, removing any QEMU dependency. + - License clarifications. + +[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.8.0...master +[4.8.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.7.0...v4.8.0 +[4.7.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...v4.7.0 +[4.6.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.0...v4.6.1 +[4.6.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...v4.6.0 +[4.5.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.4.0...v4.5.0 +[4.4.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.1...v4.4.0 +[4.3.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.0...v4.3.1 +[4.3.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.2.0...v4.3.0 +[4.2.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.1.0...v4.2.0 +[4.1.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.0.0...v4.1.0 +[4.0.0]: https://gitlab.freedesktop.org/slirp/libslirp/commits/v4.0.0 diff --git a/libslirp/CMakeLists.txt b/libslirp/CMakeLists.txt new file mode 100644 index 000000000..419188b3a --- /dev/null +++ b/libslirp/CMakeLists.txt @@ -0,0 +1,317 @@ +if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message("") + message("*** Do NOT build or generate CMake artifacts in the source directory! ***") + message("") + message("Create a subdirectory and build in that subdirectory, e.g.:") + message("") + message(" $ mkdir cmake-build") + message(" $ cd cmake-build") + message(" $ cmake -G \"your generator here\" ..") + message("") + message(FATAL_ERROR "Preventing in-tree source build.") +endif () + +cmake_minimum_required(VERSION 3.10) + +set(SLIRP_MAJOR_VERSION 4) +set(SLIRP_MINOR_VERSION 7) +set(SLIRP_MICRO_VERSION 0) +set(SLIRP_VERSION "${SLIRP_MAJOR_VERSION}.${SLIRP_MINOR_VERSION}.${SLIRP_MICRO_VERSION}") +set(SLIRP_VERSION_STRING "\"${SLIRP_VERSION}\"") + +project(libslirp VERSION "${SLIRP_VERSION}" LANGUAGES C) + +include(CheckSymbolExists) + +## Use Release as the default build configuration when not otherwise +## specified +if (NOT CMAKE_CONFIGURATION_TYPES) + if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") + message(STATUS "CMAKE_BUILD_TYPE defaulted to ${CMAKE_BUILD_TYPE}") + endif () +endif () + +option(NOGLIB "Build a libslirp with minimal GLib support" TRUE) +option(WALL "Turn on full compiler warnings." TRUE) +option(TARGET_WINVER "Specify a specific Windows version, e.g., 0x0601 for Windows 7" "") + +set(LIBSLIRP_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/src/arp_table.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/bootp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/cksum.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/dhcpv6.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/dnssearch.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/if.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip6_icmp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip6_input.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip6_output.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip_icmp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip_input.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ip_output.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/mbuf.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/misc.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ncsi.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/ndp_table.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/sbuf.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/slirp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/socket.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/state.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/stream.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_input.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_output.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_subr.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tcp_timer.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/tftp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/udp.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/udp6.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/util.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/version.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/vmstate.c) + +if (NOGLIB) + list(APPEND LIBSLIRP_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/minimal/glib-stubs.c) +endif () + +set(EXTRA_TARGET_CFLAGS "") + +if (WALL) + if (CMAKE_C_COMPILER_ID STREQUAL "GNU") + LIST(APPEND EXTRA_TARGET_CFLAGS "$<$:-Wall>" "$<$:-Wformat>") + elseif (CMAKE_C_COMPILER_ID MATCHES ".*Clang") + if (NOT WIN32) + LIST(APPEND EXTRA_TARGET_CFLAGS "$<$:-Wall>" "$<$:-Wformat>") + else () + LIST(APPEND EXTRA_TARGET_CFLAGS "$<$:-Wall>" "$<$:-Wformat>") + endif () + elseif(MSVC) + list(APPEND EXTRA_TARGET_CFLAGS "$<$:/W3>") + endif () +endif () + +add_library(slirp SHARED ${LIBSLIRP_SRCS}) +add_library(slirp_static STATIC ${LIBSLIRP_SRCS}) + +set(EXTRA_C_DEFS "") + +## If we ever have to restore CMAKE_REQUIRED_DEFINITIONS and CMAKE_REQUIRED_LIBRARIES +set(saved_cmake_required_definitions ${CMAKE_REQUIRED_DEFINITIONS}) +set(saved_cmake_required_libraries ${CMAKE_REQUIRED_LIBRARIES}) + +if (WIN32) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") + list(APPEND CMAKE_REQUIRED_LIBRARIES ws2_32 iphlpapi) +endif () + +if (NOT NOGLIB) + ## Find GLib and use it + find_package(PkgConfig REQUIRED) + pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0) + target_link_libraries(slirp PkgConfig::deps) + target_link_libraries(slirp_static PkgConfig::deps) +else () + # Prefer clock_gettime over gettimeofday: + check_symbol_exists(clock_gettime time.h HAVE_CLOCK_GETTIME) + + if (HAVE_CLOCK_GETTIME) + list(APPEND EXTRA_C_DEFS HAVE_TIME_H HAVE_CLOCK_GETTIME) + else () + check_symbol_exists(gettimeofday sys/time.h SYS_TIME_H_GETTIMEOFDAY) + if (SYS_TIME_H_GETTIMEOFDAY) + list(APPEND EXTRA_C_DEFS HAVE_SYS_TIME_H HAVE_GETTIMEOFDAY) + endif () + endif () + + check_symbol_exists(vasprintf stdio.h HAVE_VASPRINTF) + if (HAVE_VASPRINTF) + list(APPEND EXTRA_C_DEFS HAVE_VASPRINTF) + else () + if (NOT HAVE_VASPRINTF) + set(save_cmake_reqd_defs ${CMAKE_REQUIRED_DEFINITIONS}) + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + check_symbol_exists(vasprintf stdio.h HAVE_VASPRINTF_GNU_SOURCE) + set(CMAKE_REQUIRED_DEFINITIONS ${save_cmake_reqd_defs}) + + if (HAVE_VASPRINTF_GNU_SOURCE) + list(APPEND EXTRA_C_DEFS HAVE_VASPRINTF _GNU_SOURCE) + endif () + endif () + endif () + + target_include_directories(slirp BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/minimal") + target_include_directories(slirp_static BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/minimal") +endif () + +# Create the version file in the build directory's build-include subdir: +file(READ src/libslirp-version.h.in SLIRP_VERSION_FILE) +file(CONFIGURE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build-include/libslirp-version.h" CONTENT "${SLIRP_VERSION_FILE}" @ONLY) +set(SLIRP_VERSION_FILE) + +# And also create the endianness defines: +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.20") + set(G_BYTE_ORDER "G_LITTLE_ENDIAN") + if (CMAKE_C_BYTE_ORDER EQUAL "BIG_ENDIAN") + set(G_BYTE_ORDER "G_BIG_ENDIAN") + endif () +else () + include(TestBigEndian) + test_big_endian(BYTE_ORDER) + set(G_BYTE_ORDER "G_LITTLE_ENDIAN") + if (BYTE_ORDER EQUAL 1) + set(G_BYTE_ORDER "G_BIG_ENDIAN") + endif () + endif () + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/minimal/glib-endian.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/build-include/glib-endian.h") + +if (WIN32) + check_symbol_exists(inet_pton "winsock2.h;windows.h;ws2tcpip.h" HAVE_INET_PTON) + + target_link_libraries(slirp PUBLIC ws2_32 iphlpapi) + target_link_libraries(slirp_static PUBLIC ws2_32 iphlpapi) +else () + check_symbol_exists(inet_pton "arpa/inet.h" HAVE_INET_PTON) +endif () + +if (HAVE_INET_PTON) + list(APPEND EXTRA_C_DEFS HAVE_INET_PTON) +endif () + +## macOS needs -lresolv: +if (APPLE) + target_link_libraries(slirp PUBLIC resolv) + target_link_libraries(slirp_static PUBLIC resolv) +endif () + +## ISO C safety functions... nice to use when available (and reduces the "deprecation" +## warnings on Windows. Will need updates on other platforms ) +set(saved_iso_c_required_defs ${CMAKE_REQUIRED_DEFINITIONS}) +list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D__STDC_WANT_LIB_EXT1__=1" "-D__STDC_WANT_LIB_EXT2__=1") + +check_symbol_exists(_sopen_s io.h HAVE__SOPEN_S) +if (HAVE__SOPEN_S) + list(APPEND EXTRA_C_DEFS HAVE__SOPEN_S) +endif () + +check_symbol_exists(_lseek io.h HAVE__LSEEK) +if (HAVE__LSEEK) + list(APPEND EXTRA_C_DEFS HAVE__LSEEK) +endif () + +check_symbol_exists(_read io.h HAVE__READ) +if (HAVE__READ) + list(APPEND EXTRA_C_DEFS HAVE__READ) +endif () + +check_symbol_exists(_close io.h HAVE__CLOSE) +if (HAVE__CLOSE) + list(APPEND EXTRA_C_DEFS HAVE__CLOSE) +endif () + +check_symbol_exists(_strdup string.h HAVE__STRDUP_S) +if (HAVE__STRDUP_S) + list(APPEND EXTRA_C_DEFS HAVE__STRDUP_S) +endif () + +check_symbol_exists(_dupenv_s stdlib.h HAVE_DUPENV_S) +if (HAVE_DUPENV_S) + list(APPEND EXTRA_C_DEFS HAVE_DUPENV_S) +endif () + +check_symbol_exists(strcpy_s string.h HAVE_STRCPY_S) +if (HAVE_STRCPY_S) + list(APPEND EXTRA_C_DEFS HAVE_STRCPY_S) +endif () + +check_symbol_exists(strerror_s string.h HAVE_STRERROR_S) +if (HAVE_STRERROR_S) + list(APPEND EXTRA_C_DEFS HAVE_STRERROR_S) +endif () + +check_symbol_exists(sscanf_s stdio.h HAVE_SSCANF_S) +if (HAVE_SSCANF_S) + list(APPEND EXTRA_C_DEFS HAVE_SSCANF_S) +endif () + +set(CMAKE_REQUIRED_DEFINITIONS ${saved_iso_c_required_defs}) + +target_compile_definitions(slirp PRIVATE G_LOG_DOMAIN="Slirp" BUILDING_LIBSLIRP + ${EXTRA_C_DEFS}) +target_compile_definitions(slirp_static PRIVATE G_LOG_DOMAIN="Slirp" BUILDING_LIBSLIRP + slirp_static ${EXTRA_C_DEFS}) +target_compile_definitions(slirp_static PUBLIC LIBSLIRP_STATIC) + +if (TARGET_WINVER) + target_compile_definitions(slirp PRIVATE "WINVER=${TARGET_WINVER}" "_WIN32_WINNT=${TARGET_WINVER}") + target_compile_definitions(slirp_static PRIVATE "WINVER=${TARGET_WINVER}" "_WIN32_WINNT=${TARGET_WINVER}") +endif () + +target_include_directories(slirp AFTER PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_include_directories(slirp_static AFTER PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") +target_include_directories(slirp AFTER PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/build-include") +target_include_directories(slirp_static AFTER PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/build-include") + +target_compile_options(slirp PRIVATE ${EXTRA_TARGET_CFLAGS}) +target_compile_options(slirp_static PRIVATE ${EXTRA_TARGET_CFLAGS}) + +### Helper function to build test executables: + +function(fab_test_executable _targ) + cmake_parse_arguments(SLIRP "USE_SHARED;NOLIB" "" "SOURCES" ${ARGN}) + + add_executable(${_targ} ${SLIRP_SOURCES}) + target_compile_definitions(${_targ} PRIVATE ${EXTRA_C_DEFS}) + target_include_directories(${_targ} BEFORE PRIVATE "src" "${CMAKE_CURRENT_BINARY_DIR}/build-include") + if (NOGLIB) + target_include_directories(slirp AFTER PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") + target_include_directories(${_targ} BEFORE PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/minimal") + target_include_directories(slirp_static AFTER PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/build-include") + endif () + + target_compile_options(${_targ} PRIVATE ${EXTRA_TARGET_CFLAGS}) + + if (NOT SLIRP_NOLIB) + if (SLIRP_USE_SHARED) + target_link_libraries(${_targ} PUBLIC slirp) + else () + target_link_libraries(${_targ} PUBLIC slirp_static) + endif () + endif () + + if (WIN32) + target_link_libraries(${_targ} PUBLIC ws2_32 iphlpapi) + endif () +endfunction() + + +### Tests: + +include(CTest) + +## pingtest: +fab_test_executable(pingtest SOURCES "test/pingtest.c") +add_test(NAME "pingtest" COMMAND pingtest) + +## ncsitest: +fab_test_executable(ncsitest SOURCES "test/ncsitest.c") +add_test(NAME "ncsitest" COMMAND ncsitest) + +if (NOGLIB) + ## String operations + fab_test_executable(stringops_test USE_SHARED NOLIB SOURCES "minimal/tests/stringops_test.c" "minimal/glib-stubs.c") + add_test(NAME "minimal:stringops" COMMAND stringops_test) + + ## strstr_test: + fab_test_executable(strstr_test USE_SHARED NOLIB SOURCES "minimal/tests/strstr_len_test.c" "minimal/glib-stubs.c") + add_test(NAME "minimal:strstr" COMMAND strstr_test) + + ## Debug parser + fab_test_executable(parsedebug_test NOLIB SOURCES "minimal/tests/parsedebug.c" "minimal/glib-stubs.c") + add_test(NAME "minimal:parse_debug" COMMAND parsedebug_test) + + # GRand random numbers. + fab_test_executable(grand_test NOLIB SOURCES "minimal/tests/grand_test.c" "minimal/glib-stubs.c") + add_test(NAME "minimal:grand_test" COMMAND grand_test) +endif () + diff --git a/slirp/COPYRIGHT b/libslirp/COPYRIGHT similarity index 93% rename from slirp/COPYRIGHT rename to libslirp/COPYRIGHT index 3927a5b5f..ed49512db 100644 --- a/slirp/COPYRIGHT +++ b/libslirp/COPYRIGHT @@ -1,61 +1,62 @@ -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. +Slirp was written by Danny Gasparovski. +Copyright (c), 1995,1996 All Rights Reserved. + +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. + 3. Neither the name of the copyright holder 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 ``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. diff --git a/libslirp/README.md b/libslirp/README.md new file mode 100644 index 000000000..9f9c1b14f --- /dev/null +++ b/libslirp/README.md @@ -0,0 +1,60 @@ +# libslirp + +libslirp is a user-mode networking library used by virtual machines, +containers or various tools. + +## Getting Started + +### Prerequisites + +A C compiler, meson and glib2 development libraries. + +(see also [.gitlab-ci.yml](.gitlab-ci.yml) DEPS variable for the list +of dependencies on Fedora) + +### Building + +You may build and install the shared library with meson: + +``` sh +meson build +ninja -C build install +``` +And configure QEMU with --enable-slirp=system to link against it. + +(QEMU may build with the submodule static library using --enable-slirp=git) + +### Testing + +Unfortunately, there are no automated tests available. + +You may run QEMU ``-net user`` linked with your development version. + +## Contributing + +Feel free to open issues on the [project +issues](https://gitlab.freedesktop.org/slirp/libslirp/issues) page. + +You may clone the [gitlab +project](https://gitlab.freedesktop.org/slirp/libslirp) and create a +merge request. + +Contributing with gitlab allows gitlab workflow, tracking issues, +running CI etc. + +Alternatively, you may send patches to slirp@lists.freedesktop.org +mailing list. + +## Versioning + +We intend to use [libtool's +versioning](https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html) +for the shared libraries and use [SemVer](http://semver.org/) for +project versions. + +For the versions available, see the [tags on this +repository](https://gitlab.freedesktop.org/slirp/libslirp/releases). + +## License + +See the [COPYRIGHT](COPYRIGHT) file for details. diff --git a/libslirp/build-aux/git-version-gen b/libslirp/build-aux/git-version-gen new file mode 100644 index 000000000..5617eb8d4 --- /dev/null +++ b/libslirp/build-aux/git-version-gen @@ -0,0 +1,158 @@ +#!/bin/sh +# Print a version string. +scriptversion=2010-06-14.19; # UTC + +# Copyright (C) 2007-2010 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. +# It may be run two ways: +# - from a git repository in which the "git describe" command below +# produces useful output (thus requiring at least one signed tag) +# - from a non-git-repo directory containing a .tarball-version file, which +# presumes this script is invoked like "./git-version-gen .tarball-version". + +# In order to use intra-version strings in your project, you will need two +# separate generated version string files: +# +# .tarball-version - present only in a distribution tarball, and not in +# a checked-out repository. Created with contents that were learned at +# the last time autoconf was run, and used by git-version-gen. Must not +# be present in either $(srcdir) or $(builddir) for git-version-gen to +# give accurate answers during normal development with a checked out tree, +# but must be present in a tarball when there is no version control system. +# Therefore, it cannot be used in any dependencies. GNUmakefile has +# hooks to force a reconfigure at distribution time to get the value +# correct, without penalizing normal development with extra reconfigures. +# +# .version - present in a checked-out repository and in a distribution +# tarball. Usable in dependencies, particularly for files that don't +# want to depend on config.h but do want to track version changes. +# Delete this file prior to any autoconf run where you want to rebuild +# files to pick up a version string change; and leave it stale to +# minimize rebuild time after unrelated changes to configure sources. +# +# It is probably wise to add these two files to .gitignore, so that you +# don't accidentally commit either generated file. +# +# Use the following line in your configure.ac, so that $(VERSION) will +# automatically be up-to-date each time configure is run (and note that +# since configure.ac no longer includes a version string, Makefile rules +# should not depend on configure.ac for version updates). +# +# AC_INIT([GNU project], +# m4_esyscmd([build-aux/git-version-gen .tarball-version]), +# [bug-project@example]) +# +# Then use the following lines in your Makefile.am, so that .version +# will be present for dependencies, and so that .tarball-version will +# exist in distribution tarballs. +# +# BUILT_SOURCES = $(top_srcdir)/.version +# $(top_srcdir)/.version: +# echo $(VERSION) > $@-t && mv $@-t $@ +# dist-hook: +# echo $(VERSION) > $(distdir)/.tarball-version + +case $# in + 1|2) ;; + *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version" \ + '[TAG-NORMALIZATION-SED-SCRIPT]' + exit 1;; +esac + +tarball_version_file=$1 +tag_sed_script="${2:-s/x/x/}" +nl=' +' + +# Avoid meddling by environment variable of the same name. +v= + +# First see if there is a tarball-only version file. +# then try "git describe", then default. +if test -f $tarball_version_file +then + v=`cat $tarball_version_file` || exit 1 + case $v in + *$nl*) v= ;; # reject multi-line output + [0-9]*) ;; + *) v= ;; + esac + test -z "$v" \ + && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 +fi + +if test -n "$v" +then + : # use $v +elif test -d .git \ + && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ + || git describe --abbrev=4 HEAD 2>/dev/null` \ + && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \ + && case $v in + v[0-9]*) ;; + *) (exit 1) ;; + esac +then + # Is this a new git that lists number of commits since the last + # tag or the previous older version that did not? + # Newer: v6.10-77-g0f8faeb + # Older: v6.10-g0f8faeb + case $v in + *-*-*) : git describe is okay three part flavor ;; + *-*) + : git describe is older two part flavor + # Recreate the number of commits and rewrite such that the + # result is the same as if we were using the newer version + # of git describe. + vtag=`echo "$v" | sed 's/-.*//'` + numcommits=`git rev-list "$vtag"..HEAD | wc -l` + v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; + ;; + esac + + # Change the first '-' to a '.', so version-comparing tools work properly. + # Remove the "g" in git describe's output string, to save a byte. + v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; +else + v=UNKNOWN +fi + +v=`echo "$v" |sed 's/^v//'` + +# Don't declare a version "dirty" merely because a time stamp has changed. +git update-index --refresh > /dev/null 2>&1 + +dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= +case "$dirty" in + '') ;; + *) # Append the suffix only if there isn't one already. + case $v in + *-dirty) ;; + *) v="$v-dirty" ;; + esac ;; +esac + +# Omit the trailing newline, so that m4_esyscmd can use the result directly. +echo "$v" | tr -d "$nl" + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/libslirp/fuzzing/IN_arp/arp.pcap b/libslirp/fuzzing/IN_arp/arp.pcap new file mode 100644 index 000000000..f920e213e Binary files /dev/null and b/libslirp/fuzzing/IN_arp/arp.pcap differ diff --git a/libslirp/fuzzing/IN_dhcp/dhcp.pkt b/libslirp/fuzzing/IN_dhcp/dhcp.pkt new file mode 100644 index 000000000..920003771 Binary files /dev/null and b/libslirp/fuzzing/IN_dhcp/dhcp.pkt differ diff --git a/libslirp/fuzzing/IN_dhcp/dhcp_capture.pcap b/libslirp/fuzzing/IN_dhcp/dhcp_capture.pcap new file mode 100644 index 000000000..99ae0a83a Binary files /dev/null and b/libslirp/fuzzing/IN_dhcp/dhcp_capture.pcap differ diff --git a/libslirp/fuzzing/IN_icmp/icmp_capture.pcap b/libslirp/fuzzing/IN_icmp/icmp_capture.pcap new file mode 100644 index 000000000..6a0f0b835 Binary files /dev/null and b/libslirp/fuzzing/IN_icmp/icmp_capture.pcap differ diff --git a/libslirp/fuzzing/IN_icmp/ping_10-0-2-2.pcap b/libslirp/fuzzing/IN_icmp/ping_10-0-2-2.pcap new file mode 100644 index 000000000..69b60fbca Binary files /dev/null and b/libslirp/fuzzing/IN_icmp/ping_10-0-2-2.pcap differ diff --git a/libslirp/fuzzing/IN_icmp6/icmp_capture.pcap b/libslirp/fuzzing/IN_icmp6/icmp_capture.pcap new file mode 100644 index 000000000..365bed104 Binary files /dev/null and b/libslirp/fuzzing/IN_icmp6/icmp_capture.pcap differ diff --git a/libslirp/fuzzing/IN_icmp6/ndp.pcap b/libslirp/fuzzing/IN_icmp6/ndp.pcap new file mode 100644 index 000000000..74443f1e2 --- /dev/null +++ b/libslirp/fuzzing/IN_icmp6/ndp.pcap @@ -0,0 +1 @@ +../IN_ndp/ndp.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_icmp6/ping_10-0-2-2.pcap b/libslirp/fuzzing/IN_icmp6/ping_10-0-2-2.pcap new file mode 100644 index 000000000..87f63899d Binary files /dev/null and b/libslirp/fuzzing/IN_icmp6/ping_10-0-2-2.pcap differ diff --git a/libslirp/fuzzing/IN_ip-header/DNS_freedesktop_1-1-1-1.pcap b/libslirp/fuzzing/IN_ip-header/DNS_freedesktop_1-1-1-1.pcap new file mode 100644 index 000000000..09b9ce1c1 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/DNS_freedesktop_1-1-1-1.pcap @@ -0,0 +1 @@ +../IN_udp/DNS_freedesktop_1-1-1-1.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/dhcp.pkt b/libslirp/fuzzing/IN_ip-header/dhcp.pkt new file mode 100644 index 000000000..b1a9e0456 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/dhcp.pkt @@ -0,0 +1 @@ +../IN_dhcp/dhcp.pkt \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/dhcp_capture.pcap b/libslirp/fuzzing/IN_ip-header/dhcp_capture.pcap new file mode 100644 index 000000000..e2d9c0db9 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/dhcp_capture.pcap @@ -0,0 +1 @@ +../IN_dhcp/dhcp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/icmp_capture.pcap b/libslirp/fuzzing/IN_ip-header/icmp_capture.pcap new file mode 100644 index 000000000..a9ec19360 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/icmp_capture.pcap @@ -0,0 +1 @@ +../IN_icmp/icmp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/nc-10.0.2.2-8080.pcap b/libslirp/fuzzing/IN_ip-header/nc-10.0.2.2-8080.pcap new file mode 100644 index 000000000..c99b568e4 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/nc-10.0.2.2-8080.pcap @@ -0,0 +1 @@ +../IN_tcp/nc-10.0.2.2-8080.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/nc-ident.pcap b/libslirp/fuzzing/IN_ip-header/nc-ident.pcap new file mode 100644 index 000000000..51ecdf0eb --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/nc-ident.pcap @@ -0,0 +1 @@ +../IN_tcp/nc-ident.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/ping_10-0-2-2.pcap b/libslirp/fuzzing/IN_ip-header/ping_10-0-2-2.pcap new file mode 100644 index 000000000..9a71a0e4d --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/ping_10-0-2-2.pcap @@ -0,0 +1 @@ +../IN_icmp/ping_10-0-2-2.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/tcp_qemucapt.pcap b/libslirp/fuzzing/IN_ip-header/tcp_qemucapt.pcap new file mode 100644 index 000000000..8daa990e1 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/tcp_qemucapt.pcap @@ -0,0 +1 @@ +../IN_tcp/tcp_qemucapt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/tftp-get-blah.pkt b/libslirp/fuzzing/IN_ip-header/tftp-get-blah.pkt new file mode 100644 index 000000000..c95e90d96 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/tftp-get-blah.pkt @@ -0,0 +1 @@ +../IN_tftp/tftp-get-blah.pkt \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/tftp_capture.pcap b/libslirp/fuzzing/IN_ip-header/tftp_capture.pcap new file mode 100644 index 000000000..cbaf3ab0d --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/tftp_capture.pcap @@ -0,0 +1 @@ +../IN_tftp/tftp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip-header/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_ip-header/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..8aa1945d2 --- /dev/null +++ b/libslirp/fuzzing/IN_ip-header/tftp_get_libslirp-txt.pcap @@ -0,0 +1 @@ +../IN_tftp/tftp_get_libslirp-txt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap b/libslirp/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap new file mode 100644 index 000000000..651c2739a --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/DNS_freedesktop_1-1-1-1.pcap @@ -0,0 +1 @@ +../IN_udp6/DNS_freedesktop_1-1-1-1.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/icmp_capture.pcap b/libslirp/fuzzing/IN_ip6-header/icmp_capture.pcap new file mode 100644 index 000000000..519f78a70 --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/icmp_capture.pcap @@ -0,0 +1 @@ +../IN_icmp6/icmp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap b/libslirp/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap new file mode 100644 index 000000000..632a2868d --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/ping_10-0-2-2.pcap @@ -0,0 +1 @@ +../IN_icmp6/ping_10-0-2-2.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/tcp_qemucapt.pcap b/libslirp/fuzzing/IN_ip6-header/tcp_qemucapt.pcap new file mode 100644 index 000000000..b444205b9 --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/tcp_qemucapt.pcap @@ -0,0 +1 @@ +../IN_tcp6/tcp_qemucapt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/tftp_capture.pcap b/libslirp/fuzzing/IN_ip6-header/tftp_capture.pcap new file mode 100644 index 000000000..308ab28e0 --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/tftp_capture.pcap @@ -0,0 +1 @@ +../IN_udp6/tftp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..d3b4e76db --- /dev/null +++ b/libslirp/fuzzing/IN_ip6-header/tftp_get_libslirp-txt.pcap @@ -0,0 +1 @@ +../IN_udp6/tftp_get_libslirp-txt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_ndp/ndp.pcap b/libslirp/fuzzing/IN_ndp/ndp.pcap new file mode 100644 index 000000000..ed8db971a Binary files /dev/null and b/libslirp/fuzzing/IN_ndp/ndp.pcap differ diff --git a/libslirp/fuzzing/IN_tcp-d b/libslirp/fuzzing/IN_tcp-d new file mode 100644 index 000000000..1bca80b47 --- /dev/null +++ b/libslirp/fuzzing/IN_tcp-d @@ -0,0 +1 @@ +IN_tcp \ No newline at end of file diff --git a/libslirp/fuzzing/IN_tcp-h b/libslirp/fuzzing/IN_tcp-h new file mode 100644 index 000000000..1bca80b47 --- /dev/null +++ b/libslirp/fuzzing/IN_tcp-h @@ -0,0 +1 @@ +IN_tcp \ No newline at end of file diff --git a/libslirp/fuzzing/IN_tcp/nc-10.0.2.2-8080.pcap b/libslirp/fuzzing/IN_tcp/nc-10.0.2.2-8080.pcap new file mode 100644 index 000000000..48bd88164 Binary files /dev/null and b/libslirp/fuzzing/IN_tcp/nc-10.0.2.2-8080.pcap differ diff --git a/libslirp/fuzzing/IN_tcp/nc-ident.pcap b/libslirp/fuzzing/IN_tcp/nc-ident.pcap new file mode 100644 index 000000000..3d0421b55 Binary files /dev/null and b/libslirp/fuzzing/IN_tcp/nc-ident.pcap differ diff --git a/libslirp/fuzzing/IN_tcp/tcp_qemucapt.pcap b/libslirp/fuzzing/IN_tcp/tcp_qemucapt.pcap new file mode 100644 index 000000000..83a0eddfa Binary files /dev/null and b/libslirp/fuzzing/IN_tcp/tcp_qemucapt.pcap differ diff --git a/libslirp/fuzzing/IN_tcp6-d b/libslirp/fuzzing/IN_tcp6-d new file mode 100644 index 000000000..2ad34597b --- /dev/null +++ b/libslirp/fuzzing/IN_tcp6-d @@ -0,0 +1 @@ +IN_tcp6 \ No newline at end of file diff --git a/libslirp/fuzzing/IN_tcp6-h b/libslirp/fuzzing/IN_tcp6-h new file mode 100644 index 000000000..2ad34597b --- /dev/null +++ b/libslirp/fuzzing/IN_tcp6-h @@ -0,0 +1 @@ +IN_tcp6 \ No newline at end of file diff --git a/libslirp/fuzzing/IN_tcp6/tcp_qemucapt.pcap b/libslirp/fuzzing/IN_tcp6/tcp_qemucapt.pcap new file mode 100644 index 000000000..d7936b32a Binary files /dev/null and b/libslirp/fuzzing/IN_tcp6/tcp_qemucapt.pcap differ diff --git a/libslirp/fuzzing/IN_tftp/tftp-get-blah.pkt b/libslirp/fuzzing/IN_tftp/tftp-get-blah.pkt new file mode 100644 index 000000000..c540ccf38 Binary files /dev/null and b/libslirp/fuzzing/IN_tftp/tftp-get-blah.pkt differ diff --git a/libslirp/fuzzing/IN_tftp/tftp_capture.pcap b/libslirp/fuzzing/IN_tftp/tftp_capture.pcap new file mode 100644 index 000000000..6fed42772 Binary files /dev/null and b/libslirp/fuzzing/IN_tftp/tftp_capture.pcap differ diff --git a/libslirp/fuzzing/IN_tftp/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_tftp/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..18defe4d0 Binary files /dev/null and b/libslirp/fuzzing/IN_tftp/tftp_get_libslirp-txt.pcap differ diff --git a/libslirp/fuzzing/IN_tftp6/tftp_capture.pcap b/libslirp/fuzzing/IN_tftp6/tftp_capture.pcap new file mode 100644 index 000000000..dd3ee7186 Binary files /dev/null and b/libslirp/fuzzing/IN_tftp6/tftp_capture.pcap differ diff --git a/libslirp/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..caa144a02 Binary files /dev/null and b/libslirp/fuzzing/IN_tftp6/tftp_get_libslirp-txt.pcap differ diff --git a/libslirp/fuzzing/IN_udp-h b/libslirp/fuzzing/IN_udp-h new file mode 100644 index 000000000..d1958903a --- /dev/null +++ b/libslirp/fuzzing/IN_udp-h @@ -0,0 +1 @@ +IN_udp \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp/DNS_freedesktop_1-1-1-1.pcap b/libslirp/fuzzing/IN_udp/DNS_freedesktop_1-1-1-1.pcap new file mode 100644 index 000000000..9950156ae Binary files /dev/null and b/libslirp/fuzzing/IN_udp/DNS_freedesktop_1-1-1-1.pcap differ diff --git a/libslirp/fuzzing/IN_udp/dhcp.pkt b/libslirp/fuzzing/IN_udp/dhcp.pkt new file mode 100644 index 000000000..b1a9e0456 --- /dev/null +++ b/libslirp/fuzzing/IN_udp/dhcp.pkt @@ -0,0 +1 @@ +../IN_dhcp/dhcp.pkt \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp/dhcp_capture.pcap b/libslirp/fuzzing/IN_udp/dhcp_capture.pcap new file mode 100644 index 000000000..e2d9c0db9 --- /dev/null +++ b/libslirp/fuzzing/IN_udp/dhcp_capture.pcap @@ -0,0 +1 @@ +../IN_dhcp/dhcp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp/tftp-get-blah.pkt b/libslirp/fuzzing/IN_udp/tftp-get-blah.pkt new file mode 100644 index 000000000..c95e90d96 --- /dev/null +++ b/libslirp/fuzzing/IN_udp/tftp-get-blah.pkt @@ -0,0 +1 @@ +../IN_tftp/tftp-get-blah.pkt \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp/tftp_capture.pcap b/libslirp/fuzzing/IN_udp/tftp_capture.pcap new file mode 100644 index 000000000..cbaf3ab0d --- /dev/null +++ b/libslirp/fuzzing/IN_udp/tftp_capture.pcap @@ -0,0 +1 @@ +../IN_tftp/tftp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_udp/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..8aa1945d2 --- /dev/null +++ b/libslirp/fuzzing/IN_udp/tftp_get_libslirp-txt.pcap @@ -0,0 +1 @@ +../IN_tftp/tftp_get_libslirp-txt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp6-h b/libslirp/fuzzing/IN_udp6-h new file mode 100644 index 000000000..4d7837b0d --- /dev/null +++ b/libslirp/fuzzing/IN_udp6-h @@ -0,0 +1 @@ +IN_udp6 \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap b/libslirp/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap new file mode 100644 index 000000000..87abc1238 Binary files /dev/null and b/libslirp/fuzzing/IN_udp6/DNS_freedesktop_1-1-1-1.pcap differ diff --git a/libslirp/fuzzing/IN_udp6/tftp_capture.pcap b/libslirp/fuzzing/IN_udp6/tftp_capture.pcap new file mode 100644 index 000000000..9bd683036 --- /dev/null +++ b/libslirp/fuzzing/IN_udp6/tftp_capture.pcap @@ -0,0 +1 @@ +../IN_tftp6/tftp_capture.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap b/libslirp/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap new file mode 100644 index 000000000..39fc722be --- /dev/null +++ b/libslirp/fuzzing/IN_udp6/tftp_get_libslirp-txt.pcap @@ -0,0 +1 @@ +../IN_tftp6/tftp_get_libslirp-txt.pcap \ No newline at end of file diff --git a/libslirp/fuzzing/README.md b/libslirp/fuzzing/README.md new file mode 100644 index 000000000..61a6696a5 --- /dev/null +++ b/libslirp/fuzzing/README.md @@ -0,0 +1,59 @@ +# Fuzzing libslirp state and instructions + +## Current state +We chose to use libFuzzer because of its custom mutator feature, which allows to keep coherent informations inside the packets being sent to libslirp. This ease the process of fuzzing as packets are less likely to be rejected early during processing them. + +In the current state, the `meson.build` file is not compatible with the original one used by libSlirp main repository but it should be easy to merge them in a clean way. + +Six harness are currently available, more are to be added later to focus on other parts of the code : + +- **fuzz-ip-header** : the mutator focuses on the ip header field informations, +- **fuzz-udp** : the mutator only work on udp packets, mutating the udp header and content, or only one or the other (-h,-d), +- **fuzz-tcp** : the mutator targets tcp packets, header+data or only one or the other, or only one or the other (-h,-d), +- **fuzz-icmp** : the mutator focuses on icmp packets, + +These harness should be good starting examples on how to fuzz libslirp using libFuzzer. + +## Running the fuzzer + +Building the fuzzers/harness requires the use of clang as libFuzzer is part of LLVM. +You can build it running : + +`CC=clang meson build && ninja -C build` + +It will build the fuzzer in the ./build/fuzzing/ directory. + +A script named `fuzzing/coverage.py` is available to generate coverage informations. **It makes a lot of assumptions on the directory structure** and should be read before use. + +To run the fuzzer, simply run some of: + +- `build/fuzzing/fuzz-ip-header fuzzing/IN_ip-header` +- `build/fuzzing/fuzz-udp fuzzing/IN_udp` +- `build/fuzzing/fuzz-udp-h fuzzing/IN_udp-h` +- `build/fuzzing/fuzz-tftp fuzzing/IN_tftp` +- `build/fuzzing/fuzz-dhcp fuzzing/IN_dhcp` +- `build/fuzzing/fuzz-icmp fuzzing/IN_icmp` +- `build/fuzzing/fuzz-tcp fuzzing/IN_tcp` + +Your current directory should be a separate directory as crashes to it. New inputs found by the fuzzer will go directly in the `IN` folder. + +# Adding new files to the corpus + +In its current state, the fuzzing code is taking pcap files as input, we produced some using `tcpdump` on linux inside qemu with default settings. +Those files should be captured using the `EN10MB (Ethernet)` data link type, this can be set with the flag `-y` but it seems this can't be done while listening on all interfaces (`-i any`). +New files should give new coverage, to ensure a new file is usefull the `coverage.py` script (see next section) can be used to compare the coverage with and without that new file. + +# Coverage + +The `coverage.py` script allows to see coverage informations about the corpus. It makes a lot of assumptions on the directory structure so it should be read and probably modified before running it. +It must be called with the protocol to cover: `python coverage.py udp report`. +To generate coverage informations, the following flags are passed to the fuzzer and libslirp : + +- g +- fsanitize-coverage=edge,indirect-calls,trace-cmp +- fprofile-instr-generate +- fcoverage-mapping + +The last 2 arguments should also be passed to the linker. + +Then the `llvm-profdata` and `llvm-cov` tools can be used to generate a report and a fancy set of HTML files with line-coverage informations. diff --git a/libslirp/fuzzing/coverage.py b/libslirp/fuzzing/coverage.py new file mode 100644 index 000000000..861f2acf6 --- /dev/null +++ b/libslirp/fuzzing/coverage.py @@ -0,0 +1,37 @@ +from os import chdir,listdir,environ +from os.path import isfile,join,isdir +from subprocess import DEVNULL, run +import sys + +ignored_files = "-ignore-filename-regex=glib -ignore-filename-regex=fuzz -ignore-filename-regex=helper -ignore-filename-regex=h$" + +if __name__ == "__main__": + chdir("build/fuzzing/out") + available_targets = [exe for exe in listdir("../") if isfile(join("..", exe))] + available_corpus_path = [exe for exe in listdir("../../../fuzzing/") if isdir(join("../../../fuzzing/", exe))] + available_result_types = ["export", "show", "report"] + if len(sys.argv) != 4 or sys.argv[1] not in available_targets or sys.argv[2] not in available_corpus_path or sys.argv[3] not in available_result_types: + print("usage : python coverage.py fuzz_target IN_protol result_type") + print(" - available targets : ") + print(available_targets) + print(" - available_corpus_path : ") + print(available_corpus_path) + print(" - available result types : ") + print(available_result_types) + exit(0) + fuzzing_target = sys.argv[1] + corpus_path = "../../../fuzzing/"+sys.argv[2]+"/" + result_type = sys.argv[3] + if fuzzing_target in available_targets: + environ["LLVM_PROFILE_FILE"] = fuzzing_target + "_%p.profraw" + corpus = listdir(corpus_path) + for f in corpus: + #print(corpus_path+f) + run(["../" + fuzzing_target, corpus_path+f,"-detect_leaks=0"], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL) + run(["llvm-profdata merge -sparse " + fuzzing_target + "_*.profraw -o " + fuzzing_target + ".profdata"], shell=True) + if result_type == "export" : + run(["llvm-cov show ../" + fuzzing_target + " -format=html -output-dir=../report -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True) + elif result_type == "show" : + run(["llvm-cov show ../" + fuzzing_target + " -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True) + else: + run(["llvm-cov report ../" + fuzzing_target + " -instr-profile=" + fuzzing_target + ".profdata " + ignored_files], shell=True) diff --git a/libslirp/fuzzing/fuzz-input.options b/libslirp/fuzzing/fuzz-input.options new file mode 100644 index 000000000..794888809 --- /dev/null +++ b/libslirp/fuzzing/fuzz-input.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 1024 diff --git a/libslirp/fuzzing/fuzz-main.c b/libslirp/fuzzing/fuzz-main.c new file mode 100644 index 000000000..90c9f6460 --- /dev/null +++ b/libslirp/fuzzing/fuzz-main.c @@ -0,0 +1,36 @@ +#include +#include + +#define MIN_NUMBER_OF_RUNS 1 +#define EXIT_TEST_SKIP 77 + +extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); + +int main(int argc, char **argv) +{ + int i, j; + + for (i = 1; i < argc; i++) { + GError *err = NULL; + char *name = argv[i]; + char *buf; + size_t size; + + if (!g_file_get_contents(name, &buf, &size, &err)) { + g_warning("Failed to read '%s': %s", name, err->message); + g_clear_error(&err); + return EXIT_FAILURE; + } + + g_print("%s...\n", name); + for (j = 0; j < MIN_NUMBER_OF_RUNS; j++) { + if (LLVMFuzzerTestOneInput((void *)buf, size) == EXIT_TEST_SKIP) { + g_free(buf); + return EXIT_TEST_SKIP; + } + } + g_free(buf); + } + + return EXIT_SUCCESS; +} diff --git a/libslirp/fuzzing/helper.c b/libslirp/fuzzing/helper.c new file mode 100644 index 000000000..b8340f282 --- /dev/null +++ b/libslirp/fuzzing/helper.c @@ -0,0 +1,279 @@ +#include "helper.h" +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "slirp_base_fuzz.h" + +#define MIN_NUMBER_OF_RUNS 1 +#define EXIT_TEST_SKIP 77 + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); +struct in6_addr ip6_host; +struct in6_addr ip6_dns; + +/// Function to compute the checksum of the ip header, should be compatible with +/// TCP and UDP checksum calculation too. +uint16_t compute_checksum(uint8_t *Data, size_t Size) +{ + uint32_t sum = 0; + uint16_t *Data_as_u16 = (uint16_t *)Data; + + for (size_t i = 0; i < Size / 2; i++) { + uint16_t val = ntohs(*(Data_as_u16 + i)); + sum += val; + } + if (Size % 2 == 1) + sum += Data[Size - 1] << 8; + + uint16_t carry = sum >> 16; + uint32_t sum_val = carry + (sum & 0xFFFF); + uint16_t result = (sum_val >> 16) + (sum_val & 0xFFFF); + return ~result; +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + /* FIXME: fail on some addr? */ + return 0; +} + +int listen(int sockfd, int backlog) +{ + return 0; +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + /* FIXME: fail on some addr? */ + return 0; +} + +ssize_t send(int sockfd, const void *buf, size_t len, int flags) +{ + /* FIXME: partial send? */ + return len; +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + /* FIXME: partial send? */ + return len; +} + +ssize_t recv(int sockfd, void *buf, size_t len, int flags) +{ + memset(buf, 0, len); + return len / 2; +} + +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + memset(buf, 0, len); + memset(src_addr, 0, *addrlen); + return len / 2; +} + +int setsockopt(int sockfd, int level, int optname, const void *optval, + socklen_t optlen) +{ + return 0; +} + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +static void empty_logging_func(const gchar *log_domain, + GLogLevelFlags log_level, const gchar *message, + gpointer user_data) +{ +} +#endif + +/* Disables logging for oss-fuzz. Must be used with each target. */ +static void fuzz_set_logging_func(void) +{ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + g_log_set_default_handler(empty_logging_func, NULL); +#endif +} + +static ssize_t send_packet(const void *pkt, size_t pkt_len, void *opaque) +{ + return pkt_len; +} + +static int64_t clock_get_ns(void *opaque) +{ + return 0; +} + +static void *timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) +{ + return NULL; +} + +static void timer_mod(void *timer, int64_t expire_timer, void *opaque) +{ +} + +static void timer_free(void *timer, void *opaque) +{ +} + +static void guest_error(const char *msg, void *opaque) +{ +} + +static void register_poll_socket(slirp_os_socket fd, void *opaque) +{ +} + +static void unregister_poll_socket(slirp_os_socket fd, void *opaque) +{ +} + +static void notify(void *opaque) +{ +} + +static const SlirpCb slirp_cb = { + .send_packet = send_packet, + .guest_error = guest_error, + .clock_get_ns = clock_get_ns, + .timer_new = timer_new, + .timer_mod = timer_mod, + .timer_free = timer_free, + .register_poll_socket = register_poll_socket, + .unregister_poll_socket = unregister_poll_socket, + .notify = notify, +}; + +#define MAX_EVID 1024 +static int fake_events[MAX_EVID]; + +static int add_poll_cb(slirp_os_socket fd, int events, void *opaque) +{ + g_assert(fd < G_N_ELEMENTS(fake_events)); + fake_events[fd] = events; + return fd; +} + +static int get_revents_cb(int idx, void *opaque) +{ + return fake_events[idx] & ~(SLIRP_POLL_ERR | SLIRP_POLL_HUP); +} + +// Fuzzing strategy is the following : +// LLVMFuzzerTestOneInput : +// - build a slirp instance, +// - extract the packets from the pcap one by one, +// - send the data to `slirp_input` +// - call `slirp_pollfds_fill` and `slirp_pollfds_poll` to advance slirp +// - cleanup slirp when the whole pcap has been unwrapped. +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + Slirp *slirp = NULL; + struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ + struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */ + struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */ + struct in_addr fwd = { .s_addr = htonl(0x0a000205) }; /* 10.0.2.5 */ + struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */ + struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */ + struct in6_addr ip6_prefix; + int ret, vprefix6_len; + const pcap_hdr_t *hdr = (const void *)data; + const pcaprec_hdr_t *rec = NULL; + uint32_t timeout = 0; + + if (size < sizeof(pcap_hdr_t)) { + return 0; + } + data += sizeof(*hdr); + size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + setenv("SLIRP_FUZZING", "1", 0); + + fuzz_set_logging_func(); + + ret = inet_pton(AF_INET6, "fec0::", &ip6_prefix); + vprefix6_len = 64; + g_assert_cmpint(ret, ==, 1); + + ip6_host = ip6_prefix; + ip6_host.s6_addr[15] |= 2; + ip6_dns = ip6_prefix; + ip6_dns.s6_addr[15] |= 3; + + SlirpConfig cfg = { + .version = 6, + .restricted = false, + .in_enabled = true, + .vnetwork = net, + .vnetmask = mask, + .vhost = host, + .in6_enabled = true, + .vprefix_addr6 = ip6_prefix, + .vprefix_len = vprefix6_len, + .vhost6 = ip6_host, + .tftp_path = "fuzzing/tftp", + .vdhcp_start = dhcp, + .vnameserver = dns, + .vnameserver6 = ip6_dns, + }; + slirp = slirp_new(&cfg, &slirp_cb, NULL); + + slirp_add_exec(slirp, "cat", &fwd, 1234); + + + for ( ; size > sizeof(*rec); data += rec->incl_len, size -= rec->incl_len) { + rec = (const void *)data; + data += sizeof(*rec); + size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + g_debug("unsupported rec->incl_len != rec->orig_len"); + break; + } + if (rec->incl_len > size) { + break; + } + + if (rec->incl_len >= 14) { + if (data[12] == 0x08 && data[13] == 0x00) { + /* IPv4 */ + if (rec->incl_len >= 14 + 16) { + uint32_t ipsource = * (uint32_t*) (data + 14 + 12); + + // This an answer, which we will produce, so don't receive + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + } else if (data[12] == 0x86 && data[13] == 0xdd) { + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (data + 14 + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + } + } + + slirp_input(slirp, data, rec->incl_len); + slirp_pollfds_fill_socket(slirp, &timeout, add_poll_cb, NULL); + slirp_pollfds_poll(slirp, 0, get_revents_cb, NULL); + } + + slirp_cleanup(slirp); + + return 0; +} diff --git a/libslirp/fuzzing/helper.h b/libslirp/fuzzing/helper.h new file mode 100644 index 000000000..92b5f620e --- /dev/null +++ b/libslirp/fuzzing/helper.h @@ -0,0 +1,24 @@ +#ifndef _HELPER_H +#define _HELPER_H + +#ifdef _WIN32 +/* as defined in sdkddkver.h */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* Vista */ +#endif +#include +#endif + +#include +#include +#include + +#define PSEUDO_IP_SIZE (4*2 + 4) +#define PSEUDO_IPV6_SIZE (16*2 + 4) + +uint16_t compute_checksum(uint8_t *Data, size_t Size); + +extern struct in6_addr ip6_host; +extern struct in6_addr ip6_dns; + +#endif /* _HELPER_H */ diff --git a/libslirp/fuzzing/meson.build b/libslirp/fuzzing/meson.build new file mode 100644 index 000000000..c20179516 --- /dev/null +++ b/libslirp/fuzzing/meson.build @@ -0,0 +1,64 @@ +extra_sources = [] +extra_cargs = [] +extra_ldargs = [] +fuzzing_engine = [] + + +extra_cargs += '-g' +if fuzzer_build + extra_cargs += '-fsanitize=fuzzer,address' + extra_cargs += '-fsanitize-coverage=edge,indirect-calls,trace-cmp' + extra_cargs += '-DCUSTOM_MUTATOR' + extra_cargs += '-fprofile-instr-generate' + extra_cargs += '-fcoverage-mapping' + + extra_ldargs += '-fsanitize=fuzzer,address' + extra_ldargs += '-fprofile-instr-generate' + extra_ldargs += '-fcoverage-mapping' +endif + +deps = [glib_dep, libslirp_dep, platform_deps] + +exes = [ + ['fuzz-arp', ['slirp_fuzz_arp.c', 'helper.c']], + ['fuzz-ip-header', ['slirp_fuzz_ip_header.c', 'helper.c']], + ['fuzz-udp', ['slirp_fuzz_udp.c', 'helper.c']], + ['fuzz-udp-h', ['slirp_fuzz_udp_header.c', 'helper.c']], + ['fuzz-udp-d', ['slirp_fuzz_udp_data.c', 'helper.c']], + ['fuzz-tftp', ['slirp_fuzz_udp_data.c', 'helper.c']], + ['fuzz-dhcp', ['slirp_fuzz_udp_data.c', 'helper.c']], + ['fuzz-tcp', ['slirp_fuzz_tcp.c', 'helper.c']], + ['fuzz-tcp-h', ['slirp_fuzz_tcp_header.c', 'helper.c']], + ['fuzz-tcp-d', ['slirp_fuzz_tcp_data.c', 'helper.c']], + ['fuzz-icmp', ['slirp_fuzz_icmp.c', 'helper.c']], + + ['fuzz-ndp', ['slirp_fuzz_icmp6.c', 'helper.c']], + ['fuzz-ip6-header', ['slirp_fuzz_ip6_header.c', 'helper.c']], + ['fuzz-udp6', ['slirp_fuzz_udp6.c', 'helper.c']], + ['fuzz-udp6-h', ['slirp_fuzz_udp6_header.c', 'helper.c']], + ['fuzz-udp6-d', ['slirp_fuzz_udp6_data.c', 'helper.c']], + ['fuzz-tftp6', ['slirp_fuzz_udp6_data.c', 'helper.c']], + ['fuzz-tcp6', ['slirp_fuzz_tcp6.c', 'helper.c']], + ['fuzz-tcp6-h', ['slirp_fuzz_tcp6_header.c', 'helper.c']], + ['fuzz-tcp6-d', ['slirp_fuzz_tcp6_data.c', 'helper.c']], + ['fuzz-icmp6', ['slirp_fuzz_icmp6.c', 'helper.c']], + ] + +if fuzzer_build + foreach exe : exes + executable( + exe[0], exe[1], + dependencies : deps, + c_args: extra_cargs, + link_args: extra_ldargs, + ) + endforeach +endif + +if fuzz_reproduce + executable(['reproducer', ['reproducer.c', 'helper.c']], + dependencies: deps, + c_args: extra_cargs, + link_args: extra_ldargs, + ) +endif diff --git a/libslirp/fuzzing/oss-fuzz.sh b/libslirp/fuzzing/oss-fuzz.sh new file mode 100644 index 000000000..0561bdba1 --- /dev/null +++ b/libslirp/fuzzing/oss-fuzz.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -ex + +export CC=${CC:-clang} +export CXX=${CXX:-clang++} +export WORK=${WORK:-$(pwd)} +export OUT=${OUT:-$(pwd)/out} + +build=$WORK/build +rm -rf $build +mkdir -p $build +mkdir -p $OUT + +fuzzflag="oss-fuzz=true" +if [ -z "$FUZZING_ENGINE" ]; then + fuzzflag="llvm-fuzz=true" +fi + +meson $build \ + -D$fuzzflag \ + -Db_lundef=false \ + -Ddefault_library=static \ + -Dstatic=true \ + -Dbuildtype=debugoptimized + +ninja -C $build + +zip -jqr $OUT/fuzz-arp_seed_corpus.zip "$(dirname "$0")/IN_arp" +zip -jqr $OUT/fuzz-ip-header_seed_corpus.zip "$(dirname "$0")/IN_ip-header" +zip -jqr $OUT/fuzz-udp_seed_corpus.zip "$(dirname "$0")/IN_udp" +zip -jqr $OUT/fuzz-udp-h_seed_corpus.zip "$(dirname "$0")/IN_udp-h" +zip -jqr $OUT/fuzz-tftp_seed_corpus.zip "$(dirname "$0")/IN_tftp" +zip -jqr $OUT/fuzz-dhcp_seed_corpus.zip "$(dirname "$0")/IN_dhcp" +zip -jqr $OUT/fuzz-icmp_seed_corpus.zip "$(dirname "$0")/IN_icmp" +zip -jqr $OUT/fuzz-tcp_seed_corpus.zip "$(dirname "$0")/IN_tcp" +zip -jqr $OUT/fuzz-tcp-h_seed_corpus.zip "$(dirname "$0")/IN_tcp-h" + +zip -jqr $OUT/fuzz-ndp_seed_corpus.zip "$(dirname "$0")/IN_ndp" +zip -jqr $OUT/fuzz-ip6-header_seed_corpus.zip "$(dirname "$0")/IN_ip6-header" +zip -jqr $OUT/fuzz-udp6_seed_corpus.zip "$(dirname "$0")/IN_udp6" +zip -jqr $OUT/fuzz-udp6-h_seed_corpus.zip "$(dirname "$0")/IN_udp6-h" +zip -jqr $OUT/fuzz-tftp6_seed_corpus.zip "$(dirname "$0")/IN_tftp6" +zip -jqr $OUT/fuzz-icmp6_seed_corpus.zip "$(dirname "$0")/IN_icmp6" +zip -jqr $OUT/fuzz-tcp6_seed_corpus.zip "$(dirname "$0")/IN_tcp6" + +find $build -type f -executable -name "fuzz-*" -exec mv {} $OUT \; +find $build -type f -name "*.options" -exec mv {} $OUT \; diff --git a/libslirp/fuzzing/reproducer.c b/libslirp/fuzzing/reproducer.c new file mode 100644 index 000000000..49704af7a --- /dev/null +++ b/libslirp/fuzzing/reproducer.c @@ -0,0 +1,45 @@ +#ifdef _WIN32 +/* as defined in sdkddkver.h */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* Vista */ +#endif +#include +#endif + +#include +#include +#include "../src/libslirp.h" +#include "helper.h" + +#define MIN_NUMBER_OF_RUNS 1 +#define EXIT_TEST_SKIP 77 + +extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int main(int argc, char **argv) +{ + int i, j; + + for (i = 1; i < argc; i++) { + GError *err = NULL; + char *name = argv[i]; + char *buf; + size_t size; + + if (!g_file_get_contents(name, &buf, &size, &err)) { + g_warning("Failed to read '%s': %s", name, err->message); + g_clear_error(&err); + return EXIT_FAILURE; + } + + g_print("%s...\n", name); + for (j = 0; j < MIN_NUMBER_OF_RUNS; j++) { + if (LLVMFuzzerTestOneInput((void *)buf, size) == EXIT_TEST_SKIP) { + return EXIT_TEST_SKIP; + } + } + g_free(buf); + } + + return EXIT_SUCCESS; +} diff --git a/libslirp/fuzzing/slirp_base_fuzz.h b/libslirp/fuzzing/slirp_base_fuzz.h new file mode 100644 index 000000000..05b32d66f --- /dev/null +++ b/libslirp/fuzzing/slirp_base_fuzz.h @@ -0,0 +1,23 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" + +/* Structure for the fuzzers */ +typedef struct pcap_hdr_s { + guint32 magic_number; /* magic number */ + guint16 version_major; /* major version number */ + guint16 version_minor; /* minor version number */ + gint32 thiszone; /* GMT to local correction */ + guint32 sigfigs; /* accuracy of timestamps */ + guint32 snaplen; /* max length of captured packets, in octets */ + guint32 network; /* data link type */ +} pcap_hdr_t; + +typedef struct pcaprec_hdr_s { + guint32 ts_sec; /* timestamp seconds */ + guint32 ts_usec; /* timestamp microseconds */ + guint32 incl_len; /* number of octets of packet saved in file */ + guint32 orig_len; /* actual length of packet */ +} pcaprec_hdr_t; \ No newline at end of file diff --git a/libslirp/fuzzing/slirp_fuzz_arp.c b/libslirp/fuzzing/slirp_fuzz_arp.c new file mode 100644 index 000000000..c403e164f --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_arp.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *arp_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + arp_data = Data_ptr + 14; + + uint8_t Data_to_mutate[MaxSize]; + uint16_t arp_size = rec->incl_len - 14; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the ip header, maybe the IPs or + // total length should be excluded ? + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, arp_data, arp_size); + + // Call to libfuzzer's mutation function. + // For now we dont want to change the header size as it would require to + // resize the `Data` array to include the new bytes inside the whole + // packet. + // This should be easy as LibFuzzer probably does it by itself or + // reserved enough space in Data beforehand, needs some research to + // confirm. + // FIXME: allow up to grow header size to 60 bytes, + // requires to update the `header length` before calculating + // checksum + LLVMFuzzerMutate(Data_to_mutate, arp_size, arp_size); + + // Copy the mutated data back to the `Data` array + memcpy(arp_data, Data_to_mutate, arp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_icmp.c b/libslirp/fuzzing/slirp_fuzz_icmp.c new file mode 100644 index 000000000..8a422c4be --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_icmp.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not ICMP from the mutation strategy + if (ip_data[9] != IPPROTO_ICMP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_icmp = ip_data + ip_hl_in_bytes; + uint16_t total_length = + ntohs(*((uint16_t *)ip_data + 1)); // network order to host order + uint16_t icmp_size = + (total_length - ip_hl_in_bytes); /* total length -> is stored at the + offset 2 in the header */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (icmp_size > MaxSize || icmp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in icmp + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_icmp, icmp_size); + + // Call to libfuzzer's mutation function. + // For now we dont want to change the header size as it would require to + // resize the `Data` array to include the new bytes inside the whole + // packet. + // This should be easy as LibFuzzer probably does it by itself or + // reserved enough space in Data beforehand, needs some research to + // confirm. + // FIXME: allow up to grow header size to 60 bytes, + // requires to update the `header length` before calculating + // checksum + LLVMFuzzerMutate(Data_to_mutate, icmp_size, icmp_size); + + // Set the `checksum` field to 0 and calculate the new checksum + *(uint16_t *)(Data_to_mutate + 2) = 0; + uint16_t new_checksum = + compute_checksum(Data_to_mutate, icmp_size); + *(uint16_t *)(Data_to_mutate + 2) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_icmp, Data_to_mutate, icmp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_icmp6.c b/libslirp/fuzzing/slirp_fuzz_icmp6.c new file mode 100644 index 000000000..bbe041c9f --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_icmp6.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not ICMP from the mutation strategy + if (ip_data[6] != IPPROTO_ICMPV6) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_icmp = ip_data + ip_hl_in_bytes; + uint16_t icmp_size = ntohs(*(uint16_t *)(ip_data + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (icmp_size > MaxSize || icmp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in icmp + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE); + memcpy(Data_to_mutate, start_of_icmp, icmp_size); + + // Call to libfuzzer's mutation function. + // For now we dont want to change the header size as it would require to + // resize the `Data` array to include the new bytes inside the whole + // packet. + // This should be easy as LibFuzzer probably does it by itself or + // reserved enough space in Data beforehand, needs some research to + // confirm. + // FIXME: allow up to grow header size to 60 bytes, + // requires to update the `header length` before calculating + // checksum + LLVMFuzzerMutate(Data_to_mutate, icmp_size, icmp_size); + + // Set the `checksum` field to 0 and calculate the new checksum + *(uint16_t *)(Data_to_mutate + 2) = 0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + icmp_size, ip_data + 8, 16*2); + + *(Data_to_mutate + icmp_size + 16*2 + 1) = IPPROTO_ICMPV6; + + *(Data_to_mutate + icmp_size + 16*2 + 2) = (uint8_t)(icmp_size / 256); + *(Data_to_mutate + icmp_size + 16*2 + 3) = (uint8_t)(icmp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, icmp_size + PSEUDO_IPV6_SIZE); + *(uint16_t *)(Data_to_mutate + 2) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_icmp, Data_to_mutate, icmp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_ip6_header.c b/libslirp/fuzzing/slirp_fuzz_ip6_header.c new file mode 100644 index 000000000..4714f92db --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_ip6_header.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the ip header, maybe the IPs or + // total length should be excluded ? + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, ip_data, ip_hl_in_bytes); + + // Call to libfuzzer's mutation function. + // For now we dont want to change the header size as it would require to + // resize the `Data` array to include the new bytes inside the whole + // packet. + // This should be easy as LibFuzzer probably does it by itself or + // reserved enough space in Data beforehand, needs some research to + // confirm. + // FIXME: allow up to grow header size to 60 bytes, + // requires to update the `header length` before calculating + // checksum + LLVMFuzzerMutate(Data_to_mutate, ip_hl_in_bytes, ip_hl_in_bytes); + + // Copy the mutated data back to the `Data` array + memcpy(ip_data, Data_to_mutate, ip_hl_in_bytes); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_ip_header.c b/libslirp/fuzzing/slirp_fuzz_ip_header.c new file mode 100644 index 000000000..fe07540e8 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_ip_header.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > MaxSize || ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the ip header, maybe the IPs or + // total length should be excluded ? + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, ip_data, ip_hl_in_bytes); + + // Call to libfuzzer's mutation function. + // For now we dont want to change the header size as it would require to + // resize the `Data` array to include the new bytes inside the whole + // packet. + // This should be easy as LibFuzzer probably does it by itself or + // reserved enough space in Data beforehand, needs some research to + // confirm. + // FIXME: allow up to grow header size to 60 bytes, + // requires to update the `header length` before calculating + // checksum + LLVMFuzzerMutate(Data_to_mutate, ip_hl_in_bytes, ip_hl_in_bytes); + + // Set the `checksum` field to 0 and calculate the new checksum + *(uint16_t *)(Data_to_mutate + 10) = 0; + uint16_t new_checksum = + compute_checksum(Data_to_mutate, ip_hl_in_bytes); + *(uint16_t *)(Data_to_mutate + 10) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(ip_data, Data_to_mutate, ip_hl_in_bytes); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp.c b/libslirp/fuzzing/slirp_fuzz_tcp.c new file mode 100644 index 000000000..461d43042 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[9] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1)); + uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, tcp_size, tcp_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2); + + *(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp6.c b/libslirp/fuzzing/slirp_fuzz_tcp6.c new file mode 100644 index 000000000..64ad9032e --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp6.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[6] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, tcp_size, tcp_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2); + + *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp6_data.c b/libslirp/fuzzing/slirp_fuzz_tcp6_data.c new file mode 100644 index 000000000..87982b0c8 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp6_data.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[6] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint8_t tcp_header_size = 4 * (*(start_of_tcp + 12) >> 4); + uint8_t *start_of_data = ip_data + ip_hl_in_bytes + tcp_header_size; + uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4)); + uint16_t tcp_data_size = (tcp_size - (uint16_t)tcp_header_size); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_data_size > MaxSize || tcp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - tcp_header_size) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate + tcp_header_size, tcp_data_size, tcp_data_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2); + + *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp6_header.c b/libslirp/fuzzing/slirp_fuzz_tcp6_header.c new file mode 100644 index 000000000..e6c8b774e --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp6_header.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[6] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IPV6_SIZE]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint8_t tcp_header_size = (*(start_of_tcp + 12) >> 4) * 4; + uint16_t tcp_size = ntohs(*(uint16_t *)(ip_data + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes || + tcp_header_size > MaxSize || tcp_header_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IPV6_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, tcp_header_size, tcp_header_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 8, 16*2); + + *(Data_to_mutate + tcp_size + 16*2 + 1) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 16*2 + 2) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 16*2 + 3) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IPV6_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp_data.c b/libslirp/fuzzing/slirp_fuzz_tcp_data.c new file mode 100644 index 000000000..05971c091 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp_data.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[9] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint8_t tcp_header_size = 4 * (*(start_of_tcp + 12) >> 4); + uint8_t *start_of_data = ip_data + ip_hl_in_bytes + tcp_header_size; + uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1)); + uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes); + uint16_t tcp_data_size = (tcp_size - (uint16_t)tcp_header_size); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_data_size > MaxSize || tcp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - tcp_header_size) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate + tcp_header_size, tcp_data_size, tcp_data_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2); + + *(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_tcp_header.c b/libslirp/fuzzing/slirp_fuzz_tcp_header.c new file mode 100644 index 000000000..96904ab23 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_tcp_header.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not TCP from the mutation strategy + if (ip_data[9] != IPPROTO_TCP) + continue; + + // Allocate a bit more than needed, this is useful for + // checksum calculation. + uint8_t Data_to_mutate[MaxSize + PSEUDO_IP_SIZE]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_tcp = ip_data + ip_hl_in_bytes; + uint8_t tcp_header_size = (*(start_of_tcp + 12) >> 4) * 4; + uint16_t total_length = ntohs(*((uint16_t *)ip_data + 1)); + uint16_t tcp_size = (total_length - (uint16_t)ip_hl_in_bytes); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use tcp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (tcp_size > MaxSize || tcp_size > rec->incl_len - 14 - ip_hl_in_bytes || + tcp_header_size > MaxSize || tcp_header_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the tcp packet + memset(Data_to_mutate, 0, MaxSize + PSEUDO_IP_SIZE); + memcpy(Data_to_mutate, start_of_tcp, tcp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole TCP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the TCP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, tcp_header_size, tcp_header_size); + + // Set the `checksum` field to 0 to calculate the new checksum + + *(uint16_t *)(Data_to_mutate + 16) = (uint16_t)0; + // Copy the source and destination IP addresses, the tcp length and + // protocol number at the end of the `Data_to_mutate` array to calculate + // the new checksum. + memcpy(Data_to_mutate + tcp_size, ip_data + 12, 4*2); + + *(Data_to_mutate + tcp_size + 9) = IPPROTO_TCP; + + *(Data_to_mutate + tcp_size + 10) = (uint8_t)(tcp_size / 256); + *(Data_to_mutate + tcp_size + 11) = (uint8_t)(tcp_size % 256); + + uint16_t new_checksum = + compute_checksum(Data_to_mutate, tcp_size + PSEUDO_IP_SIZE); + *(uint16_t *)(Data_to_mutate + 16) = htons(new_checksum); + + // Copy the mutated data back to the `Data` array + memcpy(start_of_tcp, Data_to_mutate, tcp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp.c b/libslirp/fuzzing/slirp_fuzz_udp.c new file mode 100644 index 000000000..272258e46 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[9] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, udp_size, udp_size); + + // Drop checksum + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp6.c b/libslirp/fuzzing/slirp_fuzz_udp6.c new file mode 100644 index 000000000..4b00de759 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp6.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[6] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, udp_size, udp_size); + + // Drop checksum + // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't + // check that actually + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp6_data.c b/libslirp/fuzzing/slirp_fuzz_udp6_data.c new file mode 100644 index 000000000..830685574 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp6_data.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[6] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint8_t udp_header_size = 8; + uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4)); + uint16_t udp_data_size = (udp_size - (uint16_t)udp_header_size); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_data_size > MaxSize || udp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - udp_header_size) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate + udp_header_size, udp_data_size, udp_data_size); + + // Drop checksum + // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't + // check that actually + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp6_header.c b/libslirp/fuzzing/slirp_fuzz_udp6_header.c new file mode 100644 index 000000000..a2734b46f --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp6_header.c @@ -0,0 +1,121 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "../src/ip6.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 24) { + struct in6_addr *ipsource = (struct in6_addr *) (ip_data + 8); + + // This an answer, which we will produce, so don't receive + if (in6_equal(ipsource, &ip6_host) || in6_equal(ipsource, &ip6_dns)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[6] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl_in_bytes = sizeof(struct ip6); /* ip header length */ + + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint8_t udp_header_size = 8; + uint16_t udp_size = ntohs(*(uint16_t *)(ip_data + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, udp_header_size, udp_header_size); + + // Drop checksum + // Stricto sensu, UDPv6 makes checksums mandatory, but libslirp doesn't + // check that actually + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp_data.c b/libslirp/fuzzing/slirp_fuzz_udp_data.c new file mode 100644 index 000000000..bc8930c9c --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp_data.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[9] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint8_t udp_header_size = 8; + uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4)); + uint16_t udp_data_size = (udp_size - (uint16_t)udp_header_size); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_data_size > MaxSize || udp_data_size > rec->incl_len - 14 - ip_hl_in_bytes - udp_header_size) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate + udp_header_size, udp_data_size, udp_data_size); + + // Drop checksum + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/slirp_fuzz_udp_header.c b/libslirp/fuzzing/slirp_fuzz_udp_header.c new file mode 100644 index 000000000..b2dc27296 --- /dev/null +++ b/libslirp/fuzzing/slirp_fuzz_udp_header.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include "../src/libslirp.h" +#include "helper.h" +#include "slirp_base_fuzz.h" + +#ifdef CUSTOM_MUTATOR +extern size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize); +size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize, unsigned int Seed); + +/// This is a custom mutator, this allows us to mutate only specific parts of +/// the input and fix the checksum so the packet isn't rejected for bad reasons. +extern size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, + size_t MaxSize, unsigned int Seed) +{ + size_t current_size = Size; + uint8_t *Data_ptr = Data; + uint8_t *ip_data; + uint32_t ipsource; + bool mutated = false; + + pcap_hdr_t *hdr = (void *)Data_ptr; + pcaprec_hdr_t *rec = NULL; + + if (current_size < sizeof(pcap_hdr_t)) { + return 0; + } + + Data_ptr += sizeof(*hdr); + current_size -= sizeof(*hdr); + + if (hdr->magic_number == 0xd4c3b2a1) { + g_debug("FIXME: byteswap fields"); + return 0; + } /* else assume native pcap file */ + if (hdr->network != 1) { + return 0; + } + + for ( ; current_size > sizeof(*rec); Data_ptr += rec->incl_len, current_size -= rec->incl_len) { + rec = (void *)Data_ptr; + Data_ptr += sizeof(*rec); + current_size -= sizeof(*rec); + + if (rec->incl_len != rec->orig_len) { + return 0; + } + if (rec->incl_len > current_size) { + return 0; + } + if (rec->incl_len < 14 + 1) { + return 0; + } + + ip_data = Data_ptr + 14; + + if (rec->incl_len >= 14 + 16) { + ipsource = * (uint32_t*) (ip_data + 12); + + // This an answer, which we will produce, so don't mutate + if (ipsource == htonl(0x0a000202) || ipsource == htonl(0x0a000203)) + continue; + } + + // Exclude packets that are not UDP from the mutation strategy + if (ip_data[9] != IPPROTO_UDP) + continue; + + uint8_t Data_to_mutate[MaxSize]; + uint8_t ip_hl = (ip_data[0] & 0xF); + uint8_t ip_hl_in_bytes = ip_hl * 4; /* ip header length */ + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use ip_hl_in_bytes inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (ip_hl_in_bytes > rec->incl_len - 14) + return 0; + + uint8_t *start_of_udp = ip_data + ip_hl_in_bytes; + uint8_t udp_header_size = 8; + uint16_t udp_size = ntohs(*(uint16_t *)(start_of_udp + 4)); + + // The size inside the packet can't be trusted, if it is too big it can + // lead to heap overflows in the fuzzing code. + // Fixme : don't use udp_size inside the fuzzing code, maybe use the + // rec->incl_len and manually calculate the size. + if (udp_size > MaxSize || udp_size > rec->incl_len - 14 - ip_hl_in_bytes) + return 0; + + // Copy interesting data to the `Data_to_mutate` array + // here we want to fuzz everything in the udp packet + memset(Data_to_mutate, 0, MaxSize); + memcpy(Data_to_mutate, start_of_udp, udp_size); + + // Call to libfuzzer's mutation function. + // Pass the whole UDP packet, mutate it and then fix checksum value + // so the packet isn't rejected. + // The new size of the data is returned by LLVMFuzzerMutate. + // Fixme: allow to change the size of the UDP packet, this will require + // to fix the size before calculating the new checksum and change + // how the Data_ptr is advanced. + // Most offsets bellow should be good for when the switch will be + // done to avoid overwriting new/mutated data. + LLVMFuzzerMutate(Data_to_mutate, udp_header_size, udp_header_size); + + // Drop checksum + *(uint16_t *)(Data_to_mutate + 6) = 0; + + // Copy the mutated data back to the `Data` array + memcpy(start_of_udp, Data_to_mutate, udp_size); + + mutated = true; + } + + if (!mutated) + return 0; + + return Size; +} +#endif // CUSTOM_MUTATOR diff --git a/libslirp/fuzzing/tftp/toto b/libslirp/fuzzing/tftp/toto new file mode 100644 index 000000000..0b70c57e3 Binary files /dev/null and b/libslirp/fuzzing/tftp/toto differ diff --git a/libslirp/meson.build b/libslirp/meson.build new file mode 100644 index 000000000..8173f19cb --- /dev/null +++ b/libslirp/meson.build @@ -0,0 +1,257 @@ +project('libslirp', 'c', + version : '4.8.0', + license : 'BSD-3-Clause', + default_options : ['warning_level=1', 'c_std=gnu99'], + meson_version : '>= 0.50', +) + +version = meson.project_version() +varr = version.split('.') +major_version = varr[0] +minor_version = varr[1] +micro_version = varr[2] + +conf = configuration_data() +conf.set('SLIRP_MAJOR_VERSION', major_version) +conf.set('SLIRP_MINOR_VERSION', minor_version) +conf.set('SLIRP_MICRO_VERSION', micro_version) + +want_ossfuzz = get_option('oss-fuzz') +want_libfuzzer = get_option('llvm-fuzz') +fuzz_reproduce = get_option('fuzz-reproduce') +target_winver = get_option('target_winver') +if want_ossfuzz and want_libfuzzer + error('only one of oss-fuzz and llvm-fuzz can be specified') +endif +fuzzer_build = want_ossfuzz or want_libfuzzer +if fuzzer_build and fuzz_reproduce + error('fuzzer build and reproducer build are mutually exclusive') +endif + +cc = meson.get_compiler('c') +add_languages('cpp', required : fuzzer_build) + +if get_option('static') == true + add_global_arguments('-static', language : 'c') +endif + +if cc.get_argument_syntax() != 'msvc' + r = run_command('build-aux/git-version-gen', + '@0@/.tarball-version'.format(meson.current_source_dir()), + check : false) + + full_version = r.stdout().strip() + if r.returncode() != 0 or full_version.startswith('UNKNOWN') + full_version = meson.project_version() + elif not full_version.startswith(meson.project_version()) + error('meson.build project version @0@ does not match git-describe output @1@' + .format(meson.project_version(), full_version)) + endif +else + full_version = meson.project_version() +endif +conf.set_quoted('SLIRP_VERSION_STRING', full_version + get_option('version_suffix')) + +# libtool versioning - this applies to libslirp +# +# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details +# +# - If interfaces have been changed or added, but binary compatibility +# has been preserved, change: +# CURRENT += 1 +# REVISION = 0 +# AGE += 1 +# - If binary compatibility has been broken (eg removed or changed +# interfaces), change: +# CURRENT += 1 +# REVISION = 0 +# AGE = 0 +# - If the interface is the same as the previous version, but bugs are +# fixed, change: +# REVISION += 1 +lt_current = 4 +lt_revision = 0 +lt_age = 4 +lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision) + +host_system = host_machine.system() + +glib_static = get_option('static') + +if host_system == 'windows' + # Windows: Build a static libslirp library, but allow glib-2.0 to link + # as a DLL. pkg-config fails us here because the static version of glib-2.0 + # requires iconv, which is not emitted by 'pkg-config --libs --static'. + glib_static = false +endif + +glib_dep = dependency('glib-2.0', static : glib_static) + +add_project_arguments(cc.get_supported_arguments('-Wmissing-prototypes', '-Wstrict-prototypes', + '-Wredundant-decls', '-Wundef', '-Wwrite-strings'), + language: 'c', native: false) + +platform_deps = [] + +cargs = [ + '-DG_LOG_DOMAIN="Slirp"', + '-DBUILDING_LIBSLIRP', +] + +if host_system == 'windows' + platform_deps += [ + cc.find_library('ws2_32'), + cc.find_library('iphlpapi') + ] + + if target_winver != '' + cargs += '-DTARGET_WINVER=@0@'.format(target_winver) + endif +elif host_system == 'darwin' + platform_deps += [ + cc.find_library('resolv') + ] +elif host_system == 'haiku' + platform_deps += [ + cc.find_library('network') + ] +endif + +if cc.check_header('valgrind/valgrind.h') + cargs += [ '-DHAVE_VALGRIND=1' ] +endif + +sources = [ + 'src/arp_table.c', + 'src/bootp.c', + 'src/cksum.c', + 'src/dhcpv6.c', + 'src/dnssearch.c', + 'src/if.c', + 'src/ip6_icmp.c', + 'src/ip6_input.c', + 'src/ip6_output.c', + 'src/ip_icmp.c', + 'src/ip_input.c', + 'src/ip_output.c', + 'src/mbuf.c', + 'src/misc.c', + 'src/ncsi.c', + 'src/ndp_table.c', + 'src/sbuf.c', + 'src/slirp.c', + 'src/socket.c', + 'src/state.c', + 'src/stream.c', + 'src/tcp_input.c', + 'src/tcp_output.c', + 'src/tcp_subr.c', + 'src/tcp_timer.c', + 'src/tftp.c', + 'src/udp.c', + 'src/udp6.c', + 'src/util.c', + 'src/version.c', + 'src/vmstate.c', +] + +mapfile = 'src/libslirp.map' +vflag = [] +vflag_test = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile) +if cc.has_link_argument('-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), 'src/libslirp.test.map')) + vflag += vflag_test +endif + +if fuzzer_build + cargs += '-fsanitize-coverage=edge,indirect-calls,trace-cmp' + cargs += '-fsanitize=fuzzer-no-link,address' + cargs += '-fprofile-instr-generate' + cargs += '-fcoverage-mapping' + cargs += '-g' + cargs += '-DSLIRP_DEBUG' + vflag += '-fsanitize=fuzzer-no-link,address' + vflag += '-fsanitize-coverage=edge,indirect-calls,trace-cmp' + vflag += '-fprofile-instr-generate' + vflag += '-fcoverage-mapping' +endif +if fuzz_reproduce + cargs += '-DSLIRP_DEBUG' + cargs += '-g' +endif + +install_devel = not meson.is_subproject() + +configure_file( + input : 'src/libslirp-version.h.in', + output : 'libslirp-version.h', + install : install_devel, + install_dir : join_paths(get_option('includedir'), 'slirp'), + configuration : conf +) + +if fuzzer_build or fuzz_reproduce + lib = static_library('slirp', sources, + c_args : cargs, + link_args : vflag, + link_depends : mapfile, + dependencies : [glib_dep, platform_deps], + ) +else + lib = library('slirp', sources, + version : lt_version, + c_args : cargs, + link_args : vflag, + link_depends : mapfile, + dependencies : [glib_dep, platform_deps], + install : install_devel or get_option('default_library') == 'shared', + ) +endif + +pingtest = executable('pingtest', 'test/pingtest.c', + link_with: [ lib ], + c_args : cargs, + link_args : vflag, + include_directories: [ 'src' ], + dependencies : [ platform_deps ] +) + +test('ping', pingtest) + +ncsitest = executable('ncsitest', 'test/ncsitest.c', + link_with: [lib], + c_args : cargs, + link_args : vflag, + include_directories: ['src'], + dependencies: [glib_dep, platform_deps] +) + +test('ncsi', ncsitest) + +if install_devel + install_headers(['src/libslirp.h'], subdir : 'slirp') + + pkg = import('pkgconfig') + + pkg.generate( + version : version, + libraries : lib, + requires : [ + 'glib-2.0', + ], + name : 'slirp', + description : 'User-space network stack', + filebase : 'slirp', + subdirs : 'slirp', + ) +else + if get_option('default_library') == 'both' + lib = lib.get_static_lib() + endif +endif + +libslirp_dep = declare_dependency( + link_with : lib, + include_directories : [include_directories('src'), include_directories('.')], +) + +subdir('fuzzing') diff --git a/libslirp/meson_options.txt b/libslirp/meson_options.txt new file mode 100644 index 000000000..5eafd38c7 --- /dev/null +++ b/libslirp/meson_options.txt @@ -0,0 +1,17 @@ +option('version_suffix', type: 'string', value: '', + description: 'Suffix to append to SLIRP_VERSION_STRING') + +option('oss-fuzz', type : 'boolean', value : 'false', + description : 'build against oss-fuzz') + +option('llvm-fuzz', type : 'boolean', value : 'false', + description : 'build against LLVM libFuzzer') + +option('fuzz-reproduce', type : 'boolean', value : 'false', + description : 'build a standalone executable to reproduce fuzz cases') + +option('static', type : 'boolean', value : 'false', + description : 'build static binary, only for debugging, otherwise rather use --default-library static') + +option('target_winver', type : 'string', value: '', + description : 'Target Windows version (default is Windows 7/0x0601)') diff --git a/libslirp/minimal/glib-endian.cmake b/libslirp/minimal/glib-endian.cmake new file mode 100644 index 000000000..cc87c3cb9 --- /dev/null +++ b/libslirp/minimal/glib-endian.cmake @@ -0,0 +1,15 @@ +/* Automagically generated endianness include. + * + * Also defines GLIB_SIZE_VOID_P using the CMake CMAKE_SIZEOF_VOID_P + * substitution. Probably not where this manifest constant really + * belongs, but there's not really a better place for it. */ + +#if !defined(_GLIB_ENDIAN_H) +#define _GLIB_ENDIAN_H + +#define G_BIG_ENDIAN 0x1234 +#define G_LITTLE_ENDIAN 0x4321 + +#define G_BYTE_ORDER @G_BYTE_ORDER@ +#define GLIB_SIZEOF_VOID_P @CMAKE_SIZEOF_VOID_P@ +#endif diff --git a/libslirp/minimal/glib-stubs.c b/libslirp/minimal/glib-stubs.c new file mode 100644 index 000000000..de339296a --- /dev/null +++ b/libslirp/minimal/glib-stubs.c @@ -0,0 +1,585 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* Minimalist glib support. + * + * This is the minimalist approach to providing glib support for libslirp. + * There are many reasons why you might want minimalism, one of which is + * the overhead of yet-another-dependency. */ + +#include +#include +#include + +#if defined(_WIN32) +/* #include */ +#include +#endif + +#if defined(G_OS_UNIX) +#include + +#if defined(HAVE_TIME_H) +#include +#endif + +#if defined(HAVE_SYS_TIME_H) +#include +#endif + +#endif + +/* getenv() wrapper: */ +const char *g_getenv(const char *envar) +{ +#if !defined(HAVE_DUPENV_S) + return getenv(envar); +#else + static char *env_store = NULL; + + /* Don't leak previous calls. */ + free(env_store); + _dupenv_s(&env_store, NULL, envar); + return env_store; +#endif +} + +/* Logging support: */ +static void output_stderr_noexit(const char *fmt, va_list args); +static void output_stderr_exit(const char *fmt, va_list args); + +GLibLoggingFlags glib_logging_level = G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|G_LOG_LEVEL_DEBUG; + +static GLibLogger basic_logger = { + .logging_enabled = 1, + .output_warning = output_stderr_noexit, + .output_debug = output_stderr_noexit, + .output_error = output_stderr_exit, + .output_critical = output_stderr_exit +}; + +GLibLogger *glib_logger = &basic_logger; + +/* Compatibility with glib logging: */ +void g_log_set_debug_enabled(int flag) +{ + glib_logger->logging_enabled = flag; +} + +void glib_set_logging_hooks(const GLibLogger *hooks) +{ + glib_logger->output_warning = hooks->output_warning; + glib_logger->output_debug = hooks->output_debug; + glib_logger->output_error = hooks->output_error; + glib_logger->output_critical = hooks->output_critical; +} + +static void output_stderr_noexit(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); +} + +static void output_stderr_exit(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); + + exit(1); + g_assert_not_reached(); +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* Random number generation: Uses the xoshiro128** PRNG. */ + +/* This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid + generators. It has excellent speed, a state size (128 bits) that is + large enough for mild parallelism, and it passes all tests we are aware + of. + + Note that version 1.0 had mistakenly s[0] instead of s[1] as state + word passed to the scrambler. + + For generating just single-precision (i.e., 32-bit) floating-point + numbers, xoshiro128+ is even faster. + + The state must be seeded so that it is not everywhere zero. */ + +/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ + +static inline uint32_t rotl(const uint32_t x, int k) +{ + return (x << k) | (x >> (32 - k)); +} + +uint32_t next(GRand *seed) +{ + const uint32_t result = rotl(seed->rand_state[1] * 5, 7) * 9; + + const uint32_t t = seed->rand_state[1] << 9; + + seed->rand_state[2] ^= seed->rand_state[0]; + seed->rand_state[3] ^= seed->rand_state[1]; + seed->rand_state[1] ^= seed->rand_state[2]; + seed->rand_state[0] ^= seed->rand_state[3]; + + seed->rand_state[2] ^= t; + + seed->rand_state[3] = rotl(seed->rand_state[3], 11); + + return result; +} + +GRand *g_rand_new() +{ + GRand *retval = calloc(1, sizeof(GRand)); + + if (NULL == retval) + return retval; + +#if !defined(_WIN32) +# if defined(HAVE_CLOCK_GETTIME) + struct timespec now; + + clock_gettime(CLOCK_REALTIME, &now); + + retval->rand_state[0] = now.tv_sec; + retval->rand_state[1] = now.tv_nsec; +# elif defined(HAVE_GETTIMEOFDAY) + struct timeval now; + + gettimeofday (&now, NULL); + + retval->rand_state[0] = now.tv_sec; + retval->rand_state[1] = now.tv_usec; +# endif + + retval->rand_state[2] = getpid (); +#else + static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime( &system_time ); + SystemTimeToFileTime( &system_time, &file_time ); + + time = ((uint64_t) file_time.dwLowDateTime ); + time += ((uint64_t) file_time.dwHighDateTime) << 32; + + retval->rand_state[0] = (uint32_t) ((time - EPOCH) / 10000000L); + retval->rand_state[1] = (uint32_t) (system_time.wMilliseconds * 1000); + retval->rand_state[2] = (uint32_t) GetCurrentProcessId(); +#endif + + retval->rand_state[3] = 0; + + return retval; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +void g_rand_free(GRand *rand) +{ + free(rand); +} + +uint32_t g_rand_int_range (GRand *rand, int begin, int end) +{ + return ((int) (next(rand) % (end - begin) + begin)); +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* String operations: */ + +static inline size_t strlen_log2(size_t size) +{ + size_t alloc; + + /* Closest power of 2 to the initial length. There are likely better ways + * to do this via Hacker's Delight. Feel free to optimize. Just don't optimize + * yet. */ + for (alloc = ((size_t) ~0) << 1; (size & alloc) != 0; alloc <<= 1) + /* empty*/; + return (~alloc + 1); +} + +GString *g_string_new(const char *initial) +{ + GString *retval = calloc(1, sizeof(GString)); + + if (NULL == retval) + return retval; + + if (NULL != initial) { + size_t init_len = strlen(initial); + + retval->allocated = strlen_log2(init_len + 1); + retval->len = init_len; + } else { + retval->allocated = 32; + retval->len = 0; + } + + retval->str = calloc(retval->allocated, sizeof(char)); + if (NULL != initial) { +#if !defined(HAVE_STRCPY_S) + strcpy(retval->str, initial); +#else + strcpy_s(retval->str, retval->allocated, initial); +#endif + } + + return retval; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +char *g_string_free(GString *gstr, int free_string) +{ + char *inner_str = NULL; + + if (NULL != gstr) { + inner_str = gstr->str; + + if (free_string) { + free(inner_str); + inner_str = NULL; + } + + free(gstr); + } + + return inner_str; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +void g_strfreev(char **strs) +{ + char **iter = strs; + + if (NULL == strs) + return; + + /* free content */ + while (*iter != NULL) { + free(*iter); + ++iter; + } + + /* Then free the vector. */ + free(strs); +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +int g_ascii_strcasecmp(const char *s1, const char *s2) +{ + while (*s1 && *s2 && isascii(*s1) && isascii(*s2)) { + if ((isalpha(*s1) && isalpha(*s2) && tolower(*s1) == tolower(*s2)) || *s1 == *s2) { + ++s1; + ++s2; + } else { + /* Branchless: booleans are 0/1 since C99, so if *s1 < *s2, return -1 (note the + * negation) or 1 if *s1 > *s2. */ + return -(*s1 < *s2) + (*s1 > *s2); + } + } + + /* Another branchless if/then: s1 shorter than s2, return -1, s2 shorter than s1, return 1, + * otherwise they were the same length and the entire expression is 0. */ + return (*s1 == '\0') * -1 + (*s2 == '\0') * 1; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* GError: */ +void g_error_free(GError *err) +{ + if (NULL != err) { + free(err->message); + free(err); + } +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* OpenBSD implementation: + * + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +g_strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* g_strerror(): */ +const char *g_strerror(int the_error) +{ +#if !defined(HAVE_STRERROR_S) + const char *errstr = strerror(the_error); +#else + static char errstr[256]; + + strerror_s(errstr, sizeof(errstr)/sizeof(errstr[0]), the_error); +#endif + + return errstr; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* Simple Rabin-Karp g_strstr_len implementation. */ + +/* Larger numbers usually result in fewer hash collisions. */ +static const int hash_base = 512; +static const int hash_prime = 241; + +/* Pre-seeded hash corrector values, up to 128 character search strings. N.B. that + * the correctors are dynamically generated instead of statically initialized. + * This allows hash_base and hash_prime to change without having to regenerate + * the initializers. */ +static int correctors[128] = { 0, }; +static int have_correctors = FALSE; +static const size_t n_correctors = sizeof(correctors) / sizeof(correctors[0]); + +static inline void seed_correctors() +{ + size_t i; + int hval = 1; + + for (i = 0; i < n_correctors; ++i) { + correctors[i] = hval; + hval = (hval * hash_base) % hash_prime; + } +} + +/* Would have preferred a tail-recursive hash function implementation here. + * Debug builds would potentially choke on long strings, so use iterative + * loop to construct the hash. */ +static inline int full_hash(const char *str, size_t len) +{ + int hash = 0; + size_t offs; + + for (offs = 0; offs < len; ++offs) { + hash = (hash * hash_base + str[offs]) % hash_prime; + } + + return hash; +} + +char *g_strstr_len(char *src, gssize src_len, const char *srch) +{ + size_t srch_len = strlen(srch); + size_t src_limit = (src_len >= 0) ? (size_t) src_len : strlen(src); + char *retval = NULL; + + if (srch_len == 0) + return src; + + if (src_limit > 0 && srch_len <= src_limit) { + int hash_correction = 1; + size_t i; + size_t offset = 0; + int srch_hash = full_hash(srch, srch_len); + int hval = full_hash(src, srch_len); + + /* Correction factor used when removing the leftmost character from the + * rolling hash value, hash_base^(srch_len-1) % hash_prime. */ + if (srch_len < n_correctors) { + if (!have_correctors) { + seed_correctors(); + have_correctors = TRUE; + } + + hash_correction = correctors[srch_len - 1]; + } else { + /* Brute force... but take advantage of the last pre-seeded corrector value. */ + hash_correction = correctors[n_correctors - 1]; + for (i = n_correctors; i < srch_len - 1; ++i) + hash_correction = (hash_correction * hash_base) % hash_prime; + } + + while (NULL == retval && offset + srch_len <= src_limit) { + if (hval == srch_hash) { + size_t j; + + for (i = offset, j = 0; j < srch_len && src[i] == srch[j]; ++i, ++j) + /* NOP */; + + if (j == srch_len) + retval = src + offset; + } + + /* If the strings don't match when hval == srch_hash, fall through into + * computing the rolling hash anyway. */ + hval = hash_base * (hval - src[offset] * hash_correction) + src[offset + srch_len]; + hval %= hash_prime; + /* Branchless adjustment: (hval < 0) == 0 or 1. */ + hval += (hval < 0) * hash_prime; + ++offset; + } + } + + return retval; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +/* g_parse_debug_string: */ + +static inline int arg_delim(char c) +{ + return (c == ' ' || c == ',' || c == ';' || c == ':' || c == '\t'); +} + +/* Note: Unrecognized debug tokens are sliently ignored. */ +uint32_t g_parse_debug_string(const char *str, const GDebugKey *keys, size_t n_keys) +{ + uint32_t retval = 0; + uint32_t all_mask = 0; + int all_flag = 0; + const char *p = str; + const char *q; + const char *s_limit = str + strlen(str); + size_t i; + + for (i = 0; i < n_keys; ++i) + all_mask |= keys[i].bitvalue; + + for (q = p; p <= s_limit; ++p) { + if ((arg_delim(*p) || *p == '\0')) { + size_t k_len = p - q; + if (k_len > 0) { + int is_all = !strncmp("all", q, k_len); + int is_help = !strncmp("help", q, k_len); + + if (!is_all && !is_help) { + for (i = 0; i < n_keys && (strncmp(q, keys[i].key, k_len) || strlen(keys[i].key) != k_len); ++i) + /* NOP */; + retval |= (i < n_keys) * keys[i].bitvalue; + } else if (is_all) { + all_flag = 1; + } else if (is_help) { + fputs("Debug keys: \n", stderr); + for (i = 0; i < n_keys; ++i) + fprintf(stderr, " %s (%08x)\n", keys[i].key, keys[i].bitvalue); + } + } + + /* Bump start of next token. */ + q = p + 1; + } + } + + return (!all_flag ? retval : (all_mask & ~retval)); +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ +void g_string_append_printf(GString *gstr, const char *fmt, ...) +{ + char *buf = NULL; + va_list args; + int ret; + +#if defined(HAVE_VASPRINTF) + va_start(args, fmt); + ret = vasprintf(&buf, fmt, args); + va_end(args); +#else + /* Have to call vnsprintf() twice to figure out how large buf needs to + * be. */ + char c; + + va_start(args, fmt); + ret = vsnprintf(&c, 1, fmt, args); + va_end(args); + if (ret >= 0) { + ++ret; + buf = (char *) calloc((size_t) ret + 1, sizeof(char)); + va_start(args, fmt); + ret = vsnprintf(buf, ret, fmt, args); + va_end(args); + } +#endif + + if (ret >= 0) { + if (ret + gstr->len + 1 >= gstr->allocated) { + /* Resize in 64-byte increments. */ + const unsigned int shift = 6; + size_t needed = ret + gstr->len + 1; + size_t new_alloc = ((needed >> shift) + ((needed & ((1 << shift) - 1)) != 0)) << shift; + char *newstr = realloc(gstr->str, new_alloc); + + if (NULL != newstr) { + gstr->str = newstr; + gstr->allocated = new_alloc; + } else { + /* Something bad happened. */ + } + } + + memcpy(gstr->str + gstr->len, buf, (size_t) (ret + 1)); + gstr->len += ret; + free(buf); + } else { + /* Something bad happened. */ + } +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ + +int g_shell_parse_argv (const char *cmd, int *argcp, char ***argvp, + GError** error) +{ + /* FIXME: */ + GLIB_UNUSED_PARAM(cmd); + GLIB_UNUSED_PARAM(argcp); + GLIB_UNUSED_PARAM(argvp); + GLIB_UNUSED_PARAM(error); + return 0; +} + +/*~=~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=*~=**/ + +int g_spawn_async (const char *wd, char **argv, char **envp, + GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + void *user_data, GPid *child_pid, GError **error) +{ + /* FIXME: */ + GLIB_UNUSED_PARAM(wd); + GLIB_UNUSED_PARAM(argv); + GLIB_UNUSED_PARAM(envp); + GLIB_UNUSED_PARAM(flags); + GLIB_UNUSED_PARAM(child_setup); + GLIB_UNUSED_PARAM(user_data); + GLIB_UNUSED_PARAM(child_pid); + GLIB_UNUSED_PARAM(error); + return 0; +} diff --git a/libslirp/minimal/glib.h b/libslirp/minimal/glib.h new file mode 100644 index 000000000..453042b99 --- /dev/null +++ b/libslirp/minimal/glib.h @@ -0,0 +1,515 @@ +/* Minimal definitions and functionality provided via the glib-2.0 utility library */ + +#if !defined(GLIB_H_MINIMAL) + +#include +#include +#include +#include +#include +#include + +#if !defined(_WIN32) +#include +#endif + +#if defined(_WIN32) +/* Code intentionally duplicated to keep the API version consistent. */ + +/* as defined in sdkddkver.h */ +#if !defined(WINVER) +#define WINVER 0x0601 /* Windows 7 */ +#endif +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT WINVER +#endif + +/* reduces the number of implicitly included headers */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#else +/* ntohl and friends... */ +#include +#endif + +#include + +/* Squelch ununsed formal paramter warnings... */ +#define GLIB_UNUSED_PARAM(thing) (void)(thing) + + +/* Platform defines: */ +#if !(defined(_WIN32) || defined(WIN32) || defined(_WIN64) || defined(WIN64)) +#define G_OS_UNIX 1 +#else +#define G_OS_WIN32 1 +#endif + +#if G_OS_UNIX +typedef ssize_t gssize; +#else +#include +typedef SSIZE_T gssize; +#endif + +#if !defined(FALSE) +#define FALSE 0 +#endif + +#if !defined(TRUE) +#define TRUE (!FALSE) +#endif + +/* GLIB_CHECK_VERSION: Always greater than glib 2.72.0 */ +#define GLIB_CHECK_VERSION(major, minor, micro) \ + (major > 2 || \ + (major == 2 && minor > 72) || \ + (major == 2 && minor == 72 && micro >= 0)) + +/* Stubs for inlining (note: The libslirp code itself just uses 'inline', no + * preprocessor specials.) */ + +#if defined(_MSC_VER) +#define GLIB_INLINE_DECL _inline +#define GLIB_NOINLINE_DECL _declspec (noinline) +#elif defined(__GNUC__) || defined(__clang__) +#define GLIB_INLINE_DECL inline +#define GLIB_NOINLINE_DECL __attribute__ ((noinline)) +#else +#define GLIB_INLINE_DECL +#define GLIB_NOINLINE_DECL +#endif + +/* GCC: */ +#if defined(__has_attribute) +# if __has_attribute (format) +# define G_GNUC_PRINTF(fmt, args) __attribute__((format(printf, fmt, args))) +# endif +/* Clang, maybe older GCC-s */ +#elif defined(__has_feature) +# if __has_feature (format) +# define G_GNUC_PRINTF(fmt, args) __attribute__((format(printf, fmt, args))) +# endif +#endif + +/* Everyone else... */ +#if !defined(G_GNUC_PRINTF) +#define G_GNUC_PRINTF(fmt, args) +#endif + +/* Static assertions appeared in C11 */ +#if __STDC_VERSION__ >= 201101L +#define G_STATIC_ASSERT(expr) _Static_assert(expr, #expr) +#else +#define G_STATIC_ASSERT(expr) +#endif + +#define G_N_ELEMENTS(arr) (sizeof(arr) / sizeof(arr[0])) + +/* glib types: */ +typedef void *gpointer; +typedef char gchar; +typedef int gboolean; +typedef int gint; + +/* g_assert_not_reached(): Tell the compiler's flow analysis that the statement cannot + be reached. */ +#if defined(__has_builtin) +# if __has_builtin(__builtin_unreachable) +# define g_assert_not_reached() { (void) 0; __builtin_unreachable(); } +# endif +#elif _MSC_VER +# define g_assert_not_reached() { (void) 0; __assume(0); } +#endif + +#if !defined(g_assert_not_reached) +# define g_assert_not_reached() { (void) 0; } +#endif + +/* G_LIKELY, G_UNLIKELY: Branch prediction hints. */ +#define G_LIKELY +#define G_UNLIKELY + +/* Pretty function names (G_STRFUNC replacement): */ +#if defined (__GNUC__) && defined (__STDC_VERSION__) +#define GLIB_PRETTY_FUNCNAME ((const char*) (__PRETTY_FUNCTION__)) +#elif __STDC_VERSION__ >= 199901 +#define GLIB_PRETTY_FUNCNAME ((const char*) (__func__)) +#elif defined (__GNUC__) || (defined(_MSC_VER) && (_MSC_VER > 1300)) +#define GLIB_PRETTY_FUNCNAME ((const char*) (__FUNCTION__)) +#else +#define GLIB_PRETTY_FUNCNAME ((const char*) ("???")) +#endif + +/* g_assert: */ +#if !defined(G_DISABLE_ASSERT) +#define g_assert(expr) g_do_assert(__FILE__, __LINE__, (expr), #expr) + +static void GLIB_INLINE_DECL g_do_assert(const char *where, int line, int bool_expr, const char *msg) +{ + if (!bool_expr) { + fprintf(stderr, "%s:%d: failed assertion: %s\n", where, line, msg); + fflush(stderr); + exit(1); + } +} +#else +#define g_assert(expr) +#endif + +/* Emulated glib logging facility: */ +#if !defined(G_LOG_DOMAIN) +#define G_LOG_DOMAIN "glib" +#endif + +typedef enum +{ + /* GLib log levels */ + G_LOG_LEVEL_ERROR = 1 << 2, /* always fatal */ + G_LOG_LEVEL_CRITICAL = 1 << 3, + G_LOG_LEVEL_WARNING = 1 << 4, + G_LOG_LEVEL_MESSAGE = 1 << 5, + G_LOG_LEVEL_INFO = 1 << 6, + G_LOG_LEVEL_DEBUG = 1 << 7 +} GLibLoggingFlags; + +typedef struct GLibLogger +{ + int logging_enabled; + void (*output_warning)(const char *fmt, va_list args) /* G_GNUC_PRINTF(1, 2) */; + void (*output_debug)(const char *fmt, va_list args) /* G_GNUC_PRINTF(1, 2) */; + void (*output_error)(const char *fmt, va_list args) /* G_GNUC_PRINTF(1, 2) */; + void (*output_critical)(const char *fmt, va_list args) /* G_GNUC_PRINTF(1, 2) */; +} GLibLogger; + + +/* Override default logging output.*/ +extern GLibLoggingFlags glib_logging_level; +extern GLibLogger *glib_logger; + +/* Compatibility with glib logging: */ +void g_log_set_debug_enabled(int); +void glib_set_logging_hooks(const GLibLogger *hooks); + +static GLIB_INLINE_DECL G_GNUC_PRINTF(1, 2) void g_warning(const char *fmt, ...) +{ + if (glib_logger->logging_enabled && glib_logging_level & G_LOG_LEVEL_WARNING) { + va_list args; + + va_start(args, fmt); + glib_logger->output_warning(fmt, args); + va_end(args); + } +} + +static GLIB_INLINE_DECL G_GNUC_PRINTF(1, 2) void g_debug(const char *fmt, ...) +{ + if (glib_logger->logging_enabled && glib_logging_level & G_LOG_LEVEL_DEBUG) { + va_list args; + + va_start(args, fmt); + glib_logger->output_debug(fmt, args); + va_end(args); + } +} + + +static GLIB_INLINE_DECL G_GNUC_PRINTF(1, 2) +void g_error(const char *fmt, ...) +{ + if (glib_logger->logging_enabled && glib_logging_level & G_LOG_LEVEL_ERROR) { + va_list args; + + va_start(args, fmt); + glib_logger->output_error(fmt, args); + va_end(args); + } +} + +static GLIB_INLINE_DECL G_GNUC_PRINTF(1, 2) +void g_critical(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + glib_logger->output_critical(fmt, args); + va_end(args); +} + +/* Macros where we want to see file and line where the error occurred. */ +#define g_warn_if_fail(expr) \ + do { \ + if (!(expr)) \ + g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, GLIB_PRETTY_FUNCNAME, \ + "Warning: Assertion failed - " #expr); \ + } while (0) + +static GLIB_INLINE_DECL void g_warn_message(const char *domain, const char *where_file, int where_line, + const char *fnname, const char *msg) +{ + if (glib_logger->logging_enabled) { + fprintf(stderr, "[%s/%s] %s:%d: %s\n", domain, fnname, where_file, where_line, msg); + } +} + +#define g_return_val_if_fail(expr, retval) \ + if (!(expr)) return retval + +#define g_return_if_fail(expr) \ + if (!(expr)) return + +#define g_warn_if_reached() \ + g_warn_message(G_LOG_DOMAIN, __FILE__, __LINE__, GLIB_PRETTY_FUNCNAME, \ + "Reached a statement that shouldn't be reached.") + +/* g_vnsprintf, g_snprintf: */ +static GLIB_INLINE_DECL +int g_vsnprintf(char *dest, size_t n_dest, const char *fmt, va_list args) +{ + if (fmt == NULL) + return -1; + + return vsnprintf(dest, n_dest, fmt, args); +} + +static GLIB_INLINE_DECL G_GNUC_PRINTF(3, 4) +int g_snprintf(char *dest, size_t n_dest, const char *fmt, ...) +{ + va_list args; + int retval; + + va_start(args, fmt); + retval = g_vsnprintf(dest, n_dest, fmt, args); + va_end(args); + + return retval; +} + +/* g_str_has_prefix: */ +static GLIB_INLINE_DECL int g_str_has_prefix(const char *str, const char *prefix) +{ + if (NULL == str || NULL == prefix) + return 0; + + size_t l_str = strlen(str), l_prefix = strlen(prefix); + if (l_str < l_prefix) + return 0; + + return memcmp(str, prefix, l_prefix); +} + +/* g_strstr: */ +char *g_strstr_len(char *src, gssize src_len, const char *srch); + +/* g_malloc, g_malloc0, g_free, g_realloc, g_new0 */ +static GLIB_INLINE_DECL void *g_malloc(size_t size) +{ + return calloc(1, size); +} +static GLIB_INLINE_DECL void *g_malloc0(size_t size) +{ + return calloc(1, size); +} + +static GLIB_INLINE_DECL void g_free(void *ptr) +{ + if (NULL != ptr) + free(ptr); +} + +static GLIB_INLINE_DECL void *g_realloc(void *ptr, size_t new_size) +{ + return realloc(ptr, new_size); +} + +/* g_new: A wrapper around calloc() returning a pointer cast to the required + * structure type. If n == 0, returns NULL. */ +#define g_new(struct_type, n) ((struct_type *) (n > 0 ? calloc(n, sizeof(struct_type)) : NULL)) + +/* g_new0: A wrapper around calloc() returning a pointer cast to the required + * structure type. */ +#define g_new0(struct_type, n) ((struct_type *) (n > 0 ? calloc(n, sizeof(struct_type)) : NULL)) + +/* GStrv: array of character pointers. */ + +typedef char **GStrv; + +static unsigned int GLIB_INLINE_DECL g_strv_length(GStrv strs) +{ + if (NULL == strs) + return 0; + + size_t i; + unsigned int n_strs; + + for (i = 0, n_strs = 0; strs[i] != NULL; ++i, ++n_strs); + + return n_strs; +} + +/* g_rand_int_range() and GRand -- glib returns gint32 (uint32_t), 32-bit random + * numbers. */ + +/* GRand (insert "Tubular Bells" joke here...) */ +typedef struct GRand +{ + uint32_t rand_state[4]; +} GRand; + +GRand *g_rand_new(); +void g_rand_free(GRand *rand); +uint32_t g_rand_int_range (GRand *rand, int begin, int end); + +/* glib string functions: */ + +typedef struct GString { + char *str; + size_t len; + size_t allocated; +} GString; + +GString *g_string_new(const char *initial); +char *g_string_free(GString *, int); +void g_strfreev(char **); +void g_string_append_printf(GString *, const char *, ...); +int g_ascii_strcasecmp(const char *s1, const char *s2); +size_t g_strlcpy(char *dest, const char *src, size_t n_dest); + +static GLIB_INLINE_DECL char *g_strdup(const char *s) +{ + if (NULL == s) + return NULL; + +#if !defined(HAVE__STRDUP_S) + return strdup(s); +#else + return _strdup(s); +#endif +} + +/* g_getenv: */ +const char *g_getenv(const char *envar); + +/* Error support: */ +typedef struct GError { + int domain; + int code; + char *message; +} GError; + +/* GDebugKey: */ +typedef struct GDebugKey { + const char *key; + uint32_t bitvalue; +} GDebugKey; + +void g_error_free(GError *); +uint32_t g_parse_debug_string(const char *str, const GDebugKey *keys, size_t n_keys); + +/* Spawn/exec support: */ + +#define G_SPAWN_SEARCH_PATH (1 << 0) + +typedef uint32_t GSpawnFlags; +typedef void (*GSpawnChildSetupFunc)(void *user_data); + +#if !defined(_WIN32) +typedef int GPid; +#else +/* This could be a DWORD for the process identifier, but really want + * the HANDLE for TerminateProcess() or WaitSingleObject(). */ +typedef HANDLE GPid; +#endif + +int g_spawn_async (const char *wd, char **argv, char **envp, + GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + void *user_data, GPid *child_pid, GError **error); + +/* Logging domain for shell functionality */ +#define G_SHELL_ERROR 0x00001000 +/* Specific shell functionality error codes */ +enum GShellError +{ + G_SHELL_ERROR_BAD_QUOTING, + G_SHELL_ERROR_EMPTY_STRING, + G_SHELL_ERROR_FAILED +}; + +int g_shell_parse_argv (const char *cmd, int *argcp, char ***argvp, + GError** error); + +/* "Safe" version of strerror() - when HAVE_GETENV_S is defined (notably, for + * Windows), return an internal buffer via strerror_s(). Otherwise, just + * returns whatever strerror() returns. */ +const char *g_strerror(int the_error); + +/* MIN and MAX: */ +#if !defined(MAX) +# if defined(__GNUC__) || defined(__clang__) +# define MAX(a, b) ({__typeof__(a) _a = a; __typeof__(b) _b = b; (_a > _b ? _a : _b); }) +# elif defined(_MSC_VER) +# define MAX __max +# else +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +# endif +#endif + +#if !defined(MIN) +# if defined(__GNUC__) || defined(__clang__) +# define MIN(a, b) ({__typeof__(a) _a = a; __typeof__(b) _b = b; (_a < _b ? _a : _b); }) +# elif defined(_MSC_VER) +# define MIN __min +# else +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +# endif +#endif + +/* And the network byte ordering and flipping. These are inlines to provide type + * safety. */ +static inline uint16_t GUINT16_FROM_BE(uint16_t val) +{ + return ntohs(val); +} + +static inline uint16_t GUINT16_TO_BE(uint16_t val) +{ + return htons(val); +} + +static inline uint32_t GUINT32_FROM_BE(uint32_t val) +{ + return ntohl(val); +} + +static inline uint32_t GUINT32_TO_BE(uint32_t val) +{ + return htonl(val); +} + +static inline int16_t GINT16_FROM_BE(int16_t val) +{ + return ((int16_t) ntohs((uint16_t) val)); +} + +static inline int16_t GINT16_TO_BE(int16_t val) +{ + return ((int16_t) htons((uint16_t) val)); +} + +static inline int32_t GINT32_FROM_BE(int32_t val) +{ + return ((int32_t) ntohl((uint32_t) val)); +} + +static inline int32_t GINT32_TO_BE(int32_t val) +{ + return ((int32_t) htonl((uint32_t) val)); +} + +#define GLIB_H_MINIMAL +#endif diff --git a/libslirp/minimal/tests/grand_test.c b/libslirp/minimal/tests/grand_test.c new file mode 100644 index 000000000..9a8c28daa --- /dev/null +++ b/libslirp/minimal/tests/grand_test.c @@ -0,0 +1,30 @@ +#include +#include + +int main(void) +{ + GRand *rnd = g_rand_new(); + GRand *rnd2 = g_rand_new(); + int j; + + puts("Random range: [10,30)"); + for (j = 0; j < 40; ++j) { + int i = g_rand_int_range(rnd, 10, 30); + + printf("rnd: %2d\n", i); + g_assert(i >= 10 && i < 30); + } + + puts("Random range: [-12,99)"); + for (j = 0; j < 200; ++j) { + int i = g_rand_int_range(rnd, -12, 99); + + printf("rnd: %2d\n", i); + g_assert(i >= -12 && i < 99); + } + + g_rand_free(rnd); + g_rand_free(rnd2); + + return 0; +} diff --git a/libslirp/minimal/tests/parsedebug.c b/libslirp/minimal/tests/parsedebug.c new file mode 100644 index 000000000..29e3c016c --- /dev/null +++ b/libslirp/minimal/tests/parsedebug.c @@ -0,0 +1,63 @@ +#include +#include +#include + +#define FLAG_0 0x00000001 +#define FLAG_1 0x00000002 +#define FLAG_2 0x00000004 +#define FLAG_3 0x00000100 + +typedef struct ParseDebug +{ + const char *flags; + uint32_t expected; +} ParseDebug; + +ParseDebug trials[] = { + { "all", FLAG_0|FLAG_1|FLAG_2|FLAG_3 }, + { "help", 0 }, + { " all", FLAG_0|FLAG_1|FLAG_2|FLAG_3 }, + { " all;", FLAG_0|FLAG_1|FLAG_2|FLAG_3 }, + { " all ;; ; ", FLAG_0|FLAG_1|FLAG_2|FLAG_3 }, + { "one;three", FLAG_1|FLAG_3 }, + { "one;three ;;", FLAG_1|FLAG_3 }, + { "one;;three", FLAG_1|FLAG_3 }, + { "all;one", FLAG_0|FLAG_2|FLAG_3 }, + { "one;all", FLAG_0|FLAG_2|FLAG_3 }, + { "zero;all;one", FLAG_2|FLAG_3 }, +}; + +const size_t n_trials = sizeof(trials) / sizeof(trials[0]); + +GDebugKey dkeys[] = { + { "zero", FLAG_0 }, + { "one", FLAG_1 }, + { "two", FLAG_2 }, + { "three", FLAG_3 }, +}; + +static size_t n_dkeys = sizeof(dkeys) / sizeof(dkeys[0]); + +int main(void) +{ + size_t i; + int success = 0, errs = 0; + + for (i = 0; i < n_trials; ++i) { + uint32_t val = g_parse_debug_string(trials[i].flags, dkeys, n_dkeys); + + if (val != trials[i].expected) { + fprintf(stderr, "flags: \"%s\" expected: %08x val: %08X\n", + trials[i].flags, trials[i].expected, val); + ++errs; + } else + ++success; + } + + if (!errs) { + fputs("All tests passed.\n", stderr); + } else { + fprintf(stderr, "%d tests passed, %d failed.\n", success, errs); + } + return 0; +} diff --git a/libslirp/minimal/tests/stringops_test.c b/libslirp/minimal/tests/stringops_test.c new file mode 100644 index 000000000..b01975093 --- /dev/null +++ b/libslirp/minimal/tests/stringops_test.c @@ -0,0 +1,46 @@ +#include +#include +#include + +int main(void) +{ + const char s2_initializer[] = "This is an initializer."; + const size_t s2_length = sizeof(s2_initializer) / sizeof(s2_initializer[0]); + const char s3_initializer[] = "0123456789012345678901234567899"; + const size_t s3_length = sizeof(s3_initializer) / sizeof(s3_initializer[0]); + + GString *s1 = g_string_new(NULL); + GString *s2 = g_string_new(s2_initializer); + GString *s3 = g_string_new(s3_initializer); + + g_assert(s1->len == 0 && s1->allocated == 32); + g_assert(s2->len == s2_length - 1 && s2->allocated == 32); + g_assert(s3->len == s3_length - 1 && s3->allocated == 64); + + g_assert(g_string_free(s1, TRUE) == NULL); + g_assert(g_string_free(s2, TRUE) == NULL); + + char *s3_str = g_string_free(s3, FALSE); + g_assert(strcmp(s3_initializer, s3_str) == 0); + free(s3_str); + + GString *s4 = g_string_new("something!!"); + g_string_append_printf(s4, " More string with arguments: %s\n", "--special--"); + fputs(s4->str, stdout); + g_assert(strcmp(s4->str, "something!! More string with arguments: --special--\n") == 0); + g_string_free(s4, TRUE); + + GString *s5 = g_string_new("a_really_long_prefix: "); + g_string_append_printf(s5, " string_append_printf %08x %08x %s\n", 0, ~0, "--special--"); + fputs(s5->str, stdout); + g_assert(strcmp(s5->str, "a_really_long_prefix: string_append_printf 00000000 ffffffff --special--\n") == 0); + g_string_free(s5, TRUE); + + g_assert(g_ascii_strcasecmp("This is equal", "this is Equal") == 0); + g_assert(g_ascii_strcasecmp("One side", "Other side") < 0); + g_assert(g_ascii_strcasecmp("One side", "One sido") < 0); + g_assert(g_ascii_strcasecmp("One sido", "One side") > 0); + g_assert(g_ascii_strcasecmp("1243 One Two three fOuR", "1243 oNe tWO thReE FoUr") == 0); + + return 0; +} diff --git a/libslirp/minimal/tests/strstr_len_test.c b/libslirp/minimal/tests/strstr_len_test.c new file mode 100644 index 000000000..ddfe98823 --- /dev/null +++ b/libslirp/minimal/tests/strstr_len_test.c @@ -0,0 +1,46 @@ +#include +#include +#include + +char *test1 = "This string tests a test pattern."; +const char *test1_patterns[] = { + "This", + "his ", + "a test", + "pattern.", + "ern.", + ".", + "tests a test", + "ng tes", + "This string tests a test pattern." +}; + +char *test2 = + "DESCRIPTION top\n" + " The strstr() function finds the first occurrence of the substring\n" + " needle in the string haystack. The terminating null bytes ('\\0')\n" + " are not compared.\n" + "\n" + " The strcasestr() function is like strstr(), but ignores the case\n" + " of both arguments.\n" + "RETURN VALUE top\n" + " These functions return a pointer to the beginning of the located\n" + " substring, or NULL if the substring is not found.\n" + "\n" + " If needle is the empty string, the return value is always\n" + " haystack itself.\n"; + +int main(void) +{ + size_t i; + + for (i = 0; i < sizeof(test1_patterns) / sizeof(test1_patterns[0]); ++i) { + g_assert(g_strstr_len(test1, strlen(test1), test1_patterns[i]) != NULL); + } + + g_assert(g_strstr_len(test2, strlen(test2), "always\n") != NULL); + g_assert(g_strstr_len(test2, strlen(test2), " If needle is the empty") != NULL); + g_assert(g_strstr_len(test2, strlen(test2), "") == test2); + + return 0; +} \ No newline at end of file diff --git a/slirp/arp_table.c b/libslirp/src/arp_table.c similarity index 68% rename from slirp/arp_table.c rename to libslirp/src/arp_table.c index e7a18e937..0c7c2c78c 100644 --- a/slirp/arp_table.c +++ b/libslirp/src/arp_table.c @@ -1,89 +1,98 @@ -/* - * ARP table - * - * Copyright (c) 2011 AdaCore - * - * 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" - -void arp_table_add(Slirp *slirp, uint32_t ip_addr, const uint8_t ethaddr[ETH_ALEN]) -{ - const uint32_t broadcast_addr = - ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; - ArpTable *arptbl = &slirp->arp_table; - int i; - - DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = 0x%x", ip_addr); - DEBUG_ARGS(" hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", - ethaddr[0], ethaddr[1], ethaddr[2], - ethaddr[3], ethaddr[4], ethaddr[5]); - - if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { - /* Do not register broadcast addresses */ - return; - } - - /* Search for an entry */ - for (i = 0; i < ARP_TABLE_SIZE; i++) { - if (arptbl->table[i].ar_sip == ip_addr) { - /* Update the entry */ - memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN); - return; - } - } - - /* No entry found, create a new one */ - arptbl->table[arptbl->next_victim].ar_sip = ip_addr; - memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN); - arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE; -} - -bool arp_table_search(Slirp *slirp, uint32_t ip_addr, - uint8_t out_ethaddr[ETH_ALEN]) -{ - const uint32_t broadcast_addr = - ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; - ArpTable *arptbl = &slirp->arp_table; - int i; - - DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = 0x%x", ip_addr); - - /* If broadcast address */ - if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { - /* return Ethernet broadcast address */ - memset(out_ethaddr, 0xff, ETH_ALEN); - return 1; - } - - for (i = 0; i < ARP_TABLE_SIZE; i++) { - if (arptbl->table[i].ar_sip == ip_addr) { - memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN); - DEBUG_ARGS(" found hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", - out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], - out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); - return 1; - } - } - - return 0; -} +/* SPDX-License-Identifier: MIT */ +/* + * ARP table + * + * Copyright (c) 2011 AdaCore + * + * 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" + +#include + +void arp_table_add(Slirp *slirp, uint32_t ip_addr, + const uint8_t ethaddr[ETH_ALEN]) +{ + const uint32_t broadcast_addr = + ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; + ArpTable *arptbl = &slirp->arp_table; + size_t i; + char ethaddr_str[ETH_ADDRSTRLEN]; + char addr[INET_ADDRSTRLEN]; + + DEBUG_CALL("arp_table_add"); + DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr }, + addr, sizeof(addr))); + DEBUG_ARG("hw addr = %s", slirp_ether_ntoa(ethaddr, ethaddr_str, + sizeof(ethaddr_str))); + + if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { + /* Do not register broadcast addresses */ + return; + } + + /* Search for an entry */ + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if (arptbl->table[i].ar_sip == ip_addr) { + /* Update the entry */ + memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN); + return; + } + } + + /* No entry found, create a new one */ + arptbl->table[arptbl->next_victim].ar_sip = ip_addr; + memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN); + arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE; +} + +bool arp_table_search(Slirp *slirp, uint32_t ip_addr, + uint8_t out_ethaddr[ETH_ALEN]) +{ + const uint32_t broadcast_addr = + ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; + ArpTable *arptbl = &slirp->arp_table; + size_t i; + char ethaddr_str[ETH_ADDRSTRLEN]; + char addr[INET_ADDRSTRLEN]; + + DEBUG_CALL("arp_table_search"); + DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr }, + addr, sizeof(addr))); + + /* If broadcast address */ + if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { + /* return Ethernet broadcast address */ + memset(out_ethaddr, 0xff, ETH_ALEN); + return 1; + } + + for (i = 0; i < ARP_TABLE_SIZE; i++) { + if (arptbl->table[i].ar_sip == ip_addr) { + memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN); + DEBUG_ARG("found hw addr = %s", + slirp_ether_ntoa(out_ethaddr, ethaddr_str, + sizeof(ethaddr_str))); + return 1; + } + } + + return 0; +} diff --git a/slirp/bootp.c b/libslirp/src/bootp.c similarity index 60% rename from slirp/bootp.c rename to libslirp/src/bootp.c index cb3279e1c..1a1a5dd2c 100644 --- a/slirp/bootp.c +++ b/libslirp/src/bootp.c @@ -1,324 +1,420 @@ -/* - * 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 - -/* XXX: only DHCP is supported */ - -#define LEASE_TIME (24 * 3600) - -static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; - -static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr, - const uint8_t *macaddr) -{ - BOOTPClient *bc; - int i; - - for(i = 0; i < NB_BOOTP_CLIENTS; i++) { - bc = &slirp->bootp_clients[i]; - if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) - goto found; - } - return NULL; - found: - bc = &slirp->bootp_clients[i]; - bc->allocated = 1; - paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); - return bc; -} - -static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr, - const uint8_t *macaddr) -{ - uint32_t req_addr = ntohl(paddr->s_addr); - uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr); - BOOTPClient *bc; - - if (req_addr >= dhcp_addr && - req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) { - bc = &slirp->bootp_clients[req_addr - dhcp_addr]; - if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) { - bc->allocated = 1; - return bc; - } - } - return NULL; -} - -static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr, - const uint8_t *macaddr) -{ - BOOTPClient *bc; - int i; - - for(i = 0; i < NB_BOOTP_CLIENTS; i++) { - if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6)) - goto found; - } - return NULL; - found: - bc = &slirp->bootp_clients[i]; - bc->allocated = 1; - paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); - return bc; -} - -static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type, - struct in_addr *preq_addr) -{ - const uint8_t *p, *p_end; - int len, tag; - - *pmsg_type = 0; - preq_addr->s_addr = htonl(0L); - - p = bp->bp_vend; - p_end = p + DHCP_OPT_LEN; - 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++; - DPRINTF("dhcp: tag=%d len=%d\n", tag, len); - - switch(tag) { - case RFC2132_MSG_TYPE: - if (len >= 1) - *pmsg_type = p[0]; - break; - case RFC2132_REQ_ADDR: - if (len >= 4) { - memcpy(&(preq_addr->s_addr), p, 4); - } - break; - default: - break; - } - p += len; - } - } - if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) && - bp->bp_ciaddr.s_addr) { - memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4); - } -} - -static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) -{ - BOOTPClient *bc = NULL; - struct mbuf *m; - struct bootp_t *rbp; - struct sockaddr_in saddr, daddr; - struct in_addr preq_addr; - int dhcp_msg_type, val; - uint8_t *q; - uint8_t client_ethaddr[ETH_ALEN]; - - /* extract exact DHCP msg type */ - dhcp_decode(bp, &dhcp_msg_type, &preq_addr); - DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); - if (preq_addr.s_addr != htonl(0L)) - DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr)); - else - DPRINTF("\n"); - - 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; - - /* Get client's hardware address from bootp request */ - memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN); - - m = m_get(slirp); - if (!m) { - 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)); - daddr.sin_addr.s_addr = 0xffffffffu; - - if (dhcp_msg_type == DHCPDISCOVER) { - if (preq_addr.s_addr != htonl(0L)) { - bc = request_addr(slirp, &preq_addr, client_ethaddr); - if (bc) { - daddr.sin_addr = preq_addr; - } - } - if (!bc) { - new_addr: - bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr); - if (!bc) { - DPRINTF("no address left\n"); - return; - } - } - memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); - } else if (preq_addr.s_addr != htonl(0L)) { - bc = request_addr(slirp, &preq_addr, client_ethaddr); - if (bc) { - daddr.sin_addr = preq_addr; - memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); - } else { - /* DHCPNAKs should be sent to broadcast */ - daddr.sin_addr.s_addr = 0xffffffff; - } - } else { - bc = find_addr(slirp, &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; - } - } - - /* Update ARP table for this IP address */ - arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr); - - saddr.sin_addr = slirp->vhost_addr; - 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, ETH_ALEN); - - 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 (bc) { - DPRINTF("%s addr=%08x\n", - (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", - ntohl(daddr.sin_addr.s_addr)); - - if (dhcp_msg_type == DHCPDISCOVER) { - *q++ = RFC2132_MSG_TYPE; - *q++ = 1; - *q++ = DHCPOFFER; - } else /* DHCPREQUEST */ { - *q++ = RFC2132_MSG_TYPE; - *q++ = 1; - *q++ = DHCPACK; - } - - if (slirp->bootp_filename) - snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s", - slirp->bootp_filename); - - *q++ = RFC2132_SRV_ID; - *q++ = 4; - memcpy(q, &saddr.sin_addr, 4); - q += 4; - - *q++ = RFC1533_NETMASK; - *q++ = 4; - memcpy(q, &slirp->vnetwork_mask, 4); - q += 4; - - if (!slirp->restricted) { - *q++ = RFC1533_GATEWAY; - *q++ = 4; - memcpy(q, &saddr.sin_addr, 4); - q += 4; - - *q++ = RFC1533_DNS; - *q++ = 4; - memcpy(q, &slirp->vnameserver_addr, 4); - q += 4; - } - - *q++ = RFC2132_LEASE_TIME; - *q++ = 4; - val = htonl(LEASE_TIME); - memcpy(q, &val, 4); - q += 4; - - if (*slirp->client_hostname) { - val = strlen(slirp->client_hostname); - *q++ = RFC1533_HOSTNAME; - *q++ = val; - memcpy(q, slirp->client_hostname, val); - q += val; - } - - if (slirp->vdnssearch) { - size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); - val = slirp->vdnssearch_len; - if ((size_t)val + 1 > spaceleft) { - g_warning("DHCP packet size exceeded, " - "omitting domain-search option."); - } else { - memcpy(q, slirp->vdnssearch, val); - q += val; - } - } - } else { - static const char nak_msg[] = "requested address not available"; - - DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr.s_addr)); - - *q++ = RFC2132_MSG_TYPE; - *q++ = 1; - *q++ = DHCPNAK; - - *q++ = RFC2132_MESSAGE; - *q++ = sizeof(nak_msg) - 1; - memcpy(q, nak_msg, sizeof(nak_msg) - 1); - q += sizeof(nak_msg) - 1; - } - *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 mbuf *m) -{ - struct bootp_t *bp = mtod(m, struct bootp_t *); - - if (bp->bp_op == BOOTP_REQUEST) { - bootp_reply(m->slirp, bp); - } -} +/* SPDX-License-Identifier: MIT */ +/* + * 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" + +#if defined(_WIN32) +/* Windows ntohl() returns an u_long value. + * Add a type cast to match the format strings. */ +#define ntohl(n) ((uint32_t)ntohl(n)) +#endif + +/* XXX: only DHCP is supported */ + +#define LEASE_TIME (24 * 3600) + +#define UEFI_HTTP_VENDOR_CLASS_ID "HTTPClient" + +static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; + +#define DPRINTF(...) DEBUG_RAW_CALL(__VA_ARGS__) + +static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr, + const uint8_t *macaddr) +{ + BOOTPClient *bc; + int i; + + for (i = 0; i < NB_BOOTP_CLIENTS; i++) { + bc = &slirp->bootp_clients[i]; + if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) + goto found; + } + return NULL; +found: + bc = &slirp->bootp_clients[i]; + bc->allocated = 1; + paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); + return bc; +} + +static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr, + const uint8_t *macaddr) +{ + uint32_t req_addr = ntohl(paddr->s_addr); + uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr); + BOOTPClient *bc; + + if (req_addr >= dhcp_addr && req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) { + bc = &slirp->bootp_clients[req_addr - dhcp_addr]; + if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) { + bc->allocated = 1; + return bc; + } + } + return NULL; +} + +static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr, + const uint8_t *macaddr) +{ + BOOTPClient *bc; + int i; + + for (i = 0; i < NB_BOOTP_CLIENTS; i++) { + if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6)) + goto found; + } + return NULL; +found: + bc = &slirp->bootp_clients[i]; + bc->allocated = 1; + paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); + return bc; +} + +static void dhcp_decode(const struct bootp_t *bp, + const uint8_t *bp_end, + int *pmsg_type, + struct in_addr *preq_addr) +{ + const uint8_t *p; + int len, tag; + + *pmsg_type = 0; + preq_addr->s_addr = htonl(0L); + + p = bp->bp_vend; + if (memcmp(p, rfc1533_cookie, 4) != 0) + return; + p += 4; + while (p < bp_end) { + tag = p[0]; + if (tag == RFC1533_PAD) { + p++; + } else if (tag == RFC1533_END) { + break; + } else { + p++; + if (p >= bp_end) + break; + len = *p++; + if (p + len > bp_end) { + break; + } + DPRINTF("dhcp: tag=%d len=%d\n", tag, len); + + switch (tag) { + case RFC2132_MSG_TYPE: + if (len >= 1) + *pmsg_type = p[0]; + break; + case RFC2132_REQ_ADDR: + if (len >= 4) { + memcpy(&(preq_addr->s_addr), p, 4); + } + break; + default: + break; + } + p += len; + } + } + if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) && + bp->bp_ciaddr.s_addr) { + memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4); + } +} + +static void bootp_reply(Slirp *slirp, + const struct bootp_t *bp, + const uint8_t *bp_end) +{ + BOOTPClient *bc = NULL; + struct mbuf *m; + struct bootp_t *rbp; + struct sockaddr_in saddr, daddr; + struct in_addr preq_addr; + int dhcp_msg_type, val; + uint8_t *q; + uint8_t *end; + uint8_t client_ethaddr[ETH_ALEN]; + + /* extract exact DHCP msg type */ + dhcp_decode(bp, bp_end, &dhcp_msg_type, &preq_addr); + DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); + if (preq_addr.s_addr != htonl(0L)) + DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); + else { + DPRINTF("\n"); + } + + 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; + + /* Get client's hardware address from bootp request */ + memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN); + + m = m_get(slirp); + if (!m) { + return; + } + m->m_data += IF_MAXLINKHDR; + m_inc(m, sizeof(struct bootp_t) + DHCP_OPT_LEN); + rbp = (struct bootp_t *)m->m_data; + m->m_data += sizeof(struct udpiphdr); + memset(rbp, 0, sizeof(struct bootp_t) + DHCP_OPT_LEN); + + if (dhcp_msg_type == DHCPDISCOVER) { + if (preq_addr.s_addr != htonl(0L)) { + bc = request_addr(slirp, &preq_addr, client_ethaddr); + if (bc) { + daddr.sin_addr = preq_addr; + } + } + if (!bc) { + new_addr: + bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr); + if (!bc) { + DPRINTF("no address left\n"); + return; + } + } + memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); + } else if (preq_addr.s_addr != htonl(0L)) { + bc = request_addr(slirp, &preq_addr, client_ethaddr); + if (bc) { + daddr.sin_addr = preq_addr; + memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); + } else { + /* DHCPNAKs should be sent to broadcast */ + daddr.sin_addr.s_addr = 0xffffffff; + } + } else { + bc = find_addr(slirp, &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; + } + } + + /* Update ARP table for this IP address */ + arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr); + + saddr.sin_addr = slirp->vhost_addr; + 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, ETH_ALEN); + + rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ + rbp->bp_siaddr = saddr.sin_addr; /* Next server IP address */ + + q = rbp->bp_vend; + end = rbp->bp_vend + DHCP_OPT_LEN; + memcpy(q, rfc1533_cookie, 4); + q += 4; + + if (bc) { + DPRINTF("%s addr=%08" PRIx32 "\n", + (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", + ntohl(daddr.sin_addr.s_addr)); + + if (dhcp_msg_type == DHCPDISCOVER) { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPOFFER; + } else /* DHCPREQUEST */ { + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPACK; + } + + if (NULL != slirp->bootp_filename) { + g_assert(strlen(slirp->bootp_filename) < sizeof(rbp->bp_file)); +#if !defined(HAVE_STRCPY_S) + strcpy(rbp->bp_file, slirp->bootp_filename); +#else + strcpy_s(rbp->bp_file, sizeof(rbp->bp_file), slirp->bootp_filename); +#endif + } + + *q++ = RFC2132_SRV_ID; + *q++ = 4; + memcpy(q, &saddr.sin_addr, 4); + q += 4; + + *q++ = RFC1533_NETMASK; + *q++ = 4; + memcpy(q, &slirp->vnetwork_mask, 4); + q += 4; + + if (!slirp->restricted) { + *q++ = RFC1533_GATEWAY; + *q++ = 4; + memcpy(q, &saddr.sin_addr, 4); + q += 4; + + *q++ = RFC1533_DNS; + *q++ = 4; + memcpy(q, &slirp->vnameserver_addr, 4); + q += 4; + } + + *q++ = RFC2132_LEASE_TIME; + *q++ = 4; + val = htonl(LEASE_TIME); + memcpy(q, &val, 4); + q += 4; + + if (*slirp->client_hostname) { + val = strlen(slirp->client_hostname); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting host name option."); + } else { + *q++ = RFC1533_HOSTNAME; + *q++ = val; + memcpy(q, slirp->client_hostname, val); + q += val; + } + } + + if (slirp->vdomainname) { + val = strlen(slirp->vdomainname); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting domain name option."); + } else { + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + } + + if (slirp->tftp_server_name) { + struct addrinfo hints = { + .ai_family = AF_INET, /* must be IPv4 */ + }; + struct addrinfo *ai = NULL; + int gaierrno; + + if ((gaierrno = getaddrinfo(slirp->tftp_server_name, + NULL, &hints, &ai))) { + g_warning("Failed to resolve tftp-server-name '%s': %s. " + "Sending DHCP server as next server address.", + slirp->tftp_server_name, + gai_strerror(gaierrno)); + } else { + /* only use the first candidate */ + rbp->bp_siaddr = ((struct sockaddr_in*)ai->ai_addr)->sin_addr; + } + freeaddrinfo(ai); + + val = strlen(slirp->tftp_server_name); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting tftp-server-name option."); + } else { + *q++ = RFC2132_TFTP_SERVER_NAME; + *q++ = val; + memcpy(q, slirp->tftp_server_name, val); + q += val; + } + } + + if (slirp->vdnssearch) { + val = slirp->vdnssearch_len; + if (q + val >= end) { + g_warning("DHCP packet size exceeded, " + "omitting domain-search option."); + } else { + memcpy(q, slirp->vdnssearch, val); + q += val; + } + } + + /* this allows to support UEFI HTTP boot: according to the UEFI + specification, DHCP server must send vendor class identifier option + set to "HTTPClient" string, when responding to DHCP requests as part + of the UEFI HTTP boot + + we assume that, if the bootfile parameter was configured as an http + URL, the user intends to perform UEFI HTTP boot, so send this option + automatically */ + if (slirp->bootp_filename && g_str_has_prefix(slirp->bootp_filename, "http://")) { + val = strlen(UEFI_HTTP_VENDOR_CLASS_ID); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting vendor class id option."); + } else { + *q++ = RFC2132_VENDOR_CLASS_ID; + *q++ = val; + memcpy(q, UEFI_HTTP_VENDOR_CLASS_ID, val); + q += val; + } + } + } else { + static const char nak_msg[] = "requested address not available"; + + DPRINTF("nak'ed addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); + + *q++ = RFC2132_MSG_TYPE; + *q++ = 1; + *q++ = DHCPNAK; + + *q++ = RFC2132_MESSAGE; + *q++ = sizeof(nak_msg) - 1; + memcpy(q, nak_msg, sizeof(nak_msg) - 1); + q += sizeof(nak_msg) - 1; + } + assert(q < end); + *q++ = RFC1533_END; + + daddr.sin_addr.s_addr = 0xffffffffu; + + assert(q <= end); + + m->m_len = sizeof(struct bootp_t) + (end - rbp->bp_vend) - sizeof(struct ip) - sizeof(struct udphdr); + udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); +} + +void bootp_input(struct mbuf *m) +{ + struct bootp_t *bp = mtod_check(m, sizeof(struct bootp_t)); + + if (!m->slirp->disable_dhcp && bp && bp->bp_op == BOOTP_REQUEST) { + bootp_reply(m->slirp, bp, m_end(m)); + } +} diff --git a/libslirp/src/bootp.h b/libslirp/src/bootp.h new file mode 100644 index 000000000..c51096025 --- /dev/null +++ b/libslirp/src/bootp.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* bootp/dhcp defines */ + +#ifndef SLIRP_BOOTP_H +#define SLIRP_BOOTP_H + +#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_MESSAGE 56 +#define RFC2132_MAX_SIZE 57 +#define RFC2132_RENEWAL_TIME 58 +#define RFC2132_REBIND_TIME 59 +#define RFC2132_VENDOR_CLASS_ID 60 +#define RFC2132_TFTP_SERVER_NAME 66 + +#define DHCPDISCOVER 1 +#define DHCPOFFER 2 +#define DHCPREQUEST 3 +#define DHCPACK 5 +#define DHCPNAK 6 + +#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 + +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]; + char bp_file[128]; + uint8_t bp_vend[]; +}; + +typedef struct { + uint16_t allocated; + uint8_t macaddr[6]; +} BOOTPClient; + +#define NB_BOOTP_CLIENTS 16 + +/* Process a bootp packet from the guest */ +void bootp_input(struct mbuf *m); + +#endif diff --git a/libslirp/src/cksum.c b/libslirp/src/cksum.c new file mode 100644 index 000000000..897ca8bc6 --- /dev/null +++ b/libslirp/src/cksum.c @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 +#include "slirp.h" + +/* + * Checksum routine adapted from NetBSD's implementation. Since we never span more + * than one mbuf or start at a non-zero offset, we can optimise. + * + * This routine is very heavily used in the network code and should be modified + * for each CPU to be as fast as possible. + */ + +uint16_t cksum(struct mbuf *m, size_t len) +{ +#if GLIB_SIZEOF_VOID_P == 8 + size_t mlen; + uint64_t sum, partial; + unsigned int final_acc; + uint8_t *data; + bool needs_swap, started_on_odd; + + started_on_odd = false; + sum = 0; + + mlen = m->m_len; + data = mtod(m, uint8_t *); + if (mlen == 0) + return ((uint16_t) ~0); + if (mlen > len) + mlen = len; + if (len > mlen) { + DEBUG_ERROR("cksum: mbuf data underrun (out of data, len > mlen)"); + DEBUG_ERROR(" len = %" SLIRP_PRIsize_t, len); + DEBUG_ERROR(" mlen = %" SLIRP_PRIsize_t, mlen); + } + + partial = 0; + if ((uintptr_t)data & 1) { + /* Align on word boundary */ + started_on_odd = !started_on_odd; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + partial = *data << 8; +#else + partial = *data; +#endif + ++data; + --mlen; + } + needs_swap = started_on_odd; + if ((uintptr_t)data & 2) { + if (mlen < 2) + goto trailing_bytes; + partial += *(uint16_t *)data; + data += 2; + mlen -= 2; + } + while (mlen >= 64) { + partial += *(uint32_t *)data; + partial += *(uint32_t *)(data + 4); + partial += *(uint32_t *)(data + 8); + partial += *(uint32_t *)(data + 12); + partial += *(uint32_t *)(data + 16); + partial += *(uint32_t *)(data + 20); + partial += *(uint32_t *)(data + 24); + partial += *(uint32_t *)(data + 28); + partial += *(uint32_t *)(data + 32); + partial += *(uint32_t *)(data + 36); + partial += *(uint32_t *)(data + 40); + partial += *(uint32_t *)(data + 44); + partial += *(uint32_t *)(data + 48); + partial += *(uint32_t *)(data + 52); + partial += *(uint32_t *)(data + 56); + partial += *(uint32_t *)(data + 60); + data += 64; + mlen -= 64; + if (partial & (3ULL << 62)) { + if (needs_swap) + partial = (partial << 8) + (partial >> 56); + sum += (partial >> 32); + sum += (partial & 0xffffffff); + partial = 0; + } + } + /* + * mlen is not updated below as the remaining tests + * are using bit masks, which are not affected. + */ + if (mlen & 32) { + partial += *(uint32_t *)data; + partial += *(uint32_t *)(data + 4); + partial += *(uint32_t *)(data + 8); + partial += *(uint32_t *)(data + 12); + partial += *(uint32_t *)(data + 16); + partial += *(uint32_t *)(data + 20); + partial += *(uint32_t *)(data + 24); + partial += *(uint32_t *)(data + 28); + data += 32; + } + if (mlen & 16) { + partial += *(uint32_t *)data; + partial += *(uint32_t *)(data + 4); + partial += *(uint32_t *)(data + 8); + partial += *(uint32_t *)(data + 12); + data += 16; + } + if (mlen & 8) { + partial += *(uint32_t *)data; + partial += *(uint32_t *)(data + 4); + data += 8; + } + if (mlen & 4) { + partial += *(uint32_t *)data; + data += 4; + } + if (mlen & 2) { + partial += *(uint16_t *)data; + data += 2; + } +trailing_bytes: + if (mlen & 1) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + partial += *data; +#else + partial += *data << 8; +#endif + started_on_odd = !started_on_odd; + } + + if (needs_swap) + partial = (partial << 8) + (partial >> 56); + + sum += (partial >> 32) + (partial & 0xffffffff); + /* + * Reduce sum to allow potential byte swap + * in the next iteration without carry. + */ + sum = (sum >> 32) + (sum & 0xffffffff); + + final_acc = (sum >> 48) + ((sum >> 32) & 0xffff) + + ((sum >> 16) & 0xffff) + (sum & 0xffff); + final_acc = (final_acc >> 16) + (final_acc & 0xffff); + final_acc = (final_acc >> 16) + (final_acc & 0xffff); + + return ((uint16_t) ~final_acc); +#else /* Assume 32-bit architecture */ + size_t mlen; + uint32_t sum, partial; + uint8_t *data; + bool needs_swap, started_on_odd; + + needs_swap = false; + started_on_odd = false; + sum = 0; + + mlen = m->m_len; + data = mtod(m, uint8_t *); + if (mlen == 0) + return ((uint16_t) ~0); + + if (mlen > len) + mlen = len; + if (len > mlen) { + DEBUG_ERROR("cksum: mbuf data underrun (out of data, len > mlen)"); + DEBUG_ERROR(" len = %" SLIRP_PRIsize_t, len); + DEBUG_ERROR(" mlen = %" SLIRP_PRIsize_t, mlen); + } + + partial = 0; + if ((uintptr_t) data & 1) { + /* Align on word boundary */ + started_on_odd = !started_on_odd; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + partial = *data << 8; +#else + partial = *data; +#endif + ++data; + --mlen; + } + needs_swap = started_on_odd; + while (mlen >= 32) { + partial += *(uint16_t *)data; + partial += *(uint16_t *)(data + 2); + partial += *(uint16_t *)(data + 4); + partial += *(uint16_t *)(data + 6); + partial += *(uint16_t *)(data + 8); + partial += *(uint16_t *)(data + 10); + partial += *(uint16_t *)(data + 12); + partial += *(uint16_t *)(data + 14); + partial += *(uint16_t *)(data + 16); + partial += *(uint16_t *)(data + 18); + partial += *(uint16_t *)(data + 20); + partial += *(uint16_t *)(data + 22); + partial += *(uint16_t *)(data + 24); + partial += *(uint16_t *)(data + 26); + partial += *(uint16_t *)(data + 28); + partial += *(uint16_t *)(data + 30); + data += 32; + mlen -= 32; + if (partial & 0xc0000000) { + if (needs_swap) + partial = (partial << 8) + (partial >> 24); + sum += (partial >> 16); + sum += (partial & 0xffff); + partial = 0; + } + } + /* + * mlen is not updated below as the remaining tests + * are using bit masks, which are not affected. + */ + if (mlen & 16) { + partial += *(uint16_t *)data; + partial += *(uint16_t *)(data + 2); + partial += *(uint16_t *)(data + 4); + partial += *(uint16_t *)(data + 6); + partial += *(uint16_t *)(data + 8); + partial += *(uint16_t *)(data + 10); + partial += *(uint16_t *)(data + 12); + partial += *(uint16_t *)(data + 14); + data += 16; + } + if (mlen & 8) { + partial += *(uint16_t *)data; + partial += *(uint16_t *)(data + 2); + partial += *(uint16_t *)(data + 4); + partial += *(uint16_t *)(data + 6); + data += 8; + } + if (mlen & 4) { + partial += *(uint16_t *)data; + partial += *(uint16_t *)(data + 2); + data += 4; + } + if (mlen & 2) { + partial += *(uint16_t *)data; + data += 2; + } + if (mlen & 1) { +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + partial += *data; +#else + partial += *data << 8; +#endif + started_on_odd = !started_on_odd; + } + + if (needs_swap) + partial = (partial << 8) + (partial >> 24); + + sum += (partial >> 16) + (partial & 0xffff); + /* + * Reduce sum to allow potential byte swap + * in the next iteration without carry. + */ + sum = (sum >> 16) + (sum & 0xffff); + return ((uint16_t) ~sum); +#endif +} + +uint16_t ip6_cksum(struct mbuf *m) +{ + /* TODO: Optimize this by being able to pass the ip6_pseudohdr to cksum + * separately from the mbuf */ + struct ip6 save_ip, *ip = mtod(m, struct ip6 *); + struct ip6_pseudohdr *ih = mtod(m, struct ip6_pseudohdr *); + uint16_t sum; + + save_ip = *ip; + + ih->ih_src = save_ip.ip_src; + ih->ih_dst = save_ip.ip_dst; + ih->ih_pl = htonl((uint32_t)ntohs(save_ip.ip_pl)); + ih->ih_zero_hi = 0; + ih->ih_zero_lo = 0; + ih->ih_nh = save_ip.ip_nh; + + sum = cksum(m, ((int)sizeof(struct ip6_pseudohdr)) + ntohl(ih->ih_pl)); + + *ip = save_ip; + + return sum; +} diff --git a/libslirp/src/debug.h b/libslirp/src/debug.h new file mode 100644 index 000000000..87412260d --- /dev/null +++ b/libslirp/src/debug.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +extern unsigned int slirp_debug; + +#define DEBUG_CALL(name) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_CALL)) { \ + g_debug(name "..."); \ + } \ + } while (0) + +#define DEBUG_VERBOSE_CALL(name) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_VERBOSE_CALL)) { \ + g_debug(name "..."); \ + } \ + } while (0) + +#define DEBUG_RAW_CALL(...) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_CALL)) { \ + g_debug(__VA_ARGS__); \ + } \ + } while (0) + +#define DEBUG_ARG(...) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_CALL)) { \ + g_debug(" " __VA_ARGS__); \ + } \ + } while (0) + +#define DEBUG_MISC(...) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_MISC)) { \ + g_debug(__VA_ARGS__); \ + } \ + } while (0) + +#define DEBUG_ERROR(...) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_ERROR)) { \ + g_debug(__VA_ARGS__); \ + } \ + } while (0) + +#define DEBUG_TFTP(...) \ + do { \ + if (G_UNLIKELY(slirp_debug & SLIRP_DBG_TFTP)) { \ + g_debug(__VA_ARGS__); \ + } \ + } while (0) + +#endif /* DEBUG_H_ */ diff --git a/libslirp/src/dhcpv6.c b/libslirp/src/dhcpv6.c new file mode 100644 index 000000000..77b451b91 --- /dev/null +++ b/libslirp/src/dhcpv6.c @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * SLIRP stateless DHCPv6 + * + * We only support stateless DHCPv6, e.g. for network booting. + * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details. + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ + +#include "slirp.h" +#include "dhcpv6.h" + +/* DHCPv6 message types */ +#define MSGTYPE_REPLY 7 +#define MSGTYPE_INFO_REQUEST 11 + +/* DHCPv6 option types */ +#define OPTION_CLIENTID 1 +#define OPTION_IAADDR 5 +#define OPTION_ORO 6 +#define OPTION_DNS_SERVERS 23 +#define OPTION_BOOTFILE_URL 59 + +struct requested_infos { + uint8_t *client_id; + int client_id_len; + bool want_dns; + bool want_boot_url; +}; + +/** + * Analyze the info request message sent by the client to see what data it + * provided and what it wants to have. The information is gathered in the + * "requested_infos" struct. Note that client_id (if provided) points into + * the odata region, thus the caller must keep odata valid as long as it + * needs to access the requested_infos struct. + */ +static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen, + struct requested_infos *ri) +{ + int i, req_opt; + + while (olen > 4) { + /* Parse one option */ + int option = odata[0] << 8 | odata[1]; + int len = odata[2] << 8 | odata[3]; + + if (len + 4 > olen) { + slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", + slirp->opaque); + return -E2BIG; + } + + switch (option) { + case OPTION_IAADDR: + /* According to RFC3315, we must discard requests with IA option */ + return -EINVAL; + case OPTION_CLIENTID: + if (len > 256) { + /* Avoid very long IDs which could cause problems later */ + return -E2BIG; + } + ri->client_id = odata + 4; + ri->client_id_len = len; + break; + case OPTION_ORO: /* Option request option */ + if (len & 1) { + return -EINVAL; + } + /* Check which options the client wants to have */ + for (i = 0; i < len; i += 2) { + req_opt = odata[4 + i] << 8 | odata[4 + i + 1]; + switch (req_opt) { + case OPTION_DNS_SERVERS: + ri->want_dns = true; + break; + case OPTION_BOOTFILE_URL: + ri->want_boot_url = true; + break; + default: + DEBUG_MISC("dhcpv6: Unsupported option request %d", + req_opt); + } + } + break; + default: + DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d", option, + len); + } + + odata += len + 4; + olen -= len + 4; + } + + return 0; +} + + +/** + * Handle information request messages + */ +static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas, + uint32_t xid, uint8_t *odata, int olen) +{ + struct requested_infos ri = { NULL }; + struct sockaddr_in6 sa6, da6; + struct mbuf *m; + uint8_t *resp; + + if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) { + return; + } + + m = m_get(slirp); + if (!m) { + return; + } + memset(m->m_data, 0, m->m_size); + m->m_data += IF_MAXLINKHDR; + resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr); + + /* Fill in response */ + *resp++ = MSGTYPE_REPLY; + *resp++ = (uint8_t)(xid >> 16); + *resp++ = (uint8_t)(xid >> 8); + *resp++ = (uint8_t)xid; + + if (ri.client_id) { + *resp++ = OPTION_CLIENTID >> 8; /* option-code high byte */ + *resp++ = OPTION_CLIENTID; /* option-code low byte */ + *resp++ = ri.client_id_len >> 8; /* option-len high byte */ + *resp++ = ri.client_id_len; /* option-len low byte */ + memcpy(resp, ri.client_id, ri.client_id_len); + resp += ri.client_id_len; + } + if (ri.want_dns) { + *resp++ = OPTION_DNS_SERVERS >> 8; /* option-code high byte */ + *resp++ = OPTION_DNS_SERVERS; /* option-code low byte */ + *resp++ = 0; /* option-len high byte */ + *resp++ = 16; /* option-len low byte */ + memcpy(resp, &slirp->vnameserver_addr6, 16); + resp += 16; + } + if (ri.want_boot_url) { + uint8_t *sa = slirp->vhost_addr6.s6_addr; + int slen, smaxlen; + + *resp++ = OPTION_BOOTFILE_URL >> 8; /* option-code high byte */ + *resp++ = OPTION_BOOTFILE_URL; /* option-code low byte */ + smaxlen = (uint8_t *)m->m_data + slirp->if_mtu - (resp + 2); + slen = slirp_fmt((char *)resp + 2, smaxlen, + "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:" + "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7], + sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14], + sa[15], slirp->bootp_filename); + *resp++ = slen >> 8; /* option-len high byte */ + *resp++ = slen; /* option-len low byte */ + resp += slen; + } + + sa6.sin6_addr = slirp->vhost_addr6; + sa6.sin6_port = DHCPV6_SERVER_PORT; + da6.sin6_addr = srcsas->sin6_addr; + da6.sin6_port = srcsas->sin6_port; + m->m_data += sizeof(struct ip6) + sizeof(struct udphdr); + m->m_len = resp - (uint8_t *)m->m_data; + udp6_output(NULL, m, &sa6, &da6); +} + +/** + * Handle DHCPv6 messages sent by the client + */ +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m) +{ + uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr); + int data_len = m->m_len - sizeof(struct udphdr); + uint32_t xid; + + if (data_len < 4) { + return; + } + + xid = ntohl(*(uint32_t *)data) & 0xffffff; + + switch (data[0]) { + case MSGTYPE_INFO_REQUEST: + dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4); + break; + default: + DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]); + } +} diff --git a/libslirp/src/dhcpv6.h b/libslirp/src/dhcpv6.h new file mode 100644 index 000000000..c0b4a248c --- /dev/null +++ b/libslirp/src/dhcpv6.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Definitions and prototypes for SLIRP stateless DHCPv6 + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +#ifndef SLIRP_DHCPV6_H +#define SLIRP_DHCPV6_H + +#define DHCPV6_SERVER_PORT 547 + +#define ALLDHCP_MULTICAST \ + { \ + .s6_addr = { \ + 0xff, \ + 0x02, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x01, \ + 0x00, \ + 0x02 \ + } \ + } + +#define in6_dhcp_multicast(a) in6_equal(a, &(struct in6_addr)ALLDHCP_MULTICAST) + +/* Process a DHCPv6 packet from the guest */ +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m); + +#endif diff --git a/slirp/dnssearch.c b/libslirp/src/dnssearch.c similarity index 83% rename from slirp/dnssearch.c rename to libslirp/src/dnssearch.c index 71d2f3827..cbd1a197a 100644 --- a/slirp/dnssearch.c +++ b/libslirp/src/dnssearch.c @@ -1,314 +1,306 @@ -/* - * Domain search option for DHCP (RFC 3397) - * - * Copyright (c) 2012 Klaus Stengel - * - * 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 -#include -#include -#include -#include "slirp.h" - -static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119; -static const uint8_t MAX_OPT_LEN = 255; -static const uint8_t OPT_HEADER_LEN = 2; -static const uint8_t REFERENCE_LEN = 2; - -struct compact_domain; - -typedef struct compact_domain { - struct compact_domain *self; - struct compact_domain *refdom; - uint8_t *labels; - size_t len; - size_t common_octets; -} CompactDomain; - -static size_t -domain_suffix_diffoff(const CompactDomain *a, const CompactDomain *b) -{ - size_t la = a->len, lb = b->len; - uint8_t *da = a->labels + la, *db = b->labels + lb; - size_t i, lm = (la < lb) ? la : lb; - - for (i = 0; i < lm; i++) { - da--; db--; - if (*da != *db) { - break; - } - } - return i; -} - -static int domain_suffix_ord(const void *cva, const void *cvb) -{ - const CompactDomain *a = (const CompactDomain *)cva, *b = (const CompactDomain *)cvb; - size_t la = a->len, lb = b->len; - size_t doff = domain_suffix_diffoff(a, b); - uint8_t ca = a->labels[la - doff]; - uint8_t cb = b->labels[lb - doff]; - - if (ca < cb) { - return -1; - } - if (ca > cb) { - return 1; - } - if (la < lb) { - return -1; - } - if (la > lb) { - return 1; - } - return 0; -} - -static size_t domain_common_label(CompactDomain *a, CompactDomain *b) -{ - size_t res, doff = domain_suffix_diffoff(a, b); - uint8_t *first_eq_pos = a->labels + (a->len - doff); - uint8_t *label = a->labels; - - while (*label && label < first_eq_pos) { - label += *label + 1; - } - res = a->len - (label - a->labels); - /* only report if it can help to reduce the packet size */ - return (res > REFERENCE_LEN) ? res : 0; -} - -static void domain_fixup_order(CompactDomain *cd, size_t n) -{ - size_t i; - - for (i = 0; i < n; i++) { - CompactDomain *cur = cd + i, *next = cd[i].self; - - while (!cur->common_octets) { - CompactDomain *tmp = next->self; /* backup target value */ - - next->self = cur; - cur->common_octets++; - - cur = next; - next = tmp; - } - } -} - -static void domain_mklabels(CompactDomain *cd, const char *input) -{ - uint8_t *len_marker = cd->labels; - uint8_t *output = len_marker; /* pre-incremented */ - const char *in = input; - char cur_chr; - size_t len = 0; - - if (cd->len == 0) { - goto fail; - } - cd->len++; - - do { - cur_chr = *in++; - if (cur_chr == '.' || cur_chr == '\0') { - len = output - len_marker; - if ((len == 0 && cur_chr == '.') || len >= 64) { - goto fail; - } - *len_marker = (uint8_t)len; - - output++; - len_marker = output; - } else { - output++; - *output = cur_chr; - } - } while (cur_chr != '\0'); - - /* ensure proper zero-termination */ - if (len != 0) { - *len_marker = 0; - cd->len++; - } - return; - -fail: - g_warning("failed to parse domain name '%s'\n", input); - cd->len = 0; -} - -static void -domain_mkxrefs(CompactDomain *doms, CompactDomain *last, size_t depth) -{ - CompactDomain *i = doms, *target = doms; - - do { - if (i->labels < target->labels) { - target = i; - } - } while (i++ != last); - - for (i = doms; i != last; i++) { - CompactDomain *group_last; - size_t next_depth; - - if (i->common_octets == depth) { - continue; - } - - next_depth = -1; - for (group_last = i; group_last != last; group_last++) { - size_t co = group_last->common_octets; - if (co <= depth) { - break; - } - if (co < next_depth) { - next_depth = co; - } - } - domain_mkxrefs(i, group_last, next_depth); - - i = group_last; - if (i == last) { - break; - } - } - - if (depth == 0) { - return; - } - - i = doms; - do { - if (i != target && i->refdom == NULL) { - i->refdom = target; - i->common_octets = depth; - } - } while (i++ != last); -} - -static size_t domain_compactify(CompactDomain *domains, size_t n) -{ - uint8_t *start = domains->self->labels, *outptr = start; - size_t i; - - for (i = 0; i < n; i++) { - CompactDomain *cd = domains[i].self; - CompactDomain *rd = cd->refdom; - - if (rd != NULL) { - size_t moff = (rd->labels - start) - + (rd->len - cd->common_octets); - if (moff < 0x3FFFu) { - cd->len -= cd->common_octets - 2; - cd->labels[cd->len - 1] = moff & 0xFFu; - cd->labels[cd->len - 2] = (uint8_t)(0xC0u | (moff >> 8)); - } - } - - if (cd->labels != outptr) { - memmove(outptr, cd->labels, cd->len); - cd->labels = outptr; - } - outptr += cd->len; - } - return outptr - start; -} - -int translate_dnssearch(Slirp *s, const char **names) -{ - size_t blocks, bsrc_start, bsrc_end, bdst_start; - size_t i, num_domains, memreq = 0; - uint8_t *result = NULL, *outptr; - CompactDomain *domains = NULL; - const char **nameptr = names; - - while (*nameptr != NULL) { - nameptr++; - } - - num_domains = nameptr - names; - if (num_domains == 0) { - return -2; - } - - domains = (CompactDomain *)g_malloc(num_domains * sizeof(*domains)); - - for (i = 0; i < num_domains; i++) { - size_t nlen = strlen(names[i]); - memreq += nlen + 2; /* 1 zero octet + 1 label length octet */ - domains[i].self = domains + i; - domains[i].len = nlen; - domains[i].common_octets = 0; - domains[i].refdom = NULL; - } - - /* reserve extra 2 header bytes for each 255 bytes of output */ - memreq += ((memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN) * OPT_HEADER_LEN; - result = (uint8_t *)g_malloc(memreq * sizeof(*result)); - - outptr = result; - for (i = 0; i < num_domains; i++) { - domains[i].labels = outptr; - domain_mklabels(domains + i, names[i]); - outptr += domains[i].len; - } - - if (outptr == result) { - g_free(domains); - g_free(result); - return -1; - } - - qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord); - domain_fixup_order(domains, num_domains); - - for (i = 1; i < num_domains; i++) { - size_t cl = domain_common_label(domains + i - 1, domains + i); - domains[i - 1].common_octets = cl; - } - - domain_mkxrefs(domains, domains + num_domains - 1, 0); - memreq = domain_compactify(domains, num_domains); - - blocks = (memreq + MAX_OPT_LEN - 1) / MAX_OPT_LEN; - bsrc_end = memreq; - bsrc_start = (blocks - 1) * MAX_OPT_LEN; - bdst_start = bsrc_start + blocks * OPT_HEADER_LEN; - memreq += blocks * OPT_HEADER_LEN; - - while (blocks--) { - size_t len = bsrc_end - bsrc_start; - memmove(result + bdst_start, result + bsrc_start, len); - result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH; - result[bdst_start - 1] = (uint8_t)len; - bsrc_end = bsrc_start; - bsrc_start -= MAX_OPT_LEN; - bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN; - } - - g_free(domains); - s->vdnssearch = result; - s->vdnssearch_len = memreq; - return 0; -} +/* SPDX-License-Identifier: MIT */ +/* + * Domain search option for DHCP (RFC 3397) + * + * Copyright (c) 2012 Klaus Stengel + * + * 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" + +static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119; +static const uint8_t MAX_OPT_LEN = 255; +static const uint8_t OPT_HEADER_LEN = 2; +static const uint8_t REFERENCE_LEN = 2; + +struct compact_domain; + +typedef struct compact_domain { + struct compact_domain *self; + struct compact_domain *refdom; + uint8_t *labels; + size_t len; + size_t common_octets; +} CompactDomain; + +static size_t domain_suffix_diffoff(const CompactDomain *a, + const CompactDomain *b) +{ + size_t la = a->len, lb = b->len; + uint8_t *da = a->labels + la, *db = b->labels + lb; + size_t i, lm = (la < lb) ? la : lb; + + for (i = 0; i < lm; i++) { + da--; + db--; + if (*da != *db) { + break; + } + } + return i; +} + +static int domain_suffix_ord(const void *cva, const void *cvb) +{ + const CompactDomain *a = cva, *b = cvb; + size_t la = a->len, lb = b->len; + size_t doff = domain_suffix_diffoff(a, b); + uint8_t ca = a->labels[la - doff]; + uint8_t cb = b->labels[lb - doff]; + + if (ca < cb) { + return -1; + } + if (ca > cb) { + return 1; + } + if (la < lb) { + return -1; + } + if (la > lb) { + return 1; + } + return 0; +} + +static size_t domain_common_label(CompactDomain *a, CompactDomain *b) +{ + size_t res, doff = domain_suffix_diffoff(a, b); + uint8_t *first_eq_pos = a->labels + (a->len - doff); + uint8_t *label = a->labels; + + while (*label && label < first_eq_pos) { + label += *label + 1; + } + res = a->len - (label - a->labels); + /* only report if it can help to reduce the packet size */ + return (res > REFERENCE_LEN) ? res : 0; +} + +static void domain_fixup_order(CompactDomain *cd, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + CompactDomain *cur = cd + i, *next = cd[i].self; + + while (!cur->common_octets) { + CompactDomain *tmp = next->self; /* backup target value */ + + next->self = cur; + cur->common_octets++; + + cur = next; + next = tmp; + } + } +} + +static void domain_mklabels(CompactDomain *cd, const char *input) +{ + uint8_t *len_marker = cd->labels; + uint8_t *output = len_marker; /* pre-incremented */ + const char *in = input; + char cur_chr; + size_t len = 0; + + if (cd->len == 0) { + goto fail; + } + cd->len++; + + do { + cur_chr = *in++; + if (cur_chr == '.' || cur_chr == '\0') { + len = output - len_marker; + if ((len == 0 && cur_chr == '.') || len >= 64) { + goto fail; + } + *len_marker = len; + + output++; + len_marker = output; + } else { + output++; + *output = cur_chr; + } + } while (cur_chr != '\0'); + + /* ensure proper zero-termination */ + if (len != 0) { + *len_marker = 0; + cd->len++; + } + return; + +fail: + g_warning("failed to parse domain name '%s'\n", input); + cd->len = 0; +} + +static void domain_mkxrefs(CompactDomain *doms, CompactDomain *last, + size_t depth) +{ + CompactDomain *i = doms, *target = doms; + + do { + if (i->labels < target->labels) { + target = i; + } + } while (i++ != last); + + for (i = doms; i != last; i++) { + CompactDomain *group_last; + size_t next_depth; + + if (i->common_octets == depth) { + continue; + } + + next_depth = -1; + for (group_last = i; group_last != last; group_last++) { + size_t co = group_last->common_octets; + if (co <= depth) { + break; + } + if (co < next_depth) { + next_depth = co; + } + } + domain_mkxrefs(i, group_last, next_depth); + + i = group_last; + if (i == last) { + break; + } + } + + if (depth == 0) { + return; + } + + i = doms; + do { + if (i != target && i->refdom == NULL) { + i->refdom = target; + i->common_octets = depth; + } + } while (i++ != last); +} + +static size_t domain_compactify(CompactDomain *domains, size_t n) +{ + uint8_t *start = domains->self->labels, *outptr = start; + size_t i; + + for (i = 0; i < n; i++) { + CompactDomain *cd = domains[i].self; + CompactDomain *rd = cd->refdom; + + if (rd != NULL) { + size_t moff = (rd->labels - start) + (rd->len - cd->common_octets); + if (moff < 0x3FFFu) { + cd->len -= cd->common_octets - 2; + cd->labels[cd->len - 1] = moff & 0xFFu; + cd->labels[cd->len - 2] = 0xC0u | (moff >> 8); + } + } + + if (cd->labels != outptr) { + memmove(outptr, cd->labels, cd->len); + cd->labels = outptr; + } + outptr += cd->len; + } + return outptr - start; +} + +int translate_dnssearch(Slirp *s, const char **names) +{ + size_t blocks, bsrc_start, bsrc_end, bdst_start; + size_t i, num_domains, memreq = 0; + uint8_t *result = NULL, *outptr; + CompactDomain *domains = NULL; + + num_domains = g_strv_length((GStrv)(void *)names); + if (num_domains == 0) { + return -2; + } + + domains = g_malloc(num_domains * sizeof(*domains)); + + for (i = 0; i < num_domains; i++) { + size_t nlen = strlen(names[i]); + memreq += nlen + 2; /* 1 zero octet + 1 label length octet */ + domains[i].self = domains + i; + domains[i].len = nlen; + domains[i].common_octets = 0; + domains[i].refdom = NULL; + } + + /* reserve extra 2 header bytes for each 255 bytes of output */ + memreq += DIV_ROUND_UP(memreq, MAX_OPT_LEN) * OPT_HEADER_LEN; + result = g_malloc(memreq * sizeof(*result)); + + outptr = result; + for (i = 0; i < num_domains; i++) { + domains[i].labels = outptr; + domain_mklabels(domains + i, names[i]); + if (domains[i].len == 0) { + /* Bogus entry, reject it all */ + g_free(domains); + g_free(result); + return -1; + } + outptr += domains[i].len; + } + + qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord); + domain_fixup_order(domains, num_domains); + + for (i = 1; i < num_domains; i++) { + size_t cl = domain_common_label(domains + i - 1, domains + i); + domains[i - 1].common_octets = cl; + } + + domain_mkxrefs(domains, domains + num_domains - 1, 0); + memreq = domain_compactify(domains, num_domains); + + blocks = DIV_ROUND_UP(memreq, MAX_OPT_LEN); + bsrc_end = memreq; + bsrc_start = (blocks - 1) * MAX_OPT_LEN; + bdst_start = bsrc_start + blocks * OPT_HEADER_LEN; + memreq += blocks * OPT_HEADER_LEN; + + while (blocks--) { + size_t len = bsrc_end - bsrc_start; + memmove(result + bdst_start, result + bsrc_start, len); + result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH; + result[bdst_start - 1] = len; + bsrc_end = bsrc_start; + bsrc_start -= MAX_OPT_LEN; + bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN; + } + + g_free(domains); + s->vdnssearch = result; + s->vdnssearch_len = memreq; + return 0; +} diff --git a/libslirp/src/if.c b/libslirp/src/if.c new file mode 100644 index 000000000..c49a64ce1 --- /dev/null +++ b/libslirp/src/if.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#include "slirp.h" + +static void ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead) +{ + ifm->m_nextpkt = ifmhead->m_nextpkt; + ifmhead->m_nextpkt = ifm; + ifm->m_prevpkt = ifmhead; + ifm->m_nextpkt->m_prevpkt = ifm; +} + +void if_init(Slirp *slirp) +{ + slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq; + slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq; +} + +/* + * 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 mbufs, 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(struct socket *so, struct mbuf *ifm) +{ + Slirp *slirp = ifm->slirp; + M_DUP_DEBUG(slirp, ifm, 0, 0); + + struct mbuf *ifq; + int on_fastq = 1; + + DEBUG_CALL("if_output"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("ifm = %p", ifm); + + /* + * First remove the mbuf 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) { + slirp_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? + */ + if (so) { + for (ifq = (struct mbuf *)slirp->if_batchq.qh_rlink; + (struct slirp_quehead *)ifq != &slirp->if_batchq; + ifq = ifq->m_prev) { + if (so == ifq->m_so) { + /* A match! */ + ifm->m_so = so; + ifs_insque(ifm, ifq->m_prevpkt); + goto diddit; + } + } + } + + /* No match, check which queue to put it on */ + if (so && (so->so_iptos & IPTOS_LOWDELAY)) { + ifq = (struct mbuf *)slirp->if_fastq.qh_rlink; + on_fastq = 1; + /* + * Check if this packet is a part of the last + * packet's session + */ + if (ifq->m_so == so) { + ifm->m_so = so; + ifs_insque(ifm, ifq->m_prevpkt); + goto diddit; + } + } else { + ifq = (struct mbuf *)slirp->if_batchq.qh_rlink; + } + + /* Create a new doubly linked list for this session */ + ifm->m_so = so; + ifs_init(ifm); + slirp_insque(ifm, ifq); + +diddit: + 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... */ + slirp_remque(ifm->m_nextpkt); + + /* ...And insert in the new. That'll teach ya! */ + slirp_insque(ifm->m_nextpkt, &slirp->if_batchq); + } + } + + /* + * This prevents us from malloc()ing too many mbufs + */ + if_start(ifm->slirp); +} + +void if_start(Slirp *slirp) +{ + uint64_t now = slirp->cb->clock_get_ns(slirp->opaque); + bool from_batchq = false; + struct mbuf *ifm, *ifm_next, *ifqt; + + DEBUG_VERBOSE_CALL("if_start"); + + if (slirp->if_start_busy) { + return; + } + slirp->if_start_busy = true; + + struct mbuf *batch_head = NULL; + if (slirp->if_batchq.qh_link != &slirp->if_batchq) { + batch_head = (struct mbuf *)slirp->if_batchq.qh_link; + } + + if (slirp->if_fastq.qh_link != &slirp->if_fastq) { + ifm_next = (struct mbuf *)slirp->if_fastq.qh_link; + } else if (batch_head) { + /* Nothing on fastq, pick up from batchq */ + ifm_next = batch_head; + from_batchq = true; + } else { + ifm_next = NULL; + } + + while (ifm_next) { + ifm = ifm_next; + + ifm_next = ifm->m_next; + if ((struct slirp_quehead *)ifm_next == &slirp->if_fastq) { + /* No more packets in fastq, switch to batchq */ + ifm_next = batch_head; + from_batchq = true; + } + if ((struct slirp_quehead *)ifm_next == &slirp->if_batchq) { + /* end of batchq */ + ifm_next = NULL; + } + + /* Try to send packet unless it already expired */ + if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) { + /* Packet is delayed due to pending ARP or NDP resolution */ + continue; + } + + /* Remove it from the queue */ + ifqt = ifm->m_prev; + slirp_remque(ifm); + + /* If there are more packets for this session, re-queue them */ + if (ifm->m_nextpkt != ifm) { + struct mbuf *next = ifm->m_nextpkt; + + slirp_insque(next, ifqt); + ifs_remque(ifm); + if (!from_batchq) { + ifm_next = next; + } + } + + /* Update so_queued */ + if (ifm->m_so && --ifm->m_so->so_queued == 0) { + /* If there's no more queued, reset nqueued */ + ifm->m_so->so_nqueued = 0; + } + + m_free(ifm); + } + + slirp->if_start_busy = false; +} diff --git a/libslirp/src/if.h b/libslirp/src/if.h new file mode 100644 index 000000000..7cf9d2750 --- /dev/null +++ b/libslirp/src/if.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#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 */ + +#define IF_MTU_DEFAULT 1500 +#define IF_MTU_MIN 68 +#define IF_MTU_MAX 65521 +#define IF_MRU_DEFAULT 1500 +#define IF_MRU_MIN 68 +#define IF_MRU_MAX 65521 +#define IF_COMP IF_AUTOCOMP /* Flags for compression */ + +/* 2 for alignment, 14 for ethernet */ +#define IF_MAXLINKHDR (2 + ETH_HLEN) + +#endif diff --git a/libslirp/src/ip.h b/libslirp/src/ip.h new file mode 100644 index 000000000..f0859f0cf --- /dev/null +++ b/libslirp/src/ip.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 + +#include + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#undef NTOHL +#undef NTOHS +#undef HTONL +#undef HTONS +#define NTOHL(d) +#define NTOHS(d) +#define HTONL(d) +#define HTONS(d) +#else +#ifndef NTOHL +#define NTOHL(d) ((d) = ntohl((d))) +#endif +#ifndef NTOHS +#define NTOHS(d) ((d) = ntohs((uint16_t)(d))) +#endif +#ifndef HTONL +#define HTONL(d) ((d) = htonl((d))) +#endif +#ifndef HTONS +#define HTONS(d) ((d) = htons((uint16_t)(d))) +#endif +#endif + +typedef uint32_t n_long; /* long as received from the net */ + +/* + * Definitions for internet protocol version 4. + * Per RFC 791, September 1981. + */ +#define IPVERSION 4 + +/* + * Structure of an internet header, naked of options. + */ +SLIRP_PACKED_BEGIN +struct ip { +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t ip_v : 4, /* version */ + ip_hl : 4; /* header length */ +#else + uint8_t ip_hl : 4, /* header length */ + ip_v : 4; /* version */ +#endif + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_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 */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +} SLIRP_PACKED_END; + +#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. + */ +SLIRP_PACKED_BEGIN +struct ip_timestamp { + uint8_t ipt_code; /* IPOPT_TS */ + uint8_t ipt_len; /* size of structure (variable) */ + uint8_t ipt_ptr; /* index of current entry */ +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t ipt_oflw : 4, /* overflow counter */ + ipt_flg : 4; /* flags, see below */ +#else + uint8_t 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; +} SLIRP_PACKED_END; + +/* 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 */ + +#if GLIB_SIZEOF_VOID_P == 4 +SLIRP_PACKED_BEGIN +struct mbuf_ptr { + struct mbuf *mptr; + uint32_t dummy; +} SLIRP_PACKED_END; +#else +SLIRP_PACKED_BEGIN +struct mbuf_ptr { + struct mbuf *mptr; +} SLIRP_PACKED_END; +#endif +struct qlink { + void *next, *prev; +}; + +/* + * Overlay for ip header used by other protocols (tcp, udp). + */ +SLIRP_PACKED_BEGIN +struct ipovly { + struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + uint16_t ih_len; /* protocol length */ + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ +} SLIRP_PACKED_END; + +/* + * 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. + */ +struct ipq { + struct qlink ip_link; /* to other reass headers */ + uint8_t ipq_ttl; /* time for reass q to live */ + uint8_t ipq_p; /* protocol of this fragment */ + uint16_t ipq_id; /* sequence id for reassembly */ + struct in_addr ipq_src, ipq_dst; +}; + +struct ipas { + struct qlink link; + union { + struct ipq ipq; + struct ip ipf_ip; + }; +}; + +#define ipf_off ipf_ip.ip_off +#define ipf_tos ipf_ip.ip_tos +#define ipf_len ipf_ip.ip_len + +#endif diff --git a/libslirp/src/ip6.h b/libslirp/src/ip6.h new file mode 100644 index 000000000..50765e6af --- /dev/null +++ b/libslirp/src/ip6.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#ifndef SLIRP_IP6_H +#define SLIRP_IP6_H + +#include +#include + +#include "util.h" + +#define ALLNODES_MULTICAST \ + { \ + .s6_addr = { \ + 0xff, \ + 0x02, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x01 \ + } \ + } + +#define SOLICITED_NODE_PREFIX \ + { \ + .s6_addr = { \ + 0xff, \ + 0x02, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x01, \ + 0xff, \ + 0x00, \ + 0x00, \ + 0x00 \ + } \ + } + +#define LINKLOCAL_ADDR \ + { \ + .s6_addr = { \ + 0xfe, \ + 0x80, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x02 \ + } \ + } + +#define ZERO_ADDR \ + { \ + .s6_addr = { \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00, \ + 0x00 \ + } \ + } + +/* Check that two IPv6 addresses are equal */ +static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b) +{ + return memcmp(a, b, sizeof(*a)) == 0; +} + +/* Check that two IPv6 addresses are equal in their network part */ +static inline bool in6_equal_net(const struct in6_addr *a, + const struct in6_addr *b, int prefix_len) +{ + if (memcmp(a, b, prefix_len / 8) != 0) { + return 0; + } + + if (prefix_len % 8 == 0) { + return 1; + } + + return a->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)) == + b->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)); +} + +/* Check that two IPv6 addresses are equal in their machine part */ +static inline bool in6_equal_mach(const struct in6_addr *a, + const struct in6_addr *b, int prefix_len) +{ + if (memcmp(&(a->s6_addr[DIV_ROUND_UP(prefix_len, 8)]), + &(b->s6_addr[DIV_ROUND_UP(prefix_len, 8)]), + 16 - DIV_ROUND_UP(prefix_len, 8)) != 0) { + return 0; + } + + if (prefix_len % 8 == 0) { + return 1; + } + + return (a->s6_addr[prefix_len / 8] & + ((1U << (8 - (prefix_len % 8))) - 1)) == + (b->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1)); +} + +/* Check that the IPv6 is equal to the virtual router */ +#define in6_equal_router(a) \ + ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len) && \ + in6_equal_mach(a, &slirp->vhost_addr6, slirp->vprefix_len)) || \ + (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64) && \ + in6_equal_mach(a, &slirp->vhost_addr6, 64))) + +/* Check that the IPv6 is equal to the virtual DNS server */ +#define in6_equal_dns(a) \ + ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len) && \ + in6_equal_mach(a, &slirp->vnameserver_addr6, slirp->vprefix_len)) || \ + (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64) && \ + in6_equal_mach(a, &slirp->vnameserver_addr6, 64))) + +/* Check that the IPv6 is equal to the host */ +#define in6_equal_host(a) (in6_equal_router(a) || in6_equal_dns(a)) + +/* Check that the IPv6 is within the sollicited node multicast network */ +#define in6_solicitednode_multicast(a) \ + (in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104)) + +/* Check that the IPv6 is zero */ +#define in6_zero(a) (in6_equal(a, &(struct in6_addr)ZERO_ADDR)) + +/* Compute emulated host MAC address from its ipv6 address */ +static inline void in6_compute_ethaddr(struct in6_addr ip, + uint8_t eth[ETH_ALEN]) +{ + eth[0] = 0x52; + eth[1] = 0x56; + memcpy(ð[2], &ip.s6_addr[16 - (ETH_ALEN - 2)], ETH_ALEN - 2); +} + +/* + * Definitions for internet protocol version 6. + * Per RFC 2460, December 1998. + */ +#define IP6VERSION 6 +#define IP6_HOP_LIMIT 255 + +/* + * Structure of an internet header, naked of options. + */ +struct ip6 { +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t ip_v : 4, /* version */ + ip_tc_hi : 4; /* traffic class */ + uint8_t ip_tc_lo : 4, ip_fl_hi : 4; /* flow label */ +#else + uint8_t ip_tc_hi : 4, ip_v : 4; + uint8_t ip_fl_hi : 4, ip_tc_lo : 4; +#endif + uint16_t ip_fl_lo; + uint16_t ip_pl; /* payload length */ + uint8_t ip_nh; /* next header */ + uint8_t ip_hl; /* hop limit */ + struct in6_addr ip_src, ip_dst; /* source and dest address */ +}; + +/* + * IPv6 pseudo-header used by upper-layer protocols + */ +struct ip6_pseudohdr { + struct in6_addr ih_src; /* source internet address */ + struct in6_addr ih_dst; /* destination internet address */ + uint32_t ih_pl; /* upper-layer packet length */ + uint16_t ih_zero_hi; /* zero */ + uint8_t ih_zero_lo; /* zero */ + uint8_t ih_nh; /* next header */ +}; + +/* + * We don't want to mark these ip6 structs as packed as they are naturally + * correctly aligned; instead assert that there is no stray padding. + * If we marked the struct as packed then we would be unable to take + * the address of any of the fields in it. + */ +G_STATIC_ASSERT(sizeof(struct ip6) == 40); +G_STATIC_ASSERT(sizeof(struct ip6_pseudohdr) == 40); + +#endif diff --git a/libslirp/src/ip6_icmp.c b/libslirp/src/ip6_icmp.c new file mode 100644 index 000000000..21224bc12 --- /dev/null +++ b/libslirp/src/ip6_icmp.c @@ -0,0 +1,652 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "slirp.h" +#include "ip6_icmp.h" + +#define NDP_Interval \ + g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) + +/* The message sent when emulating PING */ +/* Be nice and tell them it's just a pseudo-ping packet */ +static const char icmp6_ping_msg[] = + "This is a pseudo-PING packet used by Slirp to emulate ICMPV6 ECHO-REQUEST " + "packets.\n"; + +void icmp6_post_init(Slirp *slirp) +{ + if (!slirp->in6_enabled) { + return; + } + + slirp->ra_timer = + slirp_timer_new(slirp, SLIRP_TIMER_RA, NULL); + slirp->cb->timer_mod(slirp->ra_timer, + slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + + NDP_Interval, + slirp->opaque); +} + +void icmp6_cleanup(Slirp *slirp) +{ + if (!slirp->in6_enabled) { + return; + } + + slirp->cb->timer_free(slirp->ra_timer, slirp->opaque); +} + +/* Send ICMP packet to the Internet, and save it to so_m */ +static int icmp6_send(struct socket *so, struct mbuf *m, int hlen) +{ + Slirp *slirp = m->slirp; + + struct sockaddr_in6 addr; + + /* + * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent + * between host OSes. On Linux, only the ICMP header and payload is + * included. On macOS/Darwin, the socket acts like a raw socket and + * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP + * sockets aren't supported at all, so we treat them like raw sockets. It + * isn't possible to detect this difference at runtime, so we must use an + * #ifdef to determine if we need to remove the IP header. + */ +#if defined(BSD) && !defined(__GNU__) + so->so_type = IPPROTO_IPV6; +#else + so->so_type = IPPROTO_ICMPV6; +#endif + + so->s = slirp_socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); + if (not_valid_socket(so->s)) { + if (errno == EAFNOSUPPORT + || errno == EPROTONOSUPPORT + || errno == EACCES) { + /* Kernel doesn't support or allow ping sockets. */ + so->so_type = IPPROTO_IPV6; + so->s = slirp_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + } + } + if (not_valid_socket(so->s)) { + return -1; + } + slirp_register_poll_socket(so); + + if (slirp_bind_outbound(so, AF_INET6) != 0) { + // bind failed - close socket + closesocket(so->s); + so->s = -1; + return -1; + } + + M_DUP_DEBUG(slirp, m, 0, 0); + struct ip6 *ip = mtod(m, struct ip6 *); + + so->so_m = m; + so->so_faddr6 = ip->ip_dst; + so->so_laddr6 = ip->ip_src; + so->so_state = SS_ISFCONNECTED; + so->so_expire = curtime + SO_EXPIRE; + + addr.sin6_family = AF_INET6; + addr.sin6_addr = so->so_faddr6; + + slirp_insque(so, &so->slirp->icmp); + + if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC("icmp6_input icmp sendto tx errno = %d-%s", errno, + g_strerror(errno)); + icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); + icmp_detach(so); + } + + return 0; +} + +static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip, + struct icmp6 *icmp) +{ + struct mbuf *t = m_get(slirp); + t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl); + memcpy(t->m_data, m->m_data, t->m_len); + + /* IPv6 Packet */ + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_dst = ip->ip_src; + rip->ip_src = ip->ip_dst; + + /* ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_ECHO_REPLY; + ricmp->icmp6_cksum = 0; + + /* Checksum */ + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src) +{ + Slirp *slirp = m->slirp; + struct mbuf *t; + struct ip6 *ip = mtod(m, struct ip6 *); + char addrstr[INET6_ADDRSTRLEN]; + + DEBUG_CALL("icmp6_send_error"); + DEBUG_ARG("type = %d, code = %d", type, code); + + if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || in6_zero(&ip->ip_src)) { + /* TODO icmp error? */ + return; + } + + t = m_get(slirp); + + /* IPv6 packet */ + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = *src; + rip->ip_dst = ip->ip_src; + inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN); + DEBUG_ARG("target = %s", addrstr); + + rip->ip_nh = IPPROTO_ICMPV6; + const int error_data_len = MIN( + m->m_len, slirp->if_mtu - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN)); + rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = type; + ricmp->icmp6_code = code; + ricmp->icmp6_cksum = 0; + + switch (type) { + case ICMP6_UNREACH: + case ICMP6_TIMXCEED: + ricmp->icmp6_err.unused = 0; + break; + case ICMP6_TOOBIG: + ricmp->icmp6_err.mtu = htonl(slirp->if_mtu); + break; + case ICMP6_PARAMPROB: + /* TODO: Handle this case */ + break; + default: + g_assert_not_reached(); + } + t->m_data += ICMP6_ERROR_MINLEN; + memcpy(t->m_data, m->m_data, error_data_len); + + /* Checksum */ + t->m_data -= ICMP6_ERROR_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code) +{ + struct in6_addr src = LINKLOCAL_ADDR; + icmp6_forward_error(m, type, code, &src); +} + +/* + * Reflect the ip packet back to the source + */ +void icmp6_reflect(struct mbuf *m) +{ + register struct ip6 *ip = mtod(m, struct ip6 *); + int hlen = sizeof(struct ip6); + register struct icmp6 *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 icmp6 *); + + icp->icmp6_type = ICMP6_ECHO_REPLY; + + m->m_data -= hlen; + m->m_len += hlen; + + icp->icmp6_cksum = 0; + icp->icmp6_cksum = ip6_cksum(m); + + ip->ip_hl = MAXTTL; + { /* swap */ + struct in6_addr icmp_dst; + icmp_dst = ip->ip_dst; + ip->ip_dst = ip->ip_src; + ip->ip_src = icmp_dst; + } + + ip6_output((struct socket *)NULL, m, 0); +} + +void icmp6_receive(struct socket *so) +{ + struct mbuf *m = so->so_m; + int hlen = sizeof(struct ip6); + uint8_t error_code; + struct icmp6 *icp; + int id, seq, len; + + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp6 *); + + id = icp->icmp6_id; + seq = icp->icmp6_seq; + len = recv(so->s, icp, M_ROOM(m), 0); + + icp->icmp6_id = id; + icp->icmp6_seq = seq; + + m->m_data -= hlen; + m->m_len += hlen; + + if (len == -1 || len == 0) { + if (errno == ENETUNREACH) { + error_code = ICMP6_UNREACH_NO_ROUTE; + } else { + error_code = ICMP6_UNREACH_ADDRESS; + } + DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, g_strerror(errno)); + icmp6_send_error(so->so_m, ICMP_UNREACH, error_code); + } else { + icmp6_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + icmp_detach(so); +} + +/* + * Send NDP Router Advertisement + */ +static void ndp_send_ra(Slirp *slirp) +{ + DEBUG_CALL("ndp_send_ra"); + + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + size_t pl_size = 0; + struct in6_addr addr; + uint32_t scope_id; + + rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; + rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; + rip->ip_nh = IPPROTO_ICMPV6; + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_RA; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit; + ricmp->icmp6_nra.M = NDP_AdvManagedFlag; + ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag; + ricmp->icmp6_nra.reserved = 0; + ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime); + ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime); + ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime); + t->m_data += ICMP6_NDP_RA_MINLEN; + pl_size += ICMP6_NDP_RA_MINLEN; + + /* Source link-layer address (NDP option) */ + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer); + t->m_data += NDPOPT_LINKLAYER_LEN; + pl_size += NDPOPT_LINKLAYER_LEN; + + /* Prefix information (NDP option) */ + struct ndpopt *opt2 = mtod(t, struct ndpopt *); + opt2->ndpopt_type = NDPOPT_PREFIX_INFO; + opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8; + opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len; + opt2->ndpopt_prefixinfo.L = 1; + opt2->ndpopt_prefixinfo.A = 1; + opt2->ndpopt_prefixinfo.reserved1 = 0; + opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime); + opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime); + opt2->ndpopt_prefixinfo.reserved2 = 0; + opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6; + t->m_data += NDPOPT_PREFIXINFO_LEN; + pl_size += NDPOPT_PREFIXINFO_LEN; + + /* Prefix information (NDP option) */ + if (get_dns6_addr(&addr, &scope_id) >= 0) { + /* Host system does have an IPv6 DNS server, announce our proxy. */ + struct ndpopt *opt3 = mtod(t, struct ndpopt *); + opt3->ndpopt_type = NDPOPT_RDNSS; + opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8; + opt3->ndpopt_rdnss.reserved = 0; + opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval); + opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6; + t->m_data += NDPOPT_RDNSS_LEN; + pl_size += NDPOPT_RDNSS_LEN; + } + + rip->ip_pl = htons(pl_size); + t->m_data -= sizeof(struct ip6) + pl_size; + t->m_len = sizeof(struct ip6) + pl_size; + + /* ICMPv6 Checksum */ + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +void ra_timer_handler(Slirp *slirp, void *unused) +{ + slirp->cb->timer_mod(slirp->ra_timer, + slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + + NDP_Interval, + slirp->opaque); + ndp_send_ra(slirp); +} + +/* + * Send NDP Neighbor Solitication + */ +void ndp_send_ns(Slirp *slirp, struct in6_addr addr) +{ + char addrstr[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN); + + DEBUG_CALL("ndp_send_ns"); + DEBUG_ARG("target = %s", addrstr); + + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = slirp->vhost_addr6; + rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX; + memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3); + rip->ip_nh = IPPROTO_ICMPV6; + rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_NS; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nns.reserved = 0; + ricmp->icmp6_nns.target = addr; + + /* Build NDP option */ + t->m_data += ICMP6_NDP_NS_MINLEN; + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer); + + /* ICMPv6 Checksum */ + t->m_data -= ICMP6_NDP_NA_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 1); +} + +/* + * Send NDP Neighbor Advertisement + */ +static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp) +{ + /* Build IPv6 packet */ + struct mbuf *t = m_get(slirp); + struct ip6 *rip = mtod(t, struct ip6 *); + rip->ip_src = icmp->icmp6_nns.target; + if (in6_zero(&ip->ip_src)) { + rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; + } else { + rip->ip_dst = ip->ip_src; + } + rip->ip_nh = IPPROTO_ICMPV6; + rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN + NDPOPT_LINKLAYER_LEN); + t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); + + /* Build ICMPv6 packet */ + t->m_data += sizeof(struct ip6); + struct icmp6 *ricmp = mtod(t, struct icmp6 *); + ricmp->icmp6_type = ICMP6_NDP_NA; + ricmp->icmp6_code = 0; + ricmp->icmp6_cksum = 0; + + /* NDP */ + ricmp->icmp6_nna.R = NDP_IsRouter; + ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst); + ricmp->icmp6_nna.O = 1; + ricmp->icmp6_nna.reserved_1 = 0; + ricmp->icmp6_nna.reserved_2 = 0; + ricmp->icmp6_nna.reserved_3 = 0; + ricmp->icmp6_nna.target = icmp->icmp6_nns.target; + + /* Build NDP option */ + t->m_data += ICMP6_NDP_NA_MINLEN; + struct ndpopt *opt = mtod(t, struct ndpopt *); + opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET; + opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; + in6_compute_ethaddr(ricmp->icmp6_nna.target, opt->ndpopt_linklayer); + + /* ICMPv6 Checksum */ + t->m_data -= ICMP6_NDP_NA_MINLEN; + t->m_data -= sizeof(struct ip6); + ricmp->icmp6_cksum = ip6_cksum(t); + + ip6_output(NULL, t, 0); +} + +/* + * Process a NDP message + */ +static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, + struct icmp6 *icmp) +{ + g_assert(M_ROOMBEFORE(m) >= ETH_HLEN); + + m->m_len += ETH_HLEN; + m->m_data -= ETH_HLEN; + struct ethhdr *eth = mtod(m, struct ethhdr *); + m->m_len -= ETH_HLEN; + m->m_data += ETH_HLEN; + + switch (icmp->icmp6_type) { + case ICMP6_NDP_RS: + DEBUG_CALL(" type = Router Solicitation"); + if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && + ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) { + /* Gratuitous NDP */ + ndp_table_add(slirp, ip->ip_src, eth->h_source); + + ndp_send_ra(slirp); + } + break; + + case ICMP6_NDP_RA: + DEBUG_CALL(" type = Router Advertisement"); + slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't", + slirp->opaque); + break; + + case ICMP6_NDP_NS: + DEBUG_CALL(" type = Neighbor Solicitation"); + if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && + !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target) && + ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN && + (!in6_zero(&ip->ip_src) || + in6_solicitednode_multicast(&ip->ip_dst))) { + if (in6_equal_host(&icmp->icmp6_nns.target)) { + /* Gratuitous NDP */ + ndp_table_add(slirp, ip->ip_src, eth->h_source); + ndp_send_na(slirp, ip, icmp); + } + } + break; + + case ICMP6_NDP_NA: + DEBUG_CALL(" type = Neighbor Advertisement"); + if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && + ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN && + !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target) && + (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst) || icmp->icmp6_nna.S == 0)) { + ndp_table_add(slirp, icmp->icmp6_nna.target, eth->h_source); + } + break; + + case ICMP6_NDP_REDIRECT: + DEBUG_CALL(" type = Redirect"); + slirp->cb->guest_error( + "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque); + break; + } +} + +/* + * Process a received ICMPv6 message. + */ +void icmp6_input(struct mbuf *m) +{ + Slirp *slirp = m->slirp; + /* NDP reads the ethernet header for gratuitous NDP */ + M_DUP_DEBUG(slirp, m, 1, ETH_HLEN); + + struct icmp6 *icmp; + struct ip6 *ip = mtod(m, struct ip6 *); + int hlen = sizeof(struct ip6); + + DEBUG_CALL("icmp6_input"); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("m_len = %d", m->m_len); + + if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { + freeit: + m_free(m); + goto end_error; + } + + if (ip6_cksum(m)) { + goto freeit; + } + + m->m_len -= hlen; + m->m_data += hlen; + icmp = mtod(m, struct icmp6 *); + m->m_len += hlen; + m->m_data -= hlen; + + DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type); + switch (icmp->icmp6_type) { + case ICMP6_ECHO_REQUEST: + if (in6_equal_host(&ip->ip_dst)) { + icmp6_send_echoreply(m, slirp, ip, icmp); + } else if (slirp->restricted) { + goto freeit; + } else { + struct socket *so; + struct sockaddr_storage addr; + int ttl; + + so = socreate(slirp, IPPROTO_ICMPV6); + if (icmp6_send(so, m, hlen) == 0) { + /* We could send this as ICMP, good! */ + return; + } + + /* We could not send this as ICMP, try to send it on UDP echo + * service (7), wishfully hoping that it is open there. */ + + if (udp_attach(so, AF_INET6) == -1) { + DEBUG_MISC("icmp6_input udp_attach errno = %d-%s", errno, + g_strerror(errno)); + sofree(so); + m_free(m); + goto end_error; + } + so->so_m = m; + so->so_ffamily = AF_INET6; + so->so_faddr6 = ip->ip_dst; + so->so_fport = htons(7); + so->so_lfamily = AF_INET6; + so->so_laddr6 = ip->ip_src; + so->so_lport = htons(9); + so->so_state = SS_ISFCONNECTED; + + /* Send the packet */ + addr = so->fhost.ss; + if (sotranslate_out(so, &addr) < 0) { + icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); + udp_detach(so); + return; + } + + /* + * Check for TTL + */ + ttl = ip->ip_hl-1; + if (ttl <= 0) { + DEBUG_MISC("udp ttl exceeded"); + icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS); + udp_detach(so); + break; + } + setsockopt(so->s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); + + if (sendto(so->s, icmp6_ping_msg, strlen(icmp6_ping_msg), 0, + (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) { + DEBUG_MISC("icmp6_input udp sendto tx errno = %d-%s", errno, + g_strerror(errno)); + icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); + udp_detach(so); + } + } /* if (in6_equal_host(&ip->ip_dst)) */ + break; + + case ICMP6_NDP_RS: + case ICMP6_NDP_RA: + case ICMP6_NDP_NS: + case ICMP6_NDP_NA: + case ICMP6_NDP_REDIRECT: + ndp_input(m, slirp, ip, icmp); + m_free(m); + break; + + case ICMP6_UNREACH: + case ICMP6_TOOBIG: + case ICMP6_TIMXCEED: + case ICMP6_PARAMPROB: + /* XXX? report error? close socket? */ + default: + m_free(m); + break; + } + +end_error: + /* m is m_free()'d xor put in a socket xor or given to ip_send */ + return; +} diff --git a/libslirp/src/ip6_icmp.h b/libslirp/src/ip6_icmp.h new file mode 100644 index 000000000..7f8bc60bc --- /dev/null +++ b/libslirp/src/ip6_icmp.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#ifndef SLIRP_IP6_ICMP_H +#define SLIRP_IP6_ICMP_H + +/* + * Interface Control Message Protocol version 6 Definitions. + * Per RFC 4443, March 2006. + * + * Network Discover Protocol Definitions. + * Per RFC 4861, September 2007. + */ + +struct icmp6_echo { /* Echo Messages */ + uint16_t id; + uint16_t seq_num; +}; + +union icmp6_error_body { + uint32_t unused; + uint32_t pointer; + uint32_t mtu; +}; + +/* + * NDP Messages + */ +struct ndp_rs { /* Router Solicitation Message */ + uint32_t reserved; +}; + +struct ndp_ra { /* Router Advertisement Message */ + uint8_t chl; /* Cur Hop Limit */ +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t M : 1, O : 1, reserved : 6; +#else + uint8_t reserved : 6, O : 1, M : 1; +#endif + uint16_t lifetime; /* Router Lifetime */ + uint32_t reach_time; /* Reachable Time */ + uint32_t retrans_time; /* Retrans Timer */ +}; + +G_STATIC_ASSERT(sizeof(struct ndp_ra) == 12); + +struct ndp_ns { /* Neighbor Solicitation Message */ + uint32_t reserved; + struct in6_addr target; /* Target Address */ +}; + +G_STATIC_ASSERT(sizeof(struct ndp_ns) == 20); + +struct ndp_na { /* Neighbor Advertisement Message */ +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t R : 1, /* Router Flag */ + S : 1, /* Solicited Flag */ + O : 1, /* Override Flag */ + reserved_1 : 5; +#else + uint8_t reserved_1 : 5, O : 1, S : 1, R : 1; +#endif + uint8_t reserved_2; + uint16_t reserved_3; + struct in6_addr target; /* Target Address */ +}; + +G_STATIC_ASSERT(sizeof(struct ndp_na) == 20); + +struct ndp_redirect { + uint32_t reserved; + struct in6_addr target; /* Target Address */ + struct in6_addr dest; /* Destination Address */ +}; + +G_STATIC_ASSERT(sizeof(struct ndp_redirect) == 36); + +/* + * Structure of an icmpv6 header. + */ +struct icmp6 { + uint8_t icmp6_type; /* type of message, see below */ + uint8_t icmp6_code; /* type sub code */ + uint16_t icmp6_cksum; /* ones complement cksum of struct */ + union { + union icmp6_error_body error_body; + struct icmp6_echo echo; + struct ndp_rs ndp_rs; + struct ndp_ra ndp_ra; + struct ndp_ns ndp_ns; + struct ndp_na ndp_na; + struct ndp_redirect ndp_redirect; + } icmp6_body; +#define icmp6_err icmp6_body.error_body +#define icmp6_echo icmp6_body.echo +#define icmp6_id icmp6_body.echo.id +#define icmp6_seq icmp6_body.echo.seq_num +#define icmp6_nrs icmp6_body.ndp_rs +#define icmp6_nra icmp6_body.ndp_ra +#define icmp6_nns icmp6_body.ndp_ns +#define icmp6_nna icmp6_body.ndp_na +#define icmp6_redirect icmp6_body.ndp_redirect +}; + +G_STATIC_ASSERT(sizeof(struct icmp6) == 40); + +#define ICMP6_MINLEN 4 +#define ICMP6_ERROR_MINLEN 8 +#define ICMP6_ECHO_MINLEN 8 +#define ICMP6_NDP_RS_MINLEN 8 +#define ICMP6_NDP_RA_MINLEN 16 +#define ICMP6_NDP_NS_MINLEN 24 +#define ICMP6_NDP_NA_MINLEN 24 +#define ICMP6_NDP_REDIRECT_MINLEN 40 + +/* + * NDP Options + */ +SLIRP_PACKED_BEGIN +struct ndpopt { + uint8_t ndpopt_type; /* Option type */ + uint8_t ndpopt_len; /* /!\ In units of 8 octets */ + union { + unsigned char linklayer_addr[6]; /* Source/Target Link-layer */ +#define ndpopt_linklayer ndpopt_body.linklayer_addr + SLIRP_PACKED_BEGIN + struct prefixinfo { /* Prefix Information */ + uint8_t prefix_length; +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t L : 1, A : 1, reserved1 : 6; +#else + uint8_t reserved1 : 6, A : 1, L : 1; +#endif + uint32_t valid_lt; /* Valid Lifetime */ + uint32_t pref_lt; /* Preferred Lifetime */ + uint32_t reserved2; + struct in6_addr prefix; + } SLIRP_PACKED_END prefixinfo; +#define ndpopt_prefixinfo ndpopt_body.prefixinfo + SLIRP_PACKED_BEGIN + struct rdnss { + uint16_t reserved; + uint32_t lifetime; + struct in6_addr addr; + } SLIRP_PACKED_END rdnss; +#define ndpopt_rdnss ndpopt_body.rdnss + } ndpopt_body; +} SLIRP_PACKED_END; + +/* NDP options type */ +#define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */ +#define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */ +#define NDPOPT_PREFIX_INFO 3 /* Prefix Information */ +#define NDPOPT_RDNSS 25 /* Recursive DNS Server Address */ + +/* NDP options size, in octets. */ +#define NDPOPT_LINKLAYER_LEN 8 +#define NDPOPT_PREFIXINFO_LEN 32 +#define NDPOPT_RDNSS_LEN 24 + +/* + * Definition of type and code field values. + * Per https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml + * Last Updated 2012-11-12 + */ + +/* Errors */ +#define ICMP6_UNREACH 1 /* Destination Unreachable */ +#define ICMP6_UNREACH_NO_ROUTE 0 /* no route to dest */ +#define ICMP6_UNREACH_DEST_PROHIB 1 /* com with dest prohibited */ +#define ICMP6_UNREACH_SCOPE 2 /* beyond scope of src addr */ +#define ICMP6_UNREACH_ADDRESS 3 /* address unreachable */ +#define ICMP6_UNREACH_PORT 4 /* port unreachable */ +#define ICMP6_UNREACH_SRC_FAIL 5 /* src addr failed */ +#define ICMP6_UNREACH_REJECT_ROUTE 6 /* reject route to dest */ +#define ICMP6_UNREACH_SRC_HDR_ERROR 7 /* error in src routing header */ +#define ICMP6_TOOBIG 2 /* Packet Too Big */ +#define ICMP6_TIMXCEED 3 /* Time Exceeded */ +#define ICMP6_TIMXCEED_INTRANS 0 /* hop limit exceeded in transit */ +#define ICMP6_TIMXCEED_REASS 1 /* ttl=0 in reass */ +#define ICMP6_PARAMPROB 4 /* Parameter Problem */ +#define ICMP6_PARAMPROB_HDR_FIELD 0 /* err header field */ +#define ICMP6_PARAMPROB_NXTHDR_TYPE 1 /* unrecognized Next Header type */ +#define ICMP6_PARAMPROB_IPV6_OPT 2 /* unrecognized IPv6 option */ + +/* Informational Messages */ +#define ICMP6_ECHO_REQUEST 128 /* Echo Request */ +#define ICMP6_ECHO_REPLY 129 /* Echo Reply */ +#define ICMP6_NDP_RS 133 /* Router Solicitation (NDP) */ +#define ICMP6_NDP_RA 134 /* Router Advertisement (NDP) */ +#define ICMP6_NDP_NS 135 /* Neighbor Solicitation (NDP) */ +#define ICMP6_NDP_NA 136 /* Neighbor Advertisement (NDP) */ +#define ICMP6_NDP_REDIRECT 137 /* Redirect Message (NDP) */ + +/* + * Router Configuration Variables (rfc4861#section-6) + */ +#define NDP_IsRouter 1 +#define NDP_AdvSendAdvertisements 1 +#define NDP_MaxRtrAdvInterval 600000 +#define NDP_MinRtrAdvInterval \ + ((NDP_MaxRtrAdvInterval >= 9) ? NDP_MaxRtrAdvInterval / 3 : \ + NDP_MaxRtrAdvInterval) +#define NDP_AdvManagedFlag 0 +#define NDP_AdvOtherConfigFlag 0 +#define NDP_AdvLinkMTU 0 +#define NDP_AdvReachableTime 0 +#define NDP_AdvRetransTime 0 +#define NDP_AdvCurHopLimit 64 +#define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000) +#define NDP_AdvValidLifetime 86400 +#define NDP_AdvOnLinkFlag 1 +#define NDP_AdvPrefLifetime 14400 +#define NDP_AdvAutonomousFlag 1 + +/* Called from slirp_new, but after other initialization */ +void icmp6_post_init(Slirp *slirp); + +/* Called from slirp_cleanup */ +void icmp6_cleanup(Slirp *slirp); + +/* Process an ICMPv6 packet from the guest */ +void icmp6_input(struct mbuf *); + +/* Send an ICMPv6 error related to the given packet, using the given ICMPv6 type and code, using the given source */ +void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src); + +/* Similar to icmp6_forward_error, but use the link-local address as source */ +void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code); + +/* Forward the ICMP packet to the guest (probably a ping reply) */ +void icmp6_reflect(struct mbuf *); + +/* Handle ICMP data from the ICMP socket, and forward it to the guest (using so_m as reference) */ +void icmp6_receive(struct socket *so); + +/* Send a neighbour sollicitation, to resolve the given IPV6 address */ +void ndp_send_ns(Slirp *slirp, struct in6_addr addr); + +/* Timer handler for router advertisement, to send it and reschedule the timer */ +void ra_timer_handler(Slirp *slirp, void *unused); + +#endif diff --git a/libslirp/src/ip6_input.c b/libslirp/src/ip6_input.c new file mode 100644 index 000000000..4aca08285 --- /dev/null +++ b/libslirp/src/ip6_input.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "slirp.h" +#include "ip6_icmp.h" + +/* + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. + */ +void ip6_post_init(Slirp *slirp) +{ + icmp6_post_init(slirp); +} + +void ip6_cleanup(Slirp *slirp) +{ + icmp6_cleanup(slirp); +} + +void ip6_input(struct mbuf *m) +{ + Slirp *slirp = m->slirp; + /* NDP reads the ethernet header for gratuitous NDP */ + M_DUP_DEBUG(slirp, m, 1, TCPIPHDR_DELTA + 2 + ETH_HLEN); + + struct ip6 *ip6; + + if (!slirp->in6_enabled) { + goto bad; + } + + DEBUG_CALL("ip6_input"); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("m_len = %d", m->m_len); + + if (m->m_len < sizeof(struct ip6)) { + goto bad; + } + + ip6 = mtod(m, struct ip6 *); + + if (ip6->ip_v != IP6VERSION) { + goto bad; + } + + if (ntohs(ip6->ip_pl) + sizeof(struct ip6) > slirp->if_mtu) { + icmp6_send_error(m, ICMP6_TOOBIG, 0); + goto bad; + } + + // Check if the message size is big enough to hold what's + // set in the payload length header. If not this is an invalid + // packet + if (m->m_len < ntohs(ip6->ip_pl) + sizeof(struct ip6)) { + goto bad; + } + + /* check ip_ttl for a correct ICMP reply */ + if (ip6->ip_hl == 0) { + icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS); + goto bad; + } + + /* + * Switch out to protocol's input routine. + */ + switch (ip6->ip_nh) { + case IPPROTO_TCP: + NTOHS(ip6->ip_pl); + tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6); + break; + case IPPROTO_UDP: + udp6_input(m); + break; + case IPPROTO_ICMPV6: + icmp6_input(m); + break; + default: + m_free(m); + } + return; +bad: + m_free(m); +} diff --git a/libslirp/src/ip6_output.c b/libslirp/src/ip6_output.c new file mode 100644 index 000000000..834f1c0a3 --- /dev/null +++ b/libslirp/src/ip6_output.c @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "slirp.h" + +/* Number of packets queued before we start sending + * (to prevent allocing too many mbufs) */ +#define IF6_THRESH 10 + +/* + * IPv6 output. The packet in mbuf chain m contains a IP header + */ +int ip6_output(struct socket *so, struct mbuf *m, int fast) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, 0); + + struct ip6 *ip = mtod(m, struct ip6 *); + + DEBUG_CALL("ip6_output"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); + + /* Fill IPv6 header */ + ip->ip_v = IP6VERSION; + ip->ip_hl = IP6_HOP_LIMIT; + ip->ip_tc_hi = 0; + ip->ip_tc_lo = 0; + ip->ip_fl_hi = 0; + ip->ip_fl_lo = 0; + + if (fast) { + /* We cannot fast-send non-multicast, we'd need a NDP NS */ + assert(IN6_IS_ADDR_MULTICAST(&ip->ip_dst)); + if_encap(m->slirp, m); + m_free(m); + } else { + if_output(so, m); + } + + return 0; +} diff --git a/libslirp/src/ip_icmp.c b/libslirp/src/ip_icmp.c new file mode 100644 index 000000000..d8fe7cb2d --- /dev/null +++ b/libslirp/src/ip_icmp.c @@ -0,0 +1,547 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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" + +#ifndef _WIN32 +#include +#endif + +#ifndef WITH_ICMP_ERROR_MSG +#define WITH_ICMP_ERROR_MSG 0 +#endif + +/* The message sent when emulating PING */ +/* Be nice and tell them it's just a pseudo-ping packet */ +static const char icmp_ping_msg[] = + "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST " + "packets.\n"; + +/* list of actions for icmp_send_error() on RX of an icmp message */ +static const 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 +}; + +void icmp_init(Slirp *slirp) +{ + slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; + slirp->icmp_last_so = &slirp->icmp; +} + +void icmp_cleanup(Slirp *slirp) +{ + struct socket *so, *so_next; + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { + so_next = so->so_next; + icmp_detach(so); + } +} + +/* Send ICMP packet to the Internet, and save it to so_m */ +static int icmp_send(struct socket *so, struct mbuf *m, int hlen) +{ + Slirp *slirp = m->slirp; + + struct sockaddr_in addr; + + /* + * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent + * between host OSes. On Linux, only the ICMP header and payload is + * included. On macOS/Darwin, the socket acts like a raw socket and + * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP + * sockets aren't supported at all, so we treat them like raw sockets. It + * isn't possible to detect this difference at runtime, so we must use an + * #ifdef to determine if we need to remove the IP header. + */ +#if defined(BSD) && !defined(__GNU__) + so->so_type = IPPROTO_IP; +#else + so->so_type = IPPROTO_ICMP; +#endif + + so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + if (not_valid_socket(so->s)) { + if (errno == EAFNOSUPPORT + || errno == EPROTONOSUPPORT + || errno == EACCES) { + /* Kernel doesn't support or allow ping sockets. */ + so->so_type = IPPROTO_IP; + so->s = slirp_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + } + } + if (not_valid_socket(so->s)) { + return -1; + } + slirp_register_poll_socket(so); + + if (slirp_bind_outbound(so, AF_INET) != 0) { + // bind failed - close socket + closesocket(so->s); + so->s = SLIRP_INVALID_SOCKET; + return -1; + } + + M_DUP_DEBUG(slirp, m, 0, 0); + struct ip *ip = mtod(m, struct ip *); + + so->so_m = m; + so->so_faddr = ip->ip_dst; + so->so_laddr = ip->ip_src; + so->so_iptos = ip->ip_tos; + so->so_state = SS_ISFCONNECTED; + so->so_expire = curtime + SO_EXPIRE; + + addr.sin_family = AF_INET; + addr.sin_addr = so->so_faddr; + + slirp_insque(so, &so->slirp->icmp); + + if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, + (struct sockaddr *)&addr, sizeof(addr)) == -1) { + DEBUG_MISC("icmp_input icmp sendto tx errno = %d-%s", errno, + g_strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, g_strerror(errno)); + icmp_detach(so); + } + + return 0; +} + +void icmp_detach(struct socket *so) +{ + slirp_unregister_poll_socket(so); + closesocket(so->s); + sofree(so); +} + +/* + * Process a received ICMP message. + */ +void icmp_input(struct mbuf *m, int hlen) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, 0); + + register struct icmp *icp; + register struct ip *ip = mtod(m, struct ip *); + int icmplen = ip->ip_len; + + DEBUG_CALL("icmp_input"); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("m_len = %d", m->m_len); + + /* + * Locate icmp structure in mbuf, and check + * that its not corrupted and of at least minimum length. + */ + if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ + freeit: + m_free(m); + goto end_error; + } + + m->m_len -= hlen; + m->m_data += hlen; + icp = mtod(m, struct icmp *); + if (cksum(m, icmplen)) { + goto freeit; + } + m->m_len += hlen; + m->m_data -= hlen; + + DEBUG_ARG("icmp_type = %d", icp->icmp_type); + switch (icp->icmp_type) { + case ICMP_ECHO: + ip->ip_len += hlen; /* since ip_input subtracts this */ + if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == slirp->vnameserver_addr.s_addr) { + icmp_reflect(m); + } else if (slirp->restricted) { + goto freeit; + } else { + struct socket *so; + struct sockaddr_storage addr; + int ttl; + + so = socreate(slirp, IPPROTO_ICMP); + if (icmp_send(so, m, hlen) == 0) { + /* We could send this as ICMP, good! */ + return; + } + + /* We could not send this as ICMP, try to send it on UDP echo + * service (7), wishfully hoping that it is open there. */ + + if (not_valid_socket(udp_attach(so, AF_INET))) { + DEBUG_MISC("icmp_input udp_attach errno = %d-%s", errno, + g_strerror(errno)); + sofree(so); + m_free(m); + goto end_error; + } + so->so_m = m; + so->so_ffamily = AF_INET; + so->so_faddr = ip->ip_dst; + so->so_fport = htons(7); + so->so_lfamily = AF_INET; + so->so_laddr = ip->ip_src; + so->so_lport = htons(9); + so->so_iptos = ip->ip_tos; + so->so_state = SS_ISFCONNECTED; + + /* Send the packet */ + addr = so->fhost.ss; + if (sotranslate_out(so, &addr) < 0) { + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, + g_strerror(errno)); + udp_detach(so); + return; + } + + /* + * Check for TTL + */ + ttl = ip->ip_ttl-1; + if (ttl <= 0) { + DEBUG_MISC("udp ttl exceeded"); + icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, + NULL); + udp_detach(so); + break; + } + setsockopt(so->s, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + + if (sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, + (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) { + DEBUG_MISC("icmp_input udp sendto tx errno = %d-%s", errno, + g_strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, + g_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: + m_free(m); + break; + + default: + m_free(m); + } /* switch */ + +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. + * mbuf *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_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, + const char *message, struct in_addr *src) +{ + unsigned hlen, shlen, s_ip_len; + register struct ip *ip; + register struct icmp *icp; + register struct mbuf *m; + + DEBUG_CALL("icmp_send_error"); + DEBUG_ARG("msrc = %p", 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 & SLIRP_DBG_MISC) { + char addr_src[INET_ADDRSTRLEN]; + char addr_dst[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &ip->ip_src, addr_src, sizeof(addr_src)); + inet_ntop(AF_INET, &ip->ip_dst, addr_dst, sizeof(addr_dst)); + DEBUG_MISC(" %.16s to %.16s", addr_src, addr_dst); + } + if (ip->ip_off & IP_OFFMASK) + goto end_error; /* Only reply to fragment 0 */ + + /* Do not reply to source-only IPs */ + if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) { + goto end_error; + } + + 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 */ + m = m_get(msrc->slirp); + if (!m) { + goto end_error; + } + + { + 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 (message && WITH_ICMP_ERROR_MSG) { /* 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; + } + + 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 = 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 addresses */ + ip->ip_src = *src; + + ip_output((struct socket *)NULL, m); + +end_error: + return; +} +#undef ICMP_MAXDATALEN + +void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, + const char *message) +{ + icmp_forward_error(msrc, type, code, minsize, message, &msrc->slirp->vhost_addr); +} + +/* + * Reflect the ip packet back to the source + */ +void icmp_reflect(struct mbuf *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_type = ICMP_ECHOREPLY; + 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 + * mbuf's data back, and adjust the IP length. + */ + memmove((char *)(ip + 1), (char *)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; + } + + ip_output((struct socket *)NULL, m); +} + +void icmp_receive(struct socket *so) +{ + struct mbuf *m = so->so_m; + struct ip *ip = mtod(m, struct ip *); + int hlen = ip->ip_hl << 2; + uint8_t error_code; + struct icmp *icp; + int id, len; + + m->m_data += hlen; + m->m_len -= hlen; + icp = mtod(m, struct icmp *); + + id = icp->icmp_id; + len = recv(so->s, icp, M_ROOM(m), 0); + + if (so->so_type == IPPROTO_IP) { + if (len >= sizeof(struct ip)) { + struct ip *inner_ip = mtod(m, struct ip *); + int inner_hlen = inner_ip->ip_hl << 2; + if (inner_hlen > len) { + len = -1; + errno = -EINVAL; + } else { + len -= inner_hlen; + memmove(icp, (unsigned char *)icp + inner_hlen, len); + } + } else { + len = -1; + errno = -EINVAL; + } + } + + icp->icmp_id = id; + + m->m_data -= hlen; + m->m_len += hlen; + + if (len == -1 || len == 0) { + if (errno == ENETUNREACH) { + error_code = ICMP_UNREACH_NET; + } else { + error_code = ICMP_UNREACH_HOST; + } + DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, g_strerror(errno)); + icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, g_strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + icmp_detach(so); +} diff --git a/libslirp/src/ip_icmp.h b/libslirp/src/ip_icmp.h new file mode 100644 index 000000000..aad04165f --- /dev/null +++ b/libslirp/src/ip_icmp.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 uint32_t n_time; + +/* + * Structure of an icmp header. + */ +struct icmp { + uint8_t icmp_type; /* type of message, see below */ + uint8_t icmp_code; /* type sub code */ + uint16_t icmp_cksum; /* ones complement cksum of struct */ + union { + uint8_t ih_pptr; /* ICMP_PARAMPROB */ + struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ + struct ih_idseq { + uint16_t icd_id; + uint16_t icd_seq; + } ih_idseq; + int ih_void; + + /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ + struct ih_pmtu { + uint16_t ipm_void; + uint16_t 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 +}; + +/* + * Lower bounds on packet lengths for various types. + * For the error advice packets must first ensure that the + * packet is large enough 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) + +/* Called from slirp_new */ +void icmp_init(Slirp *slirp); + +/* Called from slirp_cleanup */ +void icmp_cleanup(Slirp *slirp); + +/* Process an ICMP packet from the guest */ +void icmp_input(struct mbuf *, int); + +/* Send an ICMP error related to the given packet, using the given ICMP type and code, appending the given message (if enabled at compilation), and using the given source. If minsize is sent, send only header + 8B of the given packet, otherwise send it all */ +void icmp_forward_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, + const char *message, struct in_addr *src); + +/* Similar to icmp_forward_error, but use the virtual host address as source */ +void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, + const char *message); + +/* Forward the ICMP packet to the guest (probably a ping reply) */ +void icmp_reflect(struct mbuf *); + +/* Handle ICMP data from the ICMP socket, and forward it to the guest (using so_m as reference) */ +void icmp_receive(struct socket *so); + +/* Forget about this pending ICMP request */ +void icmp_detach(struct socket *so); + +#endif diff --git a/libslirp/src/ip_input.c b/libslirp/src/ip_input.c new file mode 100644 index 000000000..0a4b008a8 --- /dev/null +++ b/libslirp/src/ip_input.c @@ -0,0 +1,450 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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. + */ + +#include "slirp.h" +#include "ip_icmp.h" + +static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp); +static void ip_freef(Slirp *slirp, struct ipq *fp); +static void ip_enq(register struct ipas *p, register struct ipas *prev); +static void ip_deq(register struct ipas *p); + +/* + * IP initialization: fill in IP protocol switch table. + * All protocols not implemented in kernel go to raw IP protocol handler. + */ +void ip_init(Slirp *slirp) +{ + slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; + udp_init(slirp); + tcp_init(slirp); + icmp_init(slirp); +} + +void ip_cleanup(Slirp *slirp) +{ + udp_cleanup(slirp); + tcp_cleanup(slirp); + icmp_cleanup(slirp); +} + +/* + * Ip input routine. Checksum and byte swap header. If fragmented + * try to reassemble. Process options. Pass to next level. + */ +void ip_input(struct mbuf *m) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, TCPIPHDR_DELTA); + + register struct ip *ip; + int hlen; + + if (!slirp->in_enabled) { + goto bad; + } + + DEBUG_CALL("ip_input"); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("m_len = %d", m->m_len); + + if (m->m_len < sizeof(struct ip)) { + goto bad; + } + + ip = mtod(m, struct ip *); + + if (ip->ip_v != IPVERSION) { + goto bad; + } + + hlen = ip->ip_hl << 2; + if (hlen < sizeof(struct ip) || hlen > m->m_len) { /* min header length */ + goto bad; /* or packet too short */ + } + + /* keep ip header intact for ICMP reply + * ip->ip_sum = cksum(m, hlen); + * if (ip->ip_sum) { + */ + if (cksum(m, hlen)) { + goto bad; + } + + /* + * Convert fields to host representation. + */ + NTOHS(ip->ip_len); + if (ip->ip_len < hlen) { + 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 mbufs if longer than we expect. + * Drop packet if shorter than we expect. + */ + if (m->m_len < ip->ip_len) { + goto bad; + } + + /* Should drop packet if mbuf 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) { + icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl"); + goto bad; + } + + /* + * 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 *q; + struct qlink *l; + /* + * Look for queue of fragments + * of this datagram. + */ + for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link; + l = l->next) { + q = container_of(l, struct ipq, ip_link); + if (ip->ip_id == q->ipq_id && + ip->ip_src.s_addr == q->ipq_src.s_addr && + ip->ip_dst.s_addr == q->ipq_dst.s_addr && + ip->ip_p == q->ipq_p) + goto found; + } + q = NULL; + 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) + ip->ip_tos |= 1; + else + ip->ip_tos &= ~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 (ip->ip_tos & 1 || ip->ip_off) { + ip = ip_reass(slirp, ip, q); + if (ip == NULL) + return; + m = dtom(slirp, ip); + } else if (q) + ip_freef(slirp, q); + + } else + ip->ip_len -= hlen; + + /* + * Switch out to protocol's input routine. + */ + switch (ip->ip_p) { + case IPPROTO_TCP: + tcp_input(m, hlen, (struct socket *)NULL, AF_INET); + break; + case IPPROTO_UDP: + udp_input(m, hlen); + break; + case IPPROTO_ICMP: + icmp_input(m, hlen); + break; + default: + m_free(m); + } + return; +bad: + m_free(m); +} + +#define iptoas(P) container_of((P), struct ipas, ipf_ip) +#define astoip(P) (&(P)->ipf_ip) +/* + * 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 q; otherwise have to make a chain. + */ +static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *q) +{ + register struct mbuf *m = dtom(slirp, ip); + struct ipas *first = container_of(q, struct ipas, ipq); + register struct ipas *cursor; + int hlen = ip->ip_hl << 2; + int i, next; + + DEBUG_CALL("ip_reass"); + DEBUG_ARG("ip = %p", ip); + DEBUG_ARG("q = %p", q); + DEBUG_ARG("m = %p", m); + + /* + * Presence of header sizes in mbufs + * 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 (q == NULL) { + struct mbuf *t = m_get(slirp); + + if (t == NULL) { + goto dropfrag; + } + first = mtod(t, struct ipas *); + q = &first->ipq; + slirp_insque(&q->ip_link, &slirp->ipq.ip_link); + q->ipq_ttl = IPFRAGTTL; + q->ipq_p = ip->ip_p; + q->ipq_id = ip->ip_id; + first->link.next = first->link.prev = first; + q->ipq_src = ip->ip_src; + q->ipq_dst = ip->ip_dst; + cursor = first; + goto insert; + } + + /* + * Find a segment which begins after this one does. + */ + for (cursor = first->link.next; cursor != first; cursor = cursor->link.next) + if (cursor->ipf_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 (cursor->link.prev != first) { + struct ipas *pq = cursor->link.prev; + i = pq->ipf_off + pq->ipf_len - ip->ip_off; + if (i > 0) { + if (i >= ip->ip_len) + goto dropfrag; + m_adj(dtom(slirp, 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 (cursor != first && ip->ip_off + ip->ip_len > cursor->ipf_off) { + struct ipas *prev; + i = (ip->ip_off + ip->ip_len) - cursor->ipf_off; + if (i < cursor->ipf_len) { + cursor->ipf_len -= i; + cursor->ipf_off += i; + m_adj(dtom(slirp, cursor), i); + break; + } + prev = cursor; + cursor = cursor->link.next; + ip_deq(prev); + m_free(dtom(slirp, prev)); + } + +insert: + /* + * Stick new segment in its place; + * check for complete reassembly. + */ + ip_enq(iptoas(ip), cursor->link.prev); + next = 0; + for (cursor = first->link.next; cursor != first; cursor = cursor->link.next) { + if (cursor->ipf_off != next) + return NULL; + next += cursor->ipf_len; + } + if (((struct ipas *)(cursor->link.prev))->ipf_tos & 1) + return NULL; + + /* + * Reassembly is complete; concatenate fragments. + */ + cursor = first->link.next; + m = dtom(slirp, cursor); + int delta = (char *)cursor - (m->m_flags & M_EXT ? m->m_ext : m->m_dat); + + cursor = cursor->link.next; + while (cursor != first) { + struct mbuf *t = dtom(slirp, cursor); + cursor = cursor->link.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. + */ + cursor = first->link.next; + + /* + * If the fragments concatenated to an mbuf that's bigger than the total + * size of the fragment and the mbuf was not already using an m_ext buffer, + * then an m_ext buffer was allocated. But q->ipq_next points to the old + * buffer (in the mbuf), so we must point ip into the new buffer. + */ + if (m->m_flags & M_EXT) { + cursor = (struct ipas *)(m->m_ext + delta); + } + + ip = astoip(cursor); + ip->ip_len = next; + ip->ip_tos &= ~1; + ip->ip_src = q->ipq_src; + ip->ip_dst = q->ipq_dst; + slirp_remque(&q->ip_link); + m_free(dtom(slirp, q)); + m->m_len += (ip->ip_hl << 2); + m->m_data -= (ip->ip_hl << 2); + + return ip; + +dropfrag: + m_free(m); + return NULL; +} + +/* + * Free a fragment reassembly header and all + * associated datagrams. + */ +static void ip_freef(Slirp *slirp, struct ipq *q) +{ + struct ipas *first = container_of(q, struct ipas, ipq); + register struct ipas *cursor, *next; + + for (cursor = first->link.next; cursor != first; cursor = next) { + next = cursor->link.next; + ip_deq(cursor); + m_free(dtom(slirp, cursor)); + } + slirp_remque(&q->ip_link); + m_free(dtom(slirp, q)); +} + +/* + * Put an ip fragment on a reassembly chain. + * Like slirp_insque, but pointers in middle of structure. + */ +static void ip_enq(register struct ipas *p, register struct ipas *prev) +{ + DEBUG_CALL("ip_enq"); + DEBUG_ARG("prev = %p", prev); + p->link.prev = prev; + p->link.next = prev->link.next; + ((struct ipas *)(prev->link.next))->link.prev = p; + prev->link.next = p; +} + +/* + * To ip_enq as slirp_remque is to slirp_insque. + */ +static void ip_deq(register struct ipas *p) +{ + ((struct ipas *)(p->link.prev))->link.next = p->link.next; + ((struct ipas *)(p->link.next))->link.prev = p->link.prev; +} + +void ip_slowtimo(Slirp *slirp) +{ + struct qlink *l; + + DEBUG_CALL("ip_slowtimo"); + + l = slirp->ipq.ip_link.next; + + if (l == NULL) + return; + + while (l != &slirp->ipq.ip_link) { + struct ipq *q = container_of(l, struct ipq, ip_link); + l = l->next; + if (--q->ipq_ttl == 0) { + ip_freef(slirp, q); + } + } +} + +void ip_stripoptions(register struct mbuf *m) +{ + register int i; + struct ip *ip = mtod(m, struct ip *); + register char *opts; + int olen; + + olen = (ip->ip_hl << 2) - sizeof(struct ip); + opts = (char *)(ip + 1); + i = m->m_len - (sizeof(struct ip) + olen); + memmove(opts, opts + olen, (unsigned)i); + m->m_len -= olen; + + ip->ip_hl = sizeof(struct ip) >> 2; +} diff --git a/slirp/ip_output.c b/libslirp/src/ip_output.c similarity index 50% rename from slirp/ip_output.c rename to libslirp/src/ip_output.c index b45eaef5d..4f6260591 100644 --- a/slirp/ip_output.c +++ b/libslirp/src/ip_output.c @@ -1,172 +1,171 @@ -/* - * 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 - -/* Number of packets queued before we start sending - * (to prevent allocing too many mbufs) */ -#define IF_THRESH 10 - -/* - * IP output. The packet in mbuf chain m contains a skeletal IP - * header (with len, off, ttl, proto, tos, src, dst). - * The mbuf chain containing the packet will be freed. - * The mbuf opt, if present, will not be freed. - */ -int -ip_output(struct socket *so, struct mbuf *m0) -{ - Slirp *slirp = m0->slirp; - register struct ip *ip; - register struct mbuf *m = m0; - register int hlen = sizeof(struct ip ); - int len, off, error = 0; - - DEBUG_CALL("ip_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m0 = %lx", (long)m0); - - ip = mtod(m, struct ip *); - /* - * Fill in IP header. - */ - ip->ip_v = IPVERSION; - ip->ip_off &= IP_DF; - ip->ip_id = htons(slirp->ip_id++); - ip->ip_hl = hlen >> 2; - - /* - * If small enough for interface, can just send directly. - */ - if ((uint16_t)ip->ip_len <= IF_MTU) { - ip->ip_len = htons((uint16_t)ip->ip_len); - ip->ip_off = htons((uint16_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; - goto bad; - } - - len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */ - if (len < 8) { - error = -1; - goto bad; - } - - { - int mhlen, firstlen = len; - struct mbuf **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 < (uint16_t)ip->ip_len; off += len) { - register struct ip *mhip; - m = m_get(slirp); - if (m == NULL) { - error = -1; - goto sendorfree; - } - m->m_data += IF_MAXLINKHDR; - mhip = mtod(m, struct ip *); - *mhip = *ip; - - 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 >= (uint16_t)ip->ip_len) - len = (uint16_t)ip->ip_len - off; - else - mhip->ip_off |= IP_MF; - mhip->ip_len = htons((uint16_t)(len + mhlen)); - - if (m_copy(m, m0, off, len) < 0) { - error = -1; - goto sendorfree; - } - - mhip->ip_off = htons((uint16_t)mhip->ip_off); - mhip->ip_sum = 0; - mhip->ip_sum = cksum(m, mhlen); - *mnext = m; - mnext = &m->m_nextpkt; - } - /* - * 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 - (uint16_t)ip->ip_len); - ip->ip_len = htons((uint16_t)m->m_len); - ip->ip_off = htons((uint16_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 = NULL; - if (error == 0) - if_output(so, m); - else - m_free(m); - } - } - -done: - return (error); - -bad: - m_free(m0); - goto done; -} +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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. + */ + +#include "slirp.h" + +/* Number of packets queued before we start sending + * (to prevent allocing too many mbufs) */ +#define IF_THRESH 10 + +/* + * IP output. The packet in mbuf chain m contains a skeletal IP + * header (with len, off, ttl, proto, tos, src, dst). + * The mbuf chain containing the packet will be freed. + * The mbuf opt, if present, will not be freed. + */ +int ip_output(struct socket *so, struct mbuf *m0) +{ + Slirp *slirp = m0->slirp; + M_DUP_DEBUG(slirp, m0, 0, 0); + + register struct ip *ip; + register struct mbuf *m = m0; + register int hlen = sizeof(struct ip); + int len, off, error = 0; + + DEBUG_CALL("ip_output"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m0 = %p", m0); + + ip = mtod(m, struct ip *); + /* + * Fill in IP header. + */ + ip->ip_v = IPVERSION; + ip->ip_off &= IP_DF; + ip->ip_id = htons(slirp->ip_id++); + ip->ip_hl = hlen >> 2; + + /* + * If small enough for interface, can just send directly. + */ + if ((uint16_t)ip->ip_len <= slirp->if_mtu) { + ip->ip_len = htons((uint16_t)ip->ip_len); + ip->ip_off = htons((uint16_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; + goto bad; + } + + len = (slirp->if_mtu - hlen) & ~7; /* ip databytes per packet */ + if (len < 8) { + error = -1; + goto bad; + } + + { + int mhlen, firstlen = len; + struct mbuf **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 < (uint16_t)ip->ip_len; off += len) { + register struct ip *mhip; + m = m_get(slirp); + if (m == NULL) { + error = -1; + goto sendorfree; + } + m->m_data += IF_MAXLINKHDR; + mhip = mtod(m, struct ip *); + *mhip = *ip; + + 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 >= (uint16_t)ip->ip_len) + len = (uint16_t)ip->ip_len - off; + else + mhip->ip_off |= IP_MF; + mhip->ip_len = htons((uint16_t)(len + mhlen)); + + if (m_copy(m, m0, off, len) < 0) { + error = -1; + goto sendorfree; + } + + mhip->ip_off = htons((uint16_t)mhip->ip_off); + mhip->ip_sum = 0; + mhip->ip_sum = cksum(m, mhlen); + *mnext = m; + mnext = &m->m_nextpkt; + } + /* + * 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 - (uint16_t)ip->ip_len); + ip->ip_len = htons((uint16_t)m->m_len); + ip->ip_off = htons((uint16_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 = NULL; + if (error == 0) + if_output(so, m); + else + m_free(m); + } + } + +done: + return (error); + +bad: + m_free(m0); + goto done; +} diff --git a/libslirp/src/libslirp-version.h.in b/libslirp/src/libslirp-version.h.in new file mode 100644 index 000000000..faa6c8595 --- /dev/null +++ b/libslirp/src/libslirp-version.h.in @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef LIBSLIRP_VERSION_H_ +#define LIBSLIRP_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SLIRP_MAJOR_VERSION @SLIRP_MAJOR_VERSION@ +#define SLIRP_MINOR_VERSION @SLIRP_MINOR_VERSION@ +#define SLIRP_MICRO_VERSION @SLIRP_MICRO_VERSION@ +#define SLIRP_VERSION_STRING @SLIRP_VERSION_STRING@ + +#define SLIRP_CHECK_VERSION(major,minor,micro) \ + (SLIRP_MAJOR_VERSION > (major) || \ + (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION > (minor)) || \ + (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION == (minor) && \ + SLIRP_MICRO_VERSION >= (micro))) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSLIRP_VERSION_H_ */ diff --git a/libslirp/src/libslirp.h b/libslirp/src/libslirp.h new file mode 100644 index 000000000..240dfe8bc --- /dev/null +++ b/libslirp/src/libslirp.h @@ -0,0 +1,426 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef LIBSLIRP_H +#define LIBSLIRP_H + +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#include +#include +#include + +typedef SSIZE_T slirp_ssize_t; +#ifdef LIBSLIRP_STATIC +# define SLIRP_EXPORT +#elif defined(BUILDING_LIBSLIRP) +# define SLIRP_EXPORT __declspec(dllexport) +#else +# define SLIRP_EXPORT __declspec(dllimport) +#endif +#else +#include +typedef ssize_t slirp_ssize_t; +#include +#include +#define SLIRP_EXPORT +#endif + +#include "libslirp-version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define SLIRP_DEPRECATED __attribute__((__deprecated__)) +#else +#define SLIRP_DEPRECATED +#endif + +/* Socket abstraction:*/ + +#if !defined(_WIN32) +/* Traditional Unix socket. */ +typedef int slirp_os_socket; +#define SLIRP_INVALID_SOCKET (-1) +#define SLIRP_PRIfd "d" +#else +/* Windows: Win64 is a LLP64 platform, sizeof(int) < sizeof(long long) == sizeof(void *). + * + * Windows likes to pass HANDLE types around, which are pointers (aka unsigned long longs), + * which cannot be represented as ints. And, MS, in its infinite wisdom, decided to use + * a SOCKET handle instead of an int for socket library calls. */ +typedef SOCKET slirp_os_socket; +#define SLIRP_INVALID_SOCKET INVALID_SOCKET +#if defined(_WIN64) +# define SLIRP_PRIfd "llx" +#else +# define SLIRP_PRIfd "x" +#endif +#endif + +/* Opaque structure containing the slirp state */ +typedef struct Slirp Slirp; + +/* Flags passed to SlirpAddPollCb and to be returned by SlirpGetREventsCb. */ +enum { + SLIRP_POLL_IN = 1 << 0, + SLIRP_POLL_OUT = 1 << 1, + SLIRP_POLL_PRI = 1 << 2, + SLIRP_POLL_ERR = 1 << 3, + SLIRP_POLL_HUP = 1 << 4, +}; + +/* Debugging flags. */ +enum { + SLIRP_DBG_CALL = 1 << 0, + SLIRP_DBG_MISC = 1 << 1, + SLIRP_DBG_ERROR = 1 << 2, + SLIRP_DBG_TFTP = 1 << 3, + SLIRP_DBG_VERBOSE_CALL = 1 << 4, +}; + +/* Callback for application to get data from the guest */ +typedef slirp_ssize_t (*SlirpReadCb)(void *buf, size_t len, void *opaque); +/* Callback for application to send data to the guest */ +typedef slirp_ssize_t (*SlirpWriteCb)(const void *buf, size_t len, void *opaque); +/* Timer callback */ +typedef void (*SlirpTimerCb)(void *opaque); +/* This is deprecated, use SlirpAddPollSocketCb instead */ +typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque); +/* Callback for libslirp to register polling callbacks */ +typedef int (*SlirpAddPollSocketCb)(slirp_os_socket fd, int events, void *opaque); +/* Callback for libslirp to get polling result */ +typedef int (*SlirpGetREventsCb)(int idx, void *opaque); + +/* For now libslirp creates only a timer for the IPv6 RA */ +typedef enum SlirpTimerId { + SLIRP_TIMER_RA, + SLIRP_TIMER_NUM, +} SlirpTimerId; + +/* + * Callbacks from slirp, to be set by the application. + * + * The opaque parameter is set to the opaque pointer given in the slirp_new / + * slirp_init call. + */ +typedef struct SlirpCb { + /* + * Send an ethernet frame to the guest network. The opaque parameter is the + * one given to slirp_init(). If the guest is not ready to receive a frame, + * the function can just drop the data. TCP will then handle retransmissions + * at a lower pace. + * <0 reports an IO error. + */ + SlirpWriteCb send_packet; + /* Print a message for an error due to guest misbehavior. */ + void (*guest_error)(const char *msg, void *opaque); + /* Return the virtual clock value in nanoseconds */ + int64_t (*clock_get_ns)(void *opaque); + /* Create a new timer with the given callback and opaque data. Not + * needed if timer_new_opaque is provided. */ + void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque); + /* Remove and free a timer */ + void (*timer_free)(void *timer, void *opaque); + /* Modify a timer to expire at @expire_time (ms) */ + void (*timer_mod)(void *timer, int64_t expire_time, void *opaque); + /* Deprecated, use register_poll_socket instead */ + void (*register_poll_fd)(int fd, void *opaque) SLIRP_DEPRECATED; + /* Deprecated, use unregister_poll_socket instead */ + void (*unregister_poll_fd)(int fd, void *opaque) SLIRP_DEPRECATED; + /* Kick the io-thread, to signal that new events may be processed because some TCP buffer + * can now receive more data, i.e. slirp_socket_can_recv will return 1. */ + void (*notify)(void *opaque); + + /* + * Fields introduced in SlirpConfig version 4 begin + */ + + /* Initialization has completed and a Slirp* has been created. */ + void (*init_completed)(Slirp *slirp, void *opaque); + /* Create a new timer. When the timer fires, the application passes + * the SlirpTimerId and cb_opaque to slirp_handle_timer. */ + void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque); + + /* + * Fields introduced in SlirpConfig version 6 begin + */ + /* Register a socket for future polling */ + void (*register_poll_socket)(slirp_os_socket socket, void *opaque); + /* Unregister a socket */ + void (*unregister_poll_socket)(slirp_os_socket socket, void *opaque); +} SlirpCb; + +#define SLIRP_CONFIG_VERSION_MIN 1 +#define SLIRP_CONFIG_VERSION_MAX 6 + +typedef struct SlirpConfig { + /* Version must be provided */ + uint32_t version; + /* + * Fields introduced in SlirpConfig version 1 begin + */ + /* Whether to prevent the guest from accessing the Internet */ + int restricted; + /* Whether IPv4 is enabled */ + bool in_enabled; + /* Virtual network for the guest */ + struct in_addr vnetwork; + /* Mask for the virtual network for the guest */ + struct in_addr vnetmask; + /* Virtual address for the host exposed to the guest */ + struct in_addr vhost; + /* Whether IPv6 is enabled */ + bool in6_enabled; + /* Virtual IPv6 network for the guest */ + struct in6_addr vprefix_addr6; + /* Len of the virtual IPv6 network for the guest */ + uint8_t vprefix_len; + /* Virtual address for the host exposed to the guest */ + struct in6_addr vhost6; + /* Hostname exposed to the guest in DHCP hostname option */ + const char *vhostname; + /* Hostname exposed to the guest in the DHCP TFTP server name option */ + const char *tftp_server_name; + /* Path of the files served by TFTP */ + const char *tftp_path; + /* Boot file name exposed to the guest via DHCP */ + const char *bootfile; + /* Start of the DHCP range */ + struct in_addr vdhcp_start; + /* Virtual address for the DNS server exposed to the guest */ + struct in_addr vnameserver; + /* Virtual IPv6 address for the DNS server exposed to the guest */ + struct in6_addr vnameserver6; + /* DNS search names exposed to the guest via DHCP */ + const char **vdnssearch; + /* Domain name exposed to the guest via DHCP */ + const char *vdomainname; + /* MTU when sending packets to the guest */ + /* Default: IF_MTU_DEFAULT */ + size_t if_mtu; + /* MRU when receiving packets from the guest */ + /* Default: IF_MRU_DEFAULT */ + size_t if_mru; + /* Prohibit connecting to 127.0.0.1:* */ + bool disable_host_loopback; + /* + * Enable emulation code (*warning*: this code isn't safe, it is not + * recommended to enable it) + */ + bool enable_emu; + + /* + * Fields introduced in SlirpConfig version 2 begin + */ + /* Address to be used when sending data to the Internet */ + struct sockaddr_in *outbound_addr; + /* IPv6 Address to be used when sending data to the Internet */ + struct sockaddr_in6 *outbound_addr6; + + /* + * Fields introduced in SlirpConfig version 3 begin + */ + /* slirp will not redirect/serve any DNS packet */ + bool disable_dns; + + /* + * Fields introduced in SlirpConfig version 4 begin + */ + /* slirp will not reply to any DHCP requests */ + bool disable_dhcp; + + /* + * Fields introduced in SlirpConfig version 5 begin + */ + /* Manufacturer ID (IANA Private Enterprise number) */ + uint32_t mfr_id; + /* + * MAC address allocated for an out-of-band management controller, to be + * retrieved through NC-SI. + */ + uint8_t oob_eth_addr[6]; +} SlirpConfig; + +/* Create a new instance of a slirp stack */ +SLIRP_EXPORT +Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, + void *opaque); +/* slirp_init is deprecated in favor of slirp_new */ +SLIRP_EXPORT +Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, + struct in_addr vnetmask, struct in_addr vhost, + bool in6_enabled, struct in6_addr vprefix_addr6, + uint8_t vprefix_len, struct in6_addr vhost6, + const char *vhostname, const char *tftp_server_name, + const char *tftp_path, const char *bootfile, + struct in_addr vdhcp_start, struct in_addr vnameserver, + struct in6_addr vnameserver6, const char **vdnssearch, + const char *vdomainname, const SlirpCb *callbacks, + void *opaque); +/* Shut down an instance of a slirp stack */ +SLIRP_EXPORT +void slirp_cleanup(Slirp *slirp); + +/* This is deprecated, use slirp_pollfds_fill_socket instead. */ +SLIRP_EXPORT +void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout, + SlirpAddPollCb add_poll, void *opaque) SLIRP_DEPRECATED; + +/* This is called by the application when it is about to sleep through poll(). + * *timeout is set to the amount of virtual time (in ms) that the application intends to + * wait (UINT32_MAX if infinite). slirp_pollfds_fill updates it according to + * e.g. TCP timers, so the application knows it should sleep a smaller amount of + * time. slirp_pollfds_fill calls add_poll for each file descriptor + * that should be monitored along the sleep. The opaque pointer is passed as + * such to add_poll, and add_poll returns an index. */ +SLIRP_EXPORT +void slirp_pollfds_fill_socket(Slirp *slirp, uint32_t *timeout, + SlirpAddPollSocketCb add_poll, void *opaque); + +/* This is called by the application after sleeping, to report which file + * descriptors are available. slirp_pollfds_poll calls get_revents on each file + * descriptor, giving it the index that add_poll returned during the + * slirp_pollfds_fill call, to know whether the descriptor is available for + * read/write/etc. (SLIRP_POLL_*) + * select_error should be passed 1 if poll() returned an error. */ +SLIRP_EXPORT +void slirp_pollfds_poll(Slirp *slirp, int select_error, + SlirpGetREventsCb get_revents, void *opaque); + +/* This is called by the application when the guest emits a packet on the + * guest network, to be interpreted by slirp. */ +SLIRP_EXPORT +void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); + +/* This is called by the application when a timer expires, if it provides + * the timer_new_opaque callback. It is not needed if the application only + * uses timer_new. */ +SLIRP_EXPORT +void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque); + +/* These set up / remove port forwarding between a host port in the real world + * and the guest network. + * Note: guest_addr must be in network order, while guest_port must be in host + * order. + */ +SLIRP_EXPORT +int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port, struct in_addr guest_addr, int guest_port); +SLIRP_EXPORT +int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port); + +#define SLIRP_HOSTFWD_UDP 1 +#define SLIRP_HOSTFWD_V6ONLY 2 +SLIRP_EXPORT +int slirp_add_hostxfwd(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *gaddr, socklen_t gaddrlen, + int flags); +SLIRP_EXPORT +int slirp_remove_hostxfwd(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + int flags); + +/* Set up port forwarding between a port in the guest network and a + * command running on the host */ +SLIRP_EXPORT +int slirp_add_exec(Slirp *slirp, const char *cmdline, + struct in_addr *guest_addr, int guest_port); +/* Set up port forwarding between a port in the guest network and a + * Unix port on the host */ +SLIRP_EXPORT +int slirp_add_unix(Slirp *slirp, const char *unixsock, + struct in_addr *guest_addr, int guest_port); +/* Set up port forwarding between a port in the guest network and a + * callback that will receive the data coming from the port */ +SLIRP_EXPORT +int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque, + struct in_addr *guest_addr, int guest_port); + +/* TODO: rather identify a guestfwd through an opaque pointer instead of through + * the guest_addr */ + +/* This is called by the application for a guestfwd, to determine how much data + * can be received by the forwarded port through a call to slirp_socket_recv. */ +SLIRP_EXPORT +size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, + int guest_port); +/* This is called by the application for a guestfwd, to provide the data to be + * sent on the forwarded port */ +SLIRP_EXPORT +void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, + const uint8_t *buf, int size); + +/* Remove entries added by slirp_add_exec, slirp_add_unix or slirp_add_guestfwd */ +SLIRP_EXPORT +int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr, + int guest_port); + +/* Return a human-readable state of the slirp stack */ +SLIRP_EXPORT +char *slirp_connection_info(Slirp *slirp); + +/* Return a human-readable state of the NDP/ARP tables */ +SLIRP_EXPORT +char *slirp_neighbor_info(Slirp *slirp); + +/* Save the slirp state through the write_cb. The opaque pointer is passed as + * such to the write_cb. */ +SLIRP_EXPORT +int slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque); + +/* Returns the version of the slirp state, to be saved along the state */ +SLIRP_EXPORT +int slirp_state_version(void); + +/* Load the slirp state through the read_cb. The opaque pointer is passed as + * such to the read_cb. The version should be given as it was obtained from + * slirp_state_version when slirp_state_save was called. */ +SLIRP_EXPORT +int slirp_state_load(Slirp *s, int version_id, SlirpReadCb read_cb, + void *opaque); + +/* Return the version of the slirp implementation */ +SLIRP_EXPORT +const char *slirp_version_string(void); + +/* Debugging support: There are two methods for enabling debugging + * in libslirp: the SLIRP_DEBUG environment variable and the + * slirp_(set|reset)_debug() functions. + * + * SLIRP_DEBUG is a list of debug options separated by colons, spaces + * or commas. Valid debug options are 'call', 'misc', 'error', 'tftp' + * and 'verbose_call'. + */ + +/* Set debugging flags independently of the SLIRP_DEBUG environment + * variable. */ +SLIRP_EXPORT +void slirp_set_debug(unsigned int flags); + +/* Reset debugging flags. */ +SLIRP_EXPORT +void slirp_reset_debug(unsigned int flags); + +#if defined(_WIN32) +/* Windows utility functions: */ + +/* inet_aton() replacement that uses inet_pton(). Eliminates the dreaded + * winsock2 deprecation messages. */ +SLIRP_EXPORT +int slirp_inet_aton(const char *cp, struct in_addr *ia); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LIBSLIRP_H */ diff --git a/libslirp/src/libslirp.map b/libslirp/src/libslirp.map new file mode 100644 index 000000000..315bdf093 --- /dev/null +++ b/libslirp/src/libslirp.map @@ -0,0 +1,46 @@ +SLIRP_4.0 { +global: + slirp_add_exec; + slirp_add_guestfwd; + slirp_add_hostfwd; + slirp_cleanup; + slirp_connection_info; + slirp_init; + slirp_input; + slirp_pollfds_fill; + slirp_pollfds_poll; + slirp_remove_hostfwd; + slirp_socket_can_recv; + slirp_socket_recv; + slirp_state_load; + slirp_state_save; + slirp_state_version; + slirp_version_string; +local: + *; +}; + +SLIRP_4.1 { + slirp_new; +} SLIRP_4.0; + +SLIRP_4.2 { + slirp_add_unix; + slirp_remove_guestfwd; +} SLIRP_4.1; + +SLIRP_4.5 { + slirp_add_hostxfwd; + slirp_remove_hostxfwd; + slirp_neighbor_info; +} SLIRP_4.2; + +SLIRP_4.7 { + slirp_handle_timer; +} SLIRP_4.5; + +SLIRP_4.9 { + slirp_pollfds_fill_socket; + slirp_set_debug; + slirp_reset_debug; +} SLIRP_4.7; diff --git a/libslirp/src/libslirp.test.map b/libslirp/src/libslirp.test.map new file mode 100644 index 000000000..773376bbe --- /dev/null +++ b/libslirp/src/libslirp.test.map @@ -0,0 +1,9 @@ +SLIRP_4.0 { +global: + main; +local: + *; +}; + +SLIRP_4.1 { +} SLIRP_4.0; diff --git a/libslirp/src/main.h b/libslirp/src/main.h new file mode 100644 index 000000000..ca36277e0 --- /dev/null +++ b/libslirp/src/main.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef SLIRP_MAIN_H +#define SLIRP_MAIN_H + +#include "libslirp.h" + +/* The current guest virtual time */ +extern unsigned curtime; +/* Always equal to INADDR_LOOPBACK, in network order */ +extern struct in_addr loopback_addr; +/* Always equal to IN_CLASSA_NET, in network order */ +extern unsigned long loopback_mask; + +/* Send a packet to the guest */ +int if_encap(Slirp *slirp, struct mbuf *ifm); +/* Send a frame to the guest. Flags are passed to the send() call */ +slirp_ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags); + +#endif diff --git a/libslirp/src/mbuf.c b/libslirp/src/mbuf.c new file mode 100644 index 000000000..5ccbda313 --- /dev/null +++ b/libslirp/src/mbuf.c @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski + */ + +/* + * 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 g_malloced buffer is pointed to + * by m_ext (and the data pointers) and M_EXT is set in + * the flags + */ + +#include "slirp.h" + +#define MBUF_THRESH 30 + +/* + * Find a nice value for msize + */ +#define SLIRP_MSIZE(mtu) \ + (offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + (mtu)) + +void m_init(Slirp *slirp) +{ + slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist; + slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist; +} + +static void m_cleanup_list(struct slirp_quehead *list_head, bool pkts) +{ + struct mbuf *m, *next, *next2; + bool last; + + m = (struct mbuf *)list_head->qh_link; + while ((struct slirp_quehead *)m != list_head) { + next = m->m_next; + + last = false; + while (1) { + next2 = m->m_nextpkt; + + if (pkts) { + ifs_remque(m); + last = next2 == m; + } else { + last = true; + } + + if (m->m_flags & M_EXT) { + g_free(m->m_ext); + } + + g_free(m); + + if (last) + break; + m = next2; + }; + + m = next; + } + list_head->qh_link = list_head; + list_head->qh_rlink = list_head; +} + +void m_cleanup(Slirp *slirp) +{ + m_cleanup_list(&slirp->m_usedlist, false); + m_cleanup_list(&slirp->m_freelist, false); + m_cleanup_list(&slirp->if_batchq, true); + m_cleanup_list(&slirp->if_fastq, true); +} + +/* + * Get an mbuf from the free list, if there are none + * allocate 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 g_free() it + */ +struct mbuf *m_get(Slirp *slirp) +{ + register struct mbuf *m; + int flags = 0; + + DEBUG_CALL("m_get"); + + if (MBUF_DEBUG || slirp->m_freelist.qh_link == &slirp->m_freelist) { + m = g_malloc(SLIRP_MSIZE(slirp->if_mtu)); + slirp->mbuf_alloced++; + if (MBUF_DEBUG || slirp->mbuf_alloced > MBUF_THRESH) + flags = M_DOFREE; + m->slirp = slirp; + } else { + m = (struct mbuf *)slirp->m_freelist.qh_link; + slirp_remque(m); + } + + /* Insert it in the used list */ + slirp_insque(m, &slirp->m_usedlist); + m->m_flags = (flags | M_USEDLIST); + + /* Initialise it */ + m->m_size = SLIRP_MSIZE(slirp->if_mtu) - offsetof(struct mbuf, m_dat); + m->m_data = m->m_dat; + m->m_len = 0; + m->m_nextpkt = NULL; + m->m_prevpkt = NULL; + m->resolution_requested = false; + m->expiration_date = (uint64_t)-1; + DEBUG_ARG("m = %p", m); + return m; +} + +void m_free(struct mbuf *m) +{ + DEBUG_CALL("m_free"); + DEBUG_ARG("m = %p", m); + + if (m) { + /* Remove from m_usedlist */ + if (m->m_flags & M_USEDLIST) + slirp_remque(m); + + /* If it's M_EXT, free() it */ + if (m->m_flags & M_EXT) { + g_free(m->m_ext); + m->m_flags &= ~M_EXT; + } + /* + * Either free() it or put it on the free list + */ + if (m->m_flags & M_DOFREE) { + m->slirp->mbuf_alloced--; + g_free(m); + } else if ((m->m_flags & M_FREELIST) == 0) { + slirp_insque(m, &m->slirp->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, allocate + * an M_EXT data segment + */ +void m_cat(struct mbuf *m, struct mbuf *n) +{ + /* + * If there's no room, realloc + */ + if (M_FREEROOM(m) < n->m_len) + m_inc(m, m->m_len + n->m_len); + + 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 from m_data */ +void m_inc(struct mbuf *m, int size) +{ + int gapsize; + + /* some compilers throw up on gotos. This one we can fake. */ + if (M_ROOM(m) >= size) { + return; + } + + if (m->m_flags & M_EXT) { + gapsize = m->m_data - m->m_ext; + m->m_ext = g_realloc(m->m_ext, size + gapsize); + } else { + gapsize = m->m_data - m->m_dat; + m->m_ext = g_malloc(size + gapsize); + memcpy(m->m_ext, m->m_dat, m->m_size); + m->m_flags |= M_EXT; + } + + m->m_data = m->m_ext + gapsize; + m->m_size = size + gapsize; +} + + +void m_adj(struct mbuf *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(struct mbuf *n, struct mbuf *m, int off, int 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; +} + + +struct mbuf *dtom(Slirp *slirp, void *dat) +{ + struct mbuf *m; + + DEBUG_CALL("dtom"); + DEBUG_ARG("dat = %p", dat); + + /* bug corrected for M_EXT buffers */ + for (m = (struct mbuf *)slirp->m_usedlist.qh_link; + (struct slirp_quehead *)m != &slirp->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("dtom failed"); + + return (struct mbuf *)0; +} + +struct mbuf *m_dup(Slirp *slirp, struct mbuf *m, + bool copy_header, + size_t header_size) +{ + struct mbuf *n; + int mcopy_result; + + /* The previous mbuf was supposed to have it already, we can check it along + * the way */ + assert(M_ROOMBEFORE(m) >= header_size); + + n = m_get(slirp); + m_inc(n, m->m_len + header_size); + + if (copy_header) { + m->m_len += header_size; + m->m_data -= header_size; + mcopy_result = m_copy(n, m, 0, m->m_len); + n->m_data += header_size; + n->m_len -= header_size; + m->m_len -= header_size; + m->m_data += header_size; + } else { + n->m_data += header_size; + mcopy_result = m_copy(n, m, 0, m->m_len); + } + g_assert(mcopy_result == 0); + + return n; +} + +void *mtod_check(struct mbuf *m, size_t len) +{ + if (m->m_len >= len) { + return m->m_data; + } + + DEBUG_ERROR("mtod failed"); + + return NULL; +} + +void *m_end(struct mbuf *m) +{ + return m->m_data + m->m_len; +} diff --git a/libslirp/src/mbuf.h b/libslirp/src/mbuf.h new file mode 100644 index 000000000..ff1ddc962 --- /dev/null +++ b/libslirp/src/mbuf.h @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 + +/* + * Macros for type conversion + * mtod(m,t) - convert mbuf pointer to data pointer of correct type + */ +#define mtod(m, t) ((t)(m)->m_data) + +/* 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. + */ + +/* + * mbufs allow to have a gap between the start of the allocated buffer (m_ext if + * M_EXT is set, m_dat otherwise) and the in-use data: + * + * |--gapsize----->|---m_len-------> + * |----------m_size------------------------------> + * |----M_ROOM--------------------> + * |-M_FREEROOM--> + * + * ^ ^ ^ + * m_dat/m_ext m_data end of buffer + */ + +/* + * 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) + +/* + * How much free room there is before m_data + */ +#define M_ROOMBEFORE(m) \ + (((m)->m_flags & M_EXT) ? (m)->m_data - (m)->m_ext \ + : (m)->m_data - (m)->m_dat) + +struct mbuf { + /* XXX should union some of these! */ + /* header at beginning of each mbuf: */ + struct mbuf *m_next; /* Linked list of mbufs */ + struct mbuf *m_prev; + struct mbuf *m_nextpkt; /* Next packet in queue/record */ + struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ + int m_flags; /* Misc flags */ + + int m_size; /* Size of mbuf, from m_dat or m_ext */ + struct socket *m_so; + + char *m_data; /* Current location of data */ + int m_len; /* Amount of data in this mbuf, from m_data */ + + Slirp *slirp; + bool resolution_requested; + uint64_t expiration_date; + char *m_ext; + /* start of dynamic buffer area, must be last element */ + char m_dat[]; +}; + +static inline void ifs_remque(struct mbuf *ifm) +{ + ifm->m_prevpkt->m_nextpkt = ifm->m_nextpkt; + ifm->m_nextpkt->m_prevpkt = ifm->m_prevpkt; +} + +#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 */ + +/* Called by slirp_new */ +void m_init(Slirp *); + +/* Called by slirp_cleanup */ +void m_cleanup(Slirp *slirp); + +/* Allocate an mbuf */ +struct mbuf *m_get(Slirp *); + +/* Release an mbuf (put possibly put it in allocation cache */ +void m_free(struct mbuf *); + +/* Catenate the second buffer to the end of the first buffer, and release the second */ +void m_cat(struct mbuf *, struct mbuf *); + +/* Grow the mbuf to the given size */ +void m_inc(struct mbuf *, int); + +/* If len is positive, trim that amount from the head of the mbuf. If it is negative, trim it from the tail of the mbuf */ +void m_adj(struct mbuf *, int len); + +/* Copy len bytes from the first buffer at the given offset, to the end of the second buffer */ +int m_copy(struct mbuf *, struct mbuf *, int off, int len); + +/* + * Duplicate the mbuf + * + * copy_header specifies whether the bytes before m_data should also be copied. + * header_size specifies how many bytes are to be reserved before m_data. + */ +struct mbuf *m_dup(Slirp *slirp, struct mbuf *m, bool copy_header, size_t header_size); + +/* + * 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 mbuf *dtom(Slirp *, void *); + +/* Check that the mbuf contains at least len bytes, and return the data */ +void *mtod_check(struct mbuf *, size_t len); + +/* Return the end of the data of the mbuf */ +void *m_end(struct mbuf *); + +/* Initialize the ifs queue of the mbuf */ +static inline void ifs_init(struct mbuf *ifm) +{ + ifm->m_nextpkt = ifm->m_prevpkt = ifm; +} + +#ifdef SLIRP_DEBUG +# define MBUF_DEBUG 1 +#else +# ifdef HAVE_VALGRIND +# include +# define MBUF_DEBUG RUNNING_ON_VALGRIND +# else +# define MBUF_DEBUG 0 +# endif +#endif + +/* + * When a function is given an mbuf as well as the responsibility to free it, we + * want valgrind etc. to properly identify the new responsible for the + * free. Achieve this by making a new copy. For instance: + * + * f0(void) { + * struct mbuf *m = m_get(slirp); + * [...] + * switch (something) { + * case 1: + * f1(m); + * break; + * case 2: + * f2(m); + * break; + * [...] + * } + * } + * + * f1(struct mbuf *m) { + * M_DUP_DEBUG(m->slirp, m); + * [...] + * m_free(m); // but author of f1 might be forgetting this + * } + * + * f0 transfers the freeing responsibility to f1, f2, etc. Without the + * M_DUP_DEBUG call in f1, valgrind would tell us that it is f0 where the buffer + * was allocated, but it's difficult to know whether a leak is actually in f0, + * or in f1, or in f2, etc. Duplicating the mbuf in M_DUP_DEBUG each time the + * responsibility is transferred allows to immediately know where the leak + * actually is. + */ +#define M_DUP_DEBUG(slirp, m, copy_header, header_size) do { \ + if (MBUF_DEBUG) { \ + struct mbuf *__n; \ + __n = m_dup((slirp), (m), (copy_header), (header_size)); \ + m_free(m); \ + (m) = __n; \ + } else { \ + (void) (slirp); (void) (copy_header); \ + g_assert(M_ROOMBEFORE(m) >= (header_size)); \ + } \ +} while(0) + +#endif diff --git a/libslirp/src/misc.c b/libslirp/src/misc.c new file mode 100644 index 000000000..a30e75388 --- /dev/null +++ b/libslirp/src/misc.c @@ -0,0 +1,469 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#include "slirp.h" +#ifdef G_OS_UNIX +#include +#endif +#if defined(_WIN32) +#include +#endif + +void slirp_insque(void *a, void *b) +{ + register struct slirp_quehead *element = (struct slirp_quehead *)a; + register struct slirp_quehead *head = (struct slirp_quehead *)b; + element->qh_link = head->qh_link; + head->qh_link = (struct slirp_quehead *)element; + element->qh_rlink = (struct slirp_quehead *)head; + ((struct slirp_quehead *)(element->qh_link))->qh_rlink = + (struct slirp_quehead *)element; +} + +void slirp_remque(void *a) +{ + register struct slirp_quehead *element = (struct slirp_quehead *)a; + ((struct slirp_quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; + ((struct slirp_quehead *)(element->qh_rlink))->qh_link = element->qh_link; + element->qh_rlink = NULL; +} + +/* TODO: IPv6 */ +struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb, + void *opaque, struct in_addr addr, int port) +{ + struct gfwd_list *f = g_new0(struct gfwd_list, 1); + + f->write_cb = write_cb; + f->opaque = opaque; + f->ex_fport = port; + f->ex_addr = addr; + f->ex_next = *ex_ptr; + *ex_ptr = f; + + return f; +} + +struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline, + struct in_addr addr, int port) +{ + struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port); + + f->ex_exec = g_strdup(cmdline); + + return f; +} + +struct gfwd_list *add_unix(struct gfwd_list **ex_ptr, const char *unixsock, + struct in_addr addr, int port) +{ + struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port); + + f->ex_unix = g_strdup(unixsock); + + return f; +} + +int remove_guestfwd(struct gfwd_list **ex_ptr, struct in_addr addr, int port) +{ + for (; *ex_ptr != NULL; ex_ptr = &((*ex_ptr)->ex_next)) { + struct gfwd_list *f = *ex_ptr; + if (f->ex_addr.s_addr == addr.s_addr && f->ex_fport == port) { + *ex_ptr = f->ex_next; + g_free(f->ex_exec); + g_free(f); + return 0; + } + } + return -1; +} + +static int slirp_socketpair_with_oob(slirp_os_socket sv[2]) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + socklen_t addrlen = sizeof(addr); + int ret; + slirp_os_socket s; + + sv[1] = SLIRP_INVALID_SOCKET; + s = slirp_socket(AF_INET, SOCK_STREAM, 0); + if (not_valid_socket(s) || bind(s, (struct sockaddr *)&addr, addrlen) < 0 || + listen(s, 1) < 0 || + getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) { + goto err; + } + + sv[1] = slirp_socket(AF_INET, SOCK_STREAM, 0); + if (not_valid_socket(sv[1])) { + goto err; + } + /* + * This connect won't block because we've already listen()ed on + * the server end (even though we won't accept() the connection + * until later on). + */ + do { + ret = connect(sv[1], (struct sockaddr *)&addr, addrlen); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + goto err; + } + + do { + sv[0] = accept(s, (struct sockaddr *)&addr, &addrlen); + } while (sv[0] < 0 && errno == EINTR); + if (sv[0] < 0) { + goto err; + } + + closesocket(s); + return 0; + +err: + g_critical("slirp_socketpair(): %s", g_strerror(errno)); + if (have_valid_socket(s)) { + closesocket(s); + } + if (sv[1] >= 0) { + closesocket(sv[1]); + } + return -1; +} + +static void fork_exec_child_setup(gpointer data) +{ +#ifndef _WIN32 + setsid(); + + /* Unblock all signals and leave our exec()-ee to block what it wants */ + sigset_t ss; + sigemptyset(&ss); + sigprocmask(SIG_SETMASK, &ss, NULL); + + /* POSIX is obnoxious about SIGCHLD specifically across exec() */ + signal(SIGCHLD, SIG_DFL); +#else +#ifdef GLIB_UNUSED_PARAM + GLIB_UNUSED_PARAM(data); +#endif +#endif +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +#if !GLIB_CHECK_VERSION(2, 58, 0) +typedef struct SlirpGSpawnFds { + GSpawnChildSetupFunc child_setup; + gpointer user_data; + gint stdin_fd; + gint stdout_fd; + gint stderr_fd; +} SlirpGSpawnFds; + +static inline void slirp_gspawn_fds_setup(gpointer user_data) +{ + SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data; + +#if !defined(_WIN32) + dup2(q->stdin_fd, 0); + dup2(q->stdout_fd, 1); + dup2(q->stderr_fd, 2); +#else + _dup2(q->stdin_fd, 0); + _dup2(q->stdout_fd, 1); + _dup2(q->stderr_fd, 2); +#endif + q->child_setup(q->user_data); +} +#endif + +static inline gboolean +g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv, + gchar **envp, GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + gpointer user_data, GPid *child_pid, gint stdin_fd, + gint stdout_fd, gint stderr_fd, GError **error) +{ +#if GLIB_CHECK_VERSION(2, 58, 0) + return g_spawn_async_with_fds(working_directory, argv, envp, flags, + child_setup, user_data, child_pid, stdin_fd, + stdout_fd, stderr_fd, error); +#else + SlirpGSpawnFds setup = { + .child_setup = child_setup, + .user_data = user_data, + .stdin_fd = stdin_fd, + .stdout_fd = stdout_fd, + .stderr_fd = stderr_fd, + }; + + return g_spawn_async(working_directory, argv, envp, flags, + slirp_gspawn_fds_setup, &setup, child_pid, error); +#endif +} + +#define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \ + g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +int fork_exec(struct socket *so, const char *ex) +{ + GError *err = NULL; + gint argc = 0; + gchar **argv = NULL; + int opt; + slirp_os_socket sp[2]; + + DEBUG_CALL("fork_exec"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("ex = %p", ex); + + if (slirp_socketpair_with_oob(sp) < 0) { + return 0; + } + + if (!g_shell_parse_argv(ex, &argc, &argv, &err)) { + g_critical("fork_exec invalid command: %s\nerror: %s", ex, err->message); + g_error_free(err); + return 0; + } + + g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */, + G_SPAWN_SEARCH_PATH, fork_exec_child_setup, + NULL /* data */, NULL /* child_pid */, sp[1], sp[1], + sp[1], &err); + g_strfreev(argv); + + if (err) { + g_critical("fork_exec: %s", err->message); + g_error_free(err); + closesocket(sp[0]); + closesocket(sp[1]); + return 0; + } + + so->s = sp[0]; + closesocket(sp[1]); + slirp_socket_set_fast_reuse(so->s); + opt = 1; + setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, (const void *) &opt, sizeof(int)); + slirp_set_nonblock(so->s); + slirp_register_poll_socket(so); + return 1; +} + +int open_unix(struct socket *so, const char *unixpath) +{ +#ifdef G_OS_UNIX + struct sockaddr_un sa; + int s; + + DEBUG_CALL("open_unix"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("unixpath = %s", unixpath); + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_UNIX; + if (g_strlcpy(sa.sun_path, unixpath, sizeof(sa.sun_path)) >= sizeof(sa.sun_path)) { + g_critical("Bad unix path: %s", unixpath); + return 0; + } + + s = slirp_socket(PF_UNIX, SOCK_STREAM, 0); + if (not_valid_socket(s)) { + g_critical("open_unix(): %s", g_strerror(errno)); + return 0; + } + + if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + g_critical("open_unix(): %s", g_strerror(errno)); + closesocket(s); + return 0; + } + + so->s = s; + slirp_set_nonblock(so->s); + slirp_register_poll_socket(so); + + return 1; +#else +#ifdef GLIB_UNUSED_PARAM + GLIB_UNUSED_PARAM(so); + GLIB_UNUSED_PARAM(unixpath); +#endif + g_assert_not_reached(); +#endif +} + +SLIRP_EXPORT +char *slirp_connection_info(Slirp *slirp) +{ + GString *str = g_string_new(NULL); + const char *const tcpstates[] = { + [TCPS_CLOSED] = "CLOSED", [TCPS_LISTEN] = "LISTEN", + [TCPS_SYN_SENT] = "SYN_SENT", [TCPS_SYN_RECEIVED] = "SYN_RCVD", + [TCPS_ESTABLISHED] = "ESTABLISHED", [TCPS_CLOSE_WAIT] = "CLOSE_WAIT", + [TCPS_FIN_WAIT_1] = "FIN_WAIT_1", [TCPS_CLOSING] = "CLOSING", + [TCPS_LAST_ACK] = "LAST_ACK", [TCPS_FIN_WAIT_2] = "FIN_WAIT_2", + [TCPS_TIME_WAIT] = "TIME_WAIT", + }; + struct in_addr dst_addr; + struct sockaddr_in src; + socklen_t src_len; + uint16_t dst_port; + struct socket *so; + const char *state; + char addr[INET_ADDRSTRLEN]; + char buf[20]; + + g_string_append_printf(str, + " Protocol[State] FD Source Address Port " + "Dest. Address Port RecvQ SendQ\n"); + + /* TODO: IPv6 */ + + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { + if (so->so_state & SS_HOSTFWD) { + state = "HOST_FORWARD"; + } else if (so->so_tcpcb) { + state = tcpstates[so->so_tcpcb->t_state]; + } else { + state = "NONE"; + } + if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { + src_len = sizeof(src); + getsockname(so->s, (struct sockaddr *)&src, &src_len); + dst_addr = so->so_laddr; + dst_port = so->so_lport; + } else { + src.sin_addr = so->so_laddr; + src.sin_port = so->so_lport; + dst_addr = so->so_faddr; + dst_port = so->so_fport; + } + slirp_fmt0(buf, sizeof(buf), " TCP[%s]", state); + g_string_append_printf(str, "%-19s %3"SLIRP_PRIfd" %15s %5d ", buf, so->s, + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*", + ntohs(src.sin_port)); + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), + ntohs(dst_port), so->so_rcv.sb_cc, + so->so_snd.sb_cc); + } + + for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { + if (so->so_state & SS_HOSTFWD) { + slirp_fmt0(buf, sizeof(buf), " UDP[HOST_FORWARD]"); + src_len = sizeof(src); + getsockname(so->s, (struct sockaddr *)&src, &src_len); + dst_addr = so->so_laddr; + dst_port = so->so_lport; + } else { + slirp_fmt0(buf, sizeof(buf), " UDP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + src.sin_port = so->so_lport; + dst_addr = so->so_faddr; + dst_port = so->so_fport; + } + g_string_append_printf(str, "%-19s %3"SLIRP_PRIfd" %15s %5d ", buf, so->s, + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*", + ntohs(src.sin_port)); + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), + ntohs(dst_port), so->so_rcv.sb_cc, + so->so_snd.sb_cc); + } + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { + slirp_fmt0(buf, sizeof(buf), " ICMP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + dst_addr = so->so_faddr; + g_string_append_printf(str, "%-19s %3"SLIRP_PRIfd" %15s - ", buf, so->s, + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*"); + g_string_append_printf(str, "%15s - %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } + + return g_string_free(str, FALSE); +} + +SLIRP_EXPORT +char *slirp_neighbor_info(Slirp *slirp) +{ + GString *str = g_string_new(NULL); + ArpTable *arp_table = &slirp->arp_table; + NdpTable *ndp_table = &slirp->ndp_table; + char ip_addr[INET6_ADDRSTRLEN]; + char eth_addr[ETH_ADDRSTRLEN]; + const char *ip; + + g_string_append_printf(str, " %5s %-17s %s\n", + "Table", "MacAddr", "IP Address"); + + for (int i = 0; i < ARP_TABLE_SIZE; ++i) { + struct in_addr addr; + addr.s_addr = arp_table->table[i].ar_sip; + if (!addr.s_addr) { + continue; + } + ip = inet_ntop(AF_INET, &addr, ip_addr, sizeof(ip_addr)); + g_assert(ip != NULL); + g_string_append_printf(str, " %5s %-17s %s\n", "ARP", + slirp_ether_ntoa(arp_table->table[i].ar_sha, + eth_addr, sizeof(eth_addr)), + ip); + } + + for (int i = 0; i < NDP_TABLE_SIZE; ++i) { + if (in6_zero(&ndp_table->table[i].ip_addr)) { + continue; + } + ip = inet_ntop(AF_INET6, &ndp_table->table[i].ip_addr, ip_addr, + sizeof(ip_addr)); + g_assert(ip != NULL); + g_string_append_printf(str, " %5s %-17s %s\n", "NDP", + slirp_ether_ntoa(ndp_table->table[i].eth_addr, + eth_addr, sizeof(eth_addr)), + ip); + } + + return g_string_free(str, FALSE); +} + +int slirp_bind_outbound(struct socket *so, unsigned short af) +{ + int ret = 0; + const struct sockaddr *addr = NULL; + int addr_size = 0; + + if (af == AF_INET && so->slirp->outbound_addr != NULL) { + addr = (struct sockaddr *)so->slirp->outbound_addr; + addr_size = sizeof(struct sockaddr_in); + } else if (af == AF_INET6 && so->slirp->outbound_addr6 != NULL) { + addr = (struct sockaddr *)so->slirp->outbound_addr6; + addr_size = sizeof(struct sockaddr_in6); + } + + if (addr != NULL) { + ret = bind(so->s, addr, addr_size); + } + return ret; +} diff --git a/libslirp/src/misc.h b/libslirp/src/misc.h new file mode 100644 index 000000000..39fe367a6 --- /dev/null +++ b/libslirp/src/misc.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef MISC_H +#define MISC_H + +#include "libslirp.h" + +struct gfwd_list { + SlirpWriteCb write_cb; + void *opaque; + struct in_addr ex_addr; /* Server address */ + int ex_fport; /* Port to telnet to */ + char *ex_exec; /* Command line of what to exec */ + char *ex_unix; /* unix socket */ + struct gfwd_list *ex_next; +}; + +#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_NOCONNECT 0x10 /* Don't connect */ + +struct tos_t { + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; +}; + +struct emu_t { + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; + struct emu_t *next; +}; + +struct slirp_quehead { + struct slirp_quehead *qh_link; + struct slirp_quehead *qh_rlink; +}; + +/* Insert element a into queue b */ +void slirp_insque(void *a, void *b); + +/* Remove element a from its queue */ +void slirp_remque(void *a); + +/* Run the given command in the background, and expose its output as a socket */ +int fork_exec(struct socket *so, const char *ex); + +/* Create a Unix socket, and expose it as a socket */ +int open_unix(struct socket *so, const char *unixsock); + +/* Add a guest forward on the given address and port, with guest data being + * forwarded by calling write_cb */ +struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb, + void *opaque, struct in_addr addr, int port); + +/* Run the given command in the backaground, and send its output to the guest on + * the given address and port */ +struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline, + struct in_addr addr, int port); + +/* Create a Unix socket, and expose it to the guest on the given address and + * port */ +struct gfwd_list *add_unix(struct gfwd_list **ex_ptr, const char *unixsock, + struct in_addr addr, int port); + +/* Remove the guest forward bound to the given address and port */ +int remove_guestfwd(struct gfwd_list **ex_ptr, struct in_addr addr, int port); + +/* Bind the socket to the outbound address specified in the slirp configuration */ +int slirp_bind_outbound(struct socket *so, unsigned short af); + +#endif diff --git a/libslirp/src/ncsi-pkt.h b/libslirp/src/ncsi-pkt.h new file mode 100644 index 000000000..27bedf6a9 --- /dev/null +++ b/libslirp/src/ncsi-pkt.h @@ -0,0 +1,527 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright Gavin Shan, IBM Corporation 2016. + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ + +#ifndef NCSI_PKT_H +#define NCSI_PKT_H + +/* from linux/net/ncsi/ncsi-pkt.h */ +#define __be32 uint32_t +#define __be16 uint16_t + +SLIRP_PACKED_BEGIN +struct ncsi_pkt_hdr { + unsigned char mc_id; /* Management controller ID */ + unsigned char revision; /* NCSI version - 0x01 */ + unsigned char reserved; /* Reserved */ + unsigned char id; /* Packet sequence number */ + unsigned char type; /* Packet type */ + unsigned char channel; /* Network controller ID */ + __be16 length; /* Payload length */ + __be32 reserved1[2]; /* Reserved */ +} SLIRP_PACKED_END; + +SLIRP_PACKED_BEGIN +struct ncsi_cmd_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ +} SLIRP_PACKED_END; + +SLIRP_PACKED_BEGIN +struct ncsi_rsp_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + __be16 code; /* Response code */ + __be16 reason; /* Response reason */ +} SLIRP_PACKED_END; + +SLIRP_PACKED_BEGIN +struct ncsi_aen_pkt_hdr { + struct ncsi_pkt_hdr common; /* Common NCSI packet header */ + unsigned char reserved2[3]; /* Reserved */ + unsigned char type; /* AEN packet type */ +} SLIRP_PACKED_END; + +/* NCSI common command packet */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 checksum; /* Checksum */ + unsigned char pad[26]; +} SLIRP_PACKED_END; + +SLIRP_PACKED_BEGIN +struct ncsi_rsp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Select Package */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_sp_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char hw_arbitration; /* HW arbitration */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Disable Channel */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_dc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char ald; /* Allow link down */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Reset Channel */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_rc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 reserved; /* Reserved */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* AEN Enable */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_ae_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mc_id; /* MC ID */ + __be32 mode; /* AEN working mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +} SLIRP_PACKED_END; + +/* Set Link */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_sl_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Link working mode */ + __be32 oem_mode; /* OEM link mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +} SLIRP_PACKED_END; + +/* Set VLAN Filter */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_svf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be16 reserved; /* Reserved */ + __be16 vlan; /* VLAN ID */ + __be16 reserved1; /* Reserved */ + unsigned char index; /* VLAN table index */ + unsigned char enable; /* Enable or disable */ + __be32 checksum; /* Checksum */ + unsigned char pad[14]; +} SLIRP_PACKED_END; + +/* Enable VLAN */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_ev_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* VLAN filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Set MAC Address */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_sma_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char mac[6]; /* MAC address */ + unsigned char index; /* MAC table index */ + unsigned char at_e; /* Addr type and operation */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +} SLIRP_PACKED_END; + +/* Enable Broadcast Filter */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_ebf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Filter mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Enable Global Multicast Filter */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_egmf_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mode; /* Global MC mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* Set NCSI Flow Control */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_snfc_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + unsigned char reserved[3]; /* Reserved */ + unsigned char mode; /* Flow control mode */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* OEM Request Command as per NCSI Specification */ +SLIRP_PACKED_BEGIN +struct ncsi_cmd_oem_pkt { + struct ncsi_cmd_pkt_hdr cmd; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* OEM Payload Data */ +} SLIRP_PACKED_END; + +/* OEM Response Packet as per NCSI Specification */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_oem_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Command header */ + __be32 mfr_id; /* Manufacture ID */ + unsigned char data[]; /* Payload data */ +} SLIRP_PACKED_END; + +/* Mellanox Response Data */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_oem_mlx_pkt { + unsigned char cmd_rev; /* Command Revision */ + unsigned char cmd; /* Command ID */ + unsigned char param; /* Parameter */ + unsigned char optional; /* Optional data */ + unsigned char data[]; /* Data */ +} SLIRP_PACKED_END; + +/* Get Link Status */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gls_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Link status */ + __be32 other; /* Other indications */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; + unsigned char pad[10]; +} SLIRP_PACKED_END; + +/* Get Version ID */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gvi_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 ncsi_version; /* NCSI version */ + unsigned char reserved[3]; /* Reserved */ + unsigned char alpha2; /* NCSI version */ + unsigned char fw_name[12]; /* f/w name string */ + __be32 fw_version; /* f/w version */ + __be16 pci_ids[4]; /* PCI IDs */ + __be32 mf_id; /* Manufacture ID */ + __be32 checksum; +} SLIRP_PACKED_END; + +/* Get Capabilities */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gc_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cap; /* Capabilities */ + __be32 bc_cap; /* Broadcast cap */ + __be32 mc_cap; /* Multicast cap */ + __be32 buf_cap; /* Buffering cap */ + __be32 aen_cap; /* AEN cap */ + unsigned char vlan_cnt; /* VLAN filter count */ + unsigned char mixed_cnt; /* Mix filter count */ + unsigned char mc_cnt; /* MC filter count */ + unsigned char uc_cnt; /* UC filter count */ + unsigned char reserved[2]; /* Reserved */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char channel_cnt; /* Channel count */ + __be32 checksum; /* Checksum */ +} SLIRP_PACKED_END; + +/* Get Parameters */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gp_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char mac_cnt; /* Number of MAC addr */ + unsigned char reserved[2]; /* Reserved */ + unsigned char mac_enable; /* MAC addr enable flags */ + unsigned char vlan_cnt; /* VLAN tag count */ + unsigned char reserved1; /* Reserved */ + __be16 vlan_enable; /* VLAN tag enable flags */ + __be32 link_mode; /* Link setting */ + __be32 bc_mode; /* BC filter mode */ + __be32 valid_modes; /* Valid mode parameters */ + unsigned char vlan_mode; /* VLAN mode */ + unsigned char fc_mode; /* Flow control mode */ + unsigned char reserved2[2]; /* Reserved */ + __be32 aen_mode; /* AEN mode */ + unsigned char mac[6]; /* Supported MAC addr */ + __be16 vlan; /* Supported VLAN tags */ + __be32 checksum; /* Checksum */ +} SLIRP_PACKED_END; + +/* Get Controller Packet Statistics */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gcps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 cnt_hi; /* Counter cleared */ + __be32 cnt_lo; /* Counter cleared */ + __be32 rx_bytes; /* Rx bytes */ + __be32 tx_bytes; /* Tx bytes */ + __be32 rx_uc_pkts; /* Rx UC packets */ + __be32 rx_mc_pkts; /* Rx MC packets */ + __be32 rx_bc_pkts; /* Rx BC packets */ + __be32 tx_uc_pkts; /* Tx UC packets */ + __be32 tx_mc_pkts; /* Tx MC packets */ + __be32 tx_bc_pkts; /* Tx BC packets */ + __be32 fcs_err; /* FCS errors */ + __be32 align_err; /* Alignment errors */ + __be32 false_carrier; /* False carrier detection */ + __be32 runt_pkts; /* Rx runt packets */ + __be32 jabber_pkts; /* Rx jabber packets */ + __be32 rx_pause_xon; /* Rx pause XON frames */ + __be32 rx_pause_xoff; /* Rx XOFF frames */ + __be32 tx_pause_xon; /* Tx XON frames */ + __be32 tx_pause_xoff; /* Tx XOFF frames */ + __be32 tx_s_collision; /* Single collision frames */ + __be32 tx_m_collision; /* Multiple collision frames */ + __be32 l_collision; /* Late collision frames */ + __be32 e_collision; /* Excessive collision frames */ + __be32 rx_ctl_frames; /* Rx control frames */ + __be32 rx_64_frames; /* Rx 64-bytes frames */ + __be32 rx_127_frames; /* Rx 65-127 bytes frames */ + __be32 rx_255_frames; /* Rx 128-255 bytes frames */ + __be32 rx_511_frames; /* Rx 256-511 bytes frames */ + __be32 rx_1023_frames; /* Rx 512-1023 bytes frames */ + __be32 rx_1522_frames; /* Rx 1024-1522 bytes frames */ + __be32 rx_9022_frames; /* Rx 1523-9022 bytes frames */ + __be32 tx_64_frames; /* Tx 64-bytes frames */ + __be32 tx_127_frames; /* Tx 65-127 bytes frames */ + __be32 tx_255_frames; /* Tx 128-255 bytes frames */ + __be32 tx_511_frames; /* Tx 256-511 bytes frames */ + __be32 tx_1023_frames; /* Tx 512-1023 bytes frames */ + __be32 tx_1522_frames; /* Tx 1024-1522 bytes frames */ + __be32 tx_9022_frames; /* Tx 1523-9022 bytes frames */ + __be32 rx_valid_bytes; /* Rx valid bytes */ + __be32 rx_runt_pkts; /* Rx error runt packets */ + __be32 rx_jabber_pkts; /* Rx error jabber packets */ + __be32 checksum; /* Checksum */ +} SLIRP_PACKED_END; + +/* Get NCSI Statistics */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gns_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 rx_cmds; /* Rx NCSI commands */ + __be32 dropped_cmds; /* Dropped commands */ + __be32 cmd_type_errs; /* Command type errors */ + __be32 cmd_csum_errs; /* Command checksum errors */ + __be32 rx_pkts; /* Rx NCSI packets */ + __be32 tx_pkts; /* Tx NCSI packets */ + __be32 tx_aen_pkts; /* Tx AEN packets */ + __be32 checksum; /* Checksum */ +} SLIRP_PACKED_END; + +/* Get NCSI Pass-through Statistics */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gnpts_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 tx_pkts; /* Tx packets */ + __be32 tx_dropped; /* Tx dropped packets */ + __be32 tx_channel_err; /* Tx channel errors */ + __be32 tx_us_err; /* Tx undersize errors */ + __be32 rx_pkts; /* Rx packets */ + __be32 rx_dropped; /* Rx dropped packets */ + __be32 rx_channel_err; /* Rx channel errors */ + __be32 rx_us_err; /* Rx undersize errors */ + __be32 rx_os_err; /* Rx oversize errors */ + __be32 checksum; /* Checksum */ +} SLIRP_PACKED_END; + +/* Get package status */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gps_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + __be32 status; /* Hardware arbitration status */ + __be32 checksum; +} SLIRP_PACKED_END; + +/* Get package UUID */ +SLIRP_PACKED_BEGIN +struct ncsi_rsp_gpuuid_pkt { + struct ncsi_rsp_pkt_hdr rsp; /* Response header */ + unsigned char uuid[16]; /* UUID */ + __be32 checksum; +} SLIRP_PACKED_END; + +/* AEN: Link State Change */ +SLIRP_PACKED_BEGIN +struct ncsi_aen_lsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Link status */ + __be32 oem_status; /* OEM link status */ + __be32 checksum; /* Checksum */ + unsigned char pad[14]; +} SLIRP_PACKED_END; + +/* AEN: Configuration Required */ +SLIRP_PACKED_BEGIN +struct ncsi_aen_cr_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 checksum; /* Checksum */ + unsigned char pad[22]; +} SLIRP_PACKED_END; + +/* AEN: Host Network Controller Driver Status Change */ +SLIRP_PACKED_BEGIN +struct ncsi_aen_hncdsc_pkt { + struct ncsi_aen_pkt_hdr aen; /* AEN header */ + __be32 status; /* Status */ + __be32 checksum; /* Checksum */ + unsigned char pad[18]; +} SLIRP_PACKED_END; + +/* NCSI packet revision */ +#define NCSI_PKT_REVISION 0x01 + +/* NCSI packet commands */ +#define NCSI_PKT_CMD_CIS 0x00 /* Clear Initial State */ +#define NCSI_PKT_CMD_SP 0x01 /* Select Package */ +#define NCSI_PKT_CMD_DP 0x02 /* Deselect Package */ +#define NCSI_PKT_CMD_EC 0x03 /* Enable Channel */ +#define NCSI_PKT_CMD_DC 0x04 /* Disable Channel */ +#define NCSI_PKT_CMD_RC 0x05 /* Reset Channel */ +#define NCSI_PKT_CMD_ECNT 0x06 /* Enable Channel Network Tx */ +#define NCSI_PKT_CMD_DCNT 0x07 /* Disable Channel Network Tx */ +#define NCSI_PKT_CMD_AE 0x08 /* AEN Enable */ +#define NCSI_PKT_CMD_SL 0x09 /* Set Link */ +#define NCSI_PKT_CMD_GLS 0x0a /* Get Link */ +#define NCSI_PKT_CMD_SVF 0x0b /* Set VLAN Filter */ +#define NCSI_PKT_CMD_EV 0x0c /* Enable VLAN */ +#define NCSI_PKT_CMD_DV 0x0d /* Disable VLAN */ +#define NCSI_PKT_CMD_SMA 0x0e /* Set MAC address */ +#define NCSI_PKT_CMD_EBF 0x10 /* Enable Broadcast Filter */ +#define NCSI_PKT_CMD_DBF 0x11 /* Disable Broadcast Filter */ +#define NCSI_PKT_CMD_EGMF 0x12 /* Enable Global Multicast Filter */ +#define NCSI_PKT_CMD_DGMF 0x13 /* Disable Global Multicast Filter */ +#define NCSI_PKT_CMD_SNFC 0x14 /* Set NCSI Flow Control */ +#define NCSI_PKT_CMD_GVI 0x15 /* Get Version ID */ +#define NCSI_PKT_CMD_GC 0x16 /* Get Capabilities */ +#define NCSI_PKT_CMD_GP 0x17 /* Get Parameters */ +#define NCSI_PKT_CMD_GCPS 0x18 /* Get Controller Packet Statistics */ +#define NCSI_PKT_CMD_GNS 0x19 /* Get NCSI Statistics */ +#define NCSI_PKT_CMD_GNPTS 0x1a /* Get NCSI Pass-throu Statistics */ +#define NCSI_PKT_CMD_GPS 0x1b /* Get package status */ +#define NCSI_PKT_CMD_OEM 0x50 /* OEM */ +#define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */ +#define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */ + +/* NCSI packet responses */ +#define NCSI_PKT_RSP_CIS (NCSI_PKT_CMD_CIS + 0x80) +#define NCSI_PKT_RSP_SP (NCSI_PKT_CMD_SP + 0x80) +#define NCSI_PKT_RSP_DP (NCSI_PKT_CMD_DP + 0x80) +#define NCSI_PKT_RSP_EC (NCSI_PKT_CMD_EC + 0x80) +#define NCSI_PKT_RSP_DC (NCSI_PKT_CMD_DC + 0x80) +#define NCSI_PKT_RSP_RC (NCSI_PKT_CMD_RC + 0x80) +#define NCSI_PKT_RSP_ECNT (NCSI_PKT_CMD_ECNT + 0x80) +#define NCSI_PKT_RSP_DCNT (NCSI_PKT_CMD_DCNT + 0x80) +#define NCSI_PKT_RSP_AE (NCSI_PKT_CMD_AE + 0x80) +#define NCSI_PKT_RSP_SL (NCSI_PKT_CMD_SL + 0x80) +#define NCSI_PKT_RSP_GLS (NCSI_PKT_CMD_GLS + 0x80) +#define NCSI_PKT_RSP_SVF (NCSI_PKT_CMD_SVF + 0x80) +#define NCSI_PKT_RSP_EV (NCSI_PKT_CMD_EV + 0x80) +#define NCSI_PKT_RSP_DV (NCSI_PKT_CMD_DV + 0x80) +#define NCSI_PKT_RSP_SMA (NCSI_PKT_CMD_SMA + 0x80) +#define NCSI_PKT_RSP_EBF (NCSI_PKT_CMD_EBF + 0x80) +#define NCSI_PKT_RSP_DBF (NCSI_PKT_CMD_DBF + 0x80) +#define NCSI_PKT_RSP_EGMF (NCSI_PKT_CMD_EGMF + 0x80) +#define NCSI_PKT_RSP_DGMF (NCSI_PKT_CMD_DGMF + 0x80) +#define NCSI_PKT_RSP_SNFC (NCSI_PKT_CMD_SNFC + 0x80) +#define NCSI_PKT_RSP_GVI (NCSI_PKT_CMD_GVI + 0x80) +#define NCSI_PKT_RSP_GC (NCSI_PKT_CMD_GC + 0x80) +#define NCSI_PKT_RSP_GP (NCSI_PKT_CMD_GP + 0x80) +#define NCSI_PKT_RSP_GCPS (NCSI_PKT_CMD_GCPS + 0x80) +#define NCSI_PKT_RSP_GNS (NCSI_PKT_CMD_GNS + 0x80) +#define NCSI_PKT_RSP_GNPTS (NCSI_PKT_CMD_GNPTS + 0x80) +#define NCSI_PKT_RSP_GPS (NCSI_PKT_CMD_GPS + 0x80) +#define NCSI_PKT_RSP_OEM (NCSI_PKT_CMD_OEM + 0x80) +#define NCSI_PKT_RSP_PLDM (NCSI_PKT_CMD_PLDM + 0x80) +#define NCSI_PKT_RSP_GPUUID (NCSI_PKT_CMD_GPUUID + 0x80) + +/* NCSI response code/reason */ +#define NCSI_PKT_RSP_C_COMPLETED 0x0000 /* Command Completed */ +#define NCSI_PKT_RSP_C_FAILED 0x0001 /* Command Failed */ +#define NCSI_PKT_RSP_C_UNAVAILABLE 0x0002 /* Command Unavailable */ +#define NCSI_PKT_RSP_C_UNSUPPORTED 0x0003 /* Command Unsupported */ +#define NCSI_PKT_RSP_R_NO_ERROR 0x0000 /* No Error */ +#define NCSI_PKT_RSP_R_INTERFACE 0x0001 /* Interface not ready */ +#define NCSI_PKT_RSP_R_PARAM 0x0002 /* Invalid Parameter */ +#define NCSI_PKT_RSP_R_CHANNEL 0x0003 /* Channel not Ready */ +#define NCSI_PKT_RSP_R_PACKAGE 0x0004 /* Package not Ready */ +#define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */ +#define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */ + +/* NCSI AEN packet type */ +#define NCSI_PKT_AEN 0xFF /* AEN Packet */ +#define NCSI_PKT_AEN_LSC 0x00 /* Link status change */ +#define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ +#define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ + +/* OEM Vendor Manufacture ID */ +#define NCSI_OEM_MFR_MLX_ID 0x8119 +#define NCSI_OEM_MFR_BCM_ID 0x113d +#define NCSI_OEM_MFR_INTEL_ID 0x157 +/* Intel specific OEM command */ +#define NCSI_OEM_INTEL_CMD_GMA 0x06 /* CMD ID for Get MAC */ +#define NCSI_OEM_INTEL_CMD_KEEP_PHY 0x20 /* CMD ID for Keep PHY up */ +/* Broadcom specific OEM Command */ +#define NCSI_OEM_BCM_CMD_GMA 0x01 /* CMD ID for Get MAC */ +/* Mellanox specific OEM Command */ +#define NCSI_OEM_MLX_CMD_GMA 0x00 /* CMD ID for Get MAC */ +#define NCSI_OEM_MLX_CMD_GMA_PARAM 0x1b /* Parameter for GMA */ +#define NCSI_OEM_MLX_CMD_SMAF 0x01 /* CMD ID for Set MC Affinity */ +#define NCSI_OEM_MLX_CMD_SMAF_PARAM 0x07 /* Parameter for SMAF */ +/* Offset in OEM request */ +#define MLX_SMAF_MAC_ADDR_OFFSET 8 /* Offset for MAC in SMAF */ +#define MLX_SMAF_MED_SUPPORT_OFFSET 14 /* Offset for medium in SMAF */ +/* Mac address offset in OEM response */ +#define BCM_MAC_ADDR_OFFSET 28 +#define MLX_MAC_ADDR_OFFSET 8 +#define INTEL_MAC_ADDR_OFFSET 1 + +/* Status offset in OEM response */ +#define MLX_GMA_STATUS_OFFSET 0 +/* OEM Response payload length */ +#define MLX_GMA_PAYLOAD_LEN 24 + +#endif /* NCSI_PKT_H */ diff --git a/libslirp/src/ncsi.c b/libslirp/src/ncsi.c new file mode 100644 index 000000000..846da9a49 --- /dev/null +++ b/libslirp/src/ncsi.c @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * NC-SI (Network Controller Sideband Interface) "echo" model + * + * Copyright (C) 2016-2018 IBM Corp. + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +#include "slirp.h" + +#include "ncsi-pkt.h" + +static uint32_t ncsi_calculate_checksum(uint8_t *data, int len) +{ + uint32_t checksum = 0; + int i; + + /* + * 32-bit unsigned sum of the NC-SI packet header and NC-SI packet + * payload interpreted as a series of 16-bit unsigned integer values. + */ + for (i = 0; i < len; i += 2) { + checksum += (((uint16_t) data[i]) << 8) + data[i+1]; + } + + checksum = (~checksum + 1); + return checksum; +} + +/* Response handler for Mellanox command Get Mac Address */ +static int ncsi_rsp_handler_oem_mlx_gma(Slirp *slirp, + const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + uint8_t oob_eth_addr_allocated = 0; + struct ncsi_rsp_oem_pkt *rsp; + int i; + + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + + /* Set the payload length */ + rsp->rsp.common.length = htons(MLX_GMA_PAYLOAD_LEN); + + for (i = 0; i < ETH_ALEN; i++) { + if (slirp->oob_eth_addr[i] != 0x00) { + oob_eth_addr_allocated = 1; + break; + } + } + rsp->data[MLX_GMA_STATUS_OFFSET] = oob_eth_addr_allocated; + + /* Set the allocated management address */ + memcpy(&rsp->data[MLX_MAC_ADDR_OFFSET], slirp->oob_eth_addr, ETH_ALEN); + + return 0; +} + +/* Response handler for Mellanox card */ +static int ncsi_rsp_handler_oem_mlx(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_cmd_oem_pkt *cmd; + const struct ncsi_rsp_oem_mlx_pkt *cmd_mlx; + struct ncsi_rsp_oem_pkt *rsp; + struct ncsi_rsp_oem_mlx_pkt *rsp_mlx; + + /* Get the command header */ + cmd = (const struct ncsi_cmd_oem_pkt *)nh; + cmd_mlx = (const struct ncsi_rsp_oem_mlx_pkt *)cmd->data; + + /* Get the response header */ + rsp = (struct ncsi_rsp_oem_pkt *)rnh; + rsp_mlx = (struct ncsi_rsp_oem_mlx_pkt *)rsp->data; + + /* Ensure the OEM response header matches the command's */ + rsp_mlx->cmd_rev = cmd_mlx->cmd_rev; + rsp_mlx->cmd = cmd_mlx->cmd; + rsp_mlx->param = cmd_mlx->param; + rsp_mlx->optional = cmd_mlx->optional; + + if (cmd_mlx->cmd == NCSI_OEM_MLX_CMD_GMA && + cmd_mlx->param == NCSI_OEM_MLX_CMD_GMA_PARAM) + return ncsi_rsp_handler_oem_mlx_gma(slirp, nh, rnh); + + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + +static const struct ncsi_rsp_oem_handler { + unsigned int mfr_id; + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); +} ncsi_rsp_oem_handlers[] = { + { NCSI_OEM_MFR_MLX_ID, ncsi_rsp_handler_oem_mlx }, + { NCSI_OEM_MFR_BCM_ID, NULL }, + { NCSI_OEM_MFR_INTEL_ID, NULL }, +}; + +/* Response handler for OEM command */ +static int ncsi_rsp_handler_oem(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + const struct ncsi_rsp_oem_handler *nrh = NULL; + const struct ncsi_cmd_oem_pkt *cmd = (const struct ncsi_cmd_oem_pkt *)nh; + struct ncsi_rsp_oem_pkt *rsp = (struct ncsi_rsp_oem_pkt *)rnh; + uint32_t mfr_id = ntohl(cmd->mfr_id); + int i; + + rsp->mfr_id = cmd->mfr_id; + + if (mfr_id != slirp->mfr_id) { + goto error; + } + + /* Check for manufacturer id and Find the handler */ + for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_oem_handlers); i++) { + if (ncsi_rsp_oem_handlers[i].mfr_id == mfr_id) { + if (ncsi_rsp_oem_handlers[i].handler) + nrh = &ncsi_rsp_oem_handlers[i]; + else + nrh = NULL; + + break; + } + } + + if (!nrh) { + goto error; + } + + /* Process the packet */ + return nrh->handler(slirp, nh, rnh); + +error: + rsp->rsp.common.length = htons(8); + rsp->rsp.code = htons(NCSI_PKT_RSP_C_UNSUPPORTED); + rsp->rsp.reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + return -ENOENT; +} + + +/* Get Version ID */ +static int ncsi_rsp_handler_gvi(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gvi_pkt *rsp = (struct ncsi_rsp_gvi_pkt *)rnh; + + rsp->ncsi_version = htonl(0xF1F0F000); + rsp->mf_id = htonl(slirp->mfr_id); + + return 0; +} + +/* Get Capabilities */ +static int ncsi_rsp_handler_gc(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gc_pkt *rsp = (struct ncsi_rsp_gc_pkt *)rnh; + + rsp->cap = htonl(~0); + rsp->bc_cap = htonl(~0); + rsp->mc_cap = htonl(~0); + rsp->buf_cap = htonl(~0); + rsp->aen_cap = htonl(~0); + rsp->vlan_mode = 0xff; + rsp->uc_cnt = 2; + return 0; +} + +/* Get Link status */ +static int ncsi_rsp_handler_gls(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gls_pkt *rsp = (struct ncsi_rsp_gls_pkt *)rnh; + + rsp->status = htonl(0x1); + return 0; +} + +/* Get Parameters */ +static int ncsi_rsp_handler_gp(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *)rnh; + + /* no MAC address filters or VLAN filters on the channel */ + rsp->mac_cnt = 0; + rsp->mac_enable = 0; + rsp->vlan_cnt = 0; + rsp->vlan_enable = 0; + + return 0; +} + +static const struct ncsi_rsp_handler { + unsigned char type; + int payload; + int (*handler)(Slirp *slirp, const struct ncsi_pkt_hdr *nh, + struct ncsi_rsp_pkt_hdr *rnh); +} ncsi_rsp_handlers[] = { { NCSI_PKT_RSP_CIS, 4, NULL }, + { NCSI_PKT_RSP_SP, 4, NULL }, + { NCSI_PKT_RSP_DP, 4, NULL }, + { NCSI_PKT_RSP_EC, 4, NULL }, + { NCSI_PKT_RSP_DC, 4, NULL }, + { NCSI_PKT_RSP_RC, 4, NULL }, + { NCSI_PKT_RSP_ECNT, 4, NULL }, + { NCSI_PKT_RSP_DCNT, 4, NULL }, + { NCSI_PKT_RSP_AE, 4, NULL }, + { NCSI_PKT_RSP_SL, 4, NULL }, + { NCSI_PKT_RSP_GLS, 16, ncsi_rsp_handler_gls }, + { NCSI_PKT_RSP_SVF, 4, NULL }, + { NCSI_PKT_RSP_EV, 4, NULL }, + { NCSI_PKT_RSP_DV, 4, NULL }, + { NCSI_PKT_RSP_SMA, 4, NULL }, + { NCSI_PKT_RSP_EBF, 4, NULL }, + { NCSI_PKT_RSP_DBF, 4, NULL }, + { NCSI_PKT_RSP_EGMF, 4, NULL }, + { NCSI_PKT_RSP_DGMF, 4, NULL }, + { NCSI_PKT_RSP_SNFC, 4, NULL }, + { NCSI_PKT_RSP_GVI, 40, ncsi_rsp_handler_gvi }, + { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, + { NCSI_PKT_RSP_GP, 40, ncsi_rsp_handler_gp }, + { NCSI_PKT_RSP_GCPS, 172, NULL }, + { NCSI_PKT_RSP_GNS, 172, NULL }, + { NCSI_PKT_RSP_GNPTS, 172, NULL }, + { NCSI_PKT_RSP_GPS, 8, NULL }, + { NCSI_PKT_RSP_OEM, 0, ncsi_rsp_handler_oem }, + { NCSI_PKT_RSP_PLDM, 0, NULL }, + { NCSI_PKT_RSP_GPUUID, 20, NULL } }; + +/* + * packet format : ncsi header + payload + checksum + */ +#define NCSI_MAX_PAYLOAD 172 +#define NCSI_MAX_LEN (sizeof(struct ncsi_pkt_hdr) + NCSI_MAX_PAYLOAD + 4) + +void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + const struct ncsi_pkt_hdr *nh = + (const struct ncsi_pkt_hdr *)(pkt + ETH_HLEN); + uint8_t ncsi_reply[2 + ETH_HLEN + NCSI_MAX_LEN]; + struct ethhdr *reh = (struct ethhdr *)(ncsi_reply + 2); + struct ncsi_rsp_pkt_hdr *rnh = + (struct ncsi_rsp_pkt_hdr *)(ncsi_reply + 2 + ETH_HLEN); + const struct ncsi_rsp_handler *handler = NULL; + int i; + int ncsi_rsp_len = sizeof(*nh); + uint32_t checksum; + uint32_t *pchecksum; + + if (pkt_len < ETH_HLEN + sizeof(struct ncsi_pkt_hdr)) { + return; /* packet too short */ + } + + memset(ncsi_reply, 0, sizeof(ncsi_reply)); + + memset(reh->h_dest, 0xff, ETH_ALEN); + memset(reh->h_source, 0xff, ETH_ALEN); + reh->h_proto = htons(ETH_P_NCSI); + + for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_handlers); i++) { + if (ncsi_rsp_handlers[i].type == nh->type + 0x80) { + handler = &ncsi_rsp_handlers[i]; + break; + } + } + + rnh->common.mc_id = nh->mc_id; + rnh->common.revision = NCSI_PKT_REVISION; + rnh->common.id = nh->id; + rnh->common.type = nh->type + 0x80; + rnh->common.channel = nh->channel; + + if (handler) { + rnh->common.length = htons(handler->payload); + rnh->code = htons(NCSI_PKT_RSP_C_COMPLETED); + rnh->reason = htons(NCSI_PKT_RSP_R_NO_ERROR); + + if (handler->handler) { + handler->handler(slirp, nh, rnh); + } + ncsi_rsp_len += ntohs(rnh->common.length); + } else { + rnh->common.length = 0; + rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); + rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); + } + + /* Add the optional checksum at the end of the frame. */ + checksum = ncsi_calculate_checksum((uint8_t *)rnh, ncsi_rsp_len); + pchecksum = (uint32_t *)((char *)rnh + ncsi_rsp_len); + *pchecksum = htonl(checksum); + ncsi_rsp_len += 4; + + slirp_send_packet_all(slirp, ncsi_reply + 2, ETH_HLEN + ncsi_rsp_len); +} diff --git a/libslirp/src/ndp_table.c b/libslirp/src/ndp_table.c new file mode 100644 index 000000000..fdb189d59 --- /dev/null +++ b/libslirp/src/ndp_table.c @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. + */ + +#include "slirp.h" + +void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, + uint8_t ethaddr[ETH_ALEN]) +{ + char addrstr[INET6_ADDRSTRLEN]; + NdpTable *ndp_table = &slirp->ndp_table; + int i; + char ethaddr_str[ETH_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); + + DEBUG_CALL("ndp_table_add"); + DEBUG_ARG("ip = %s", addrstr); + DEBUG_ARG("hw addr = %s", slirp_ether_ntoa(ethaddr, ethaddr_str, + sizeof(ethaddr_str))); + + if (IN6_IS_ADDR_MULTICAST(&ip_addr) || in6_zero(&ip_addr)) { + /* Do not register multicast or unspecified addresses */ + DEBUG_CALL(" abort: do not register multicast or unspecified address"); + return; + } + + /* Search for an entry */ + for (i = 0; i < NDP_TABLE_SIZE; i++) { + if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { + DEBUG_CALL(" already in table: update the entry"); + /* Update the entry */ + memcpy(ndp_table->table[i].eth_addr, ethaddr, ETH_ALEN); + return; + } + } + + /* No entry found, create a new one */ + DEBUG_CALL(" create new entry"); + /* Save the first entry, it is the guest. */ + if (in6_zero(&ndp_table->guest_in6_addr)) { + ndp_table->guest_in6_addr = ip_addr; + } + ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr; + memcpy(ndp_table->table[ndp_table->next_victim].eth_addr, ethaddr, + ETH_ALEN); + ndp_table->next_victim = (ndp_table->next_victim + 1) % NDP_TABLE_SIZE; +} + +bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, + uint8_t out_ethaddr[ETH_ALEN]) +{ + char addrstr[INET6_ADDRSTRLEN]; + NdpTable *ndp_table = &slirp->ndp_table; + int i; + char ethaddr_str[ETH_ADDRSTRLEN]; + + inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); + + DEBUG_CALL("ndp_table_search"); + DEBUG_ARG("ip = %s", addrstr); + + /* If unspecified address */ + if (in6_zero(&ip_addr)) { + /* return Ethernet broadcast address */ + memset(out_ethaddr, 0xff, ETH_ALEN); + return 1; + } + + /* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */ + if (IN6_IS_ADDR_MULTICAST(&ip_addr)) { + out_ethaddr[0] = 0x33; + out_ethaddr[1] = 0x33; + out_ethaddr[2] = ip_addr.s6_addr[12]; + out_ethaddr[3] = ip_addr.s6_addr[13]; + out_ethaddr[4] = ip_addr.s6_addr[14]; + out_ethaddr[5] = ip_addr.s6_addr[15]; + DEBUG_ARG("multicast addr = %s", + slirp_ether_ntoa(out_ethaddr, ethaddr_str, + sizeof(ethaddr_str))); + return 1; + } + + for (i = 0; i < NDP_TABLE_SIZE; i++) { + if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { + memcpy(out_ethaddr, ndp_table->table[i].eth_addr, ETH_ALEN); + DEBUG_ARG("found hw addr = %s", + slirp_ether_ntoa(out_ethaddr, ethaddr_str, + sizeof(ethaddr_str))); + return 1; + } + } + + DEBUG_CALL(" ip not found in table"); + return 0; +} diff --git a/libslirp/src/sbuf.c b/libslirp/src/sbuf.c new file mode 100644 index 000000000..637befadc --- /dev/null +++ b/libslirp/src/sbuf.c @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#include "slirp.h" + +#include + +static void sbappendsb(struct sbuf *sb, const struct mbuf *m); + +void sbfree(struct sbuf *sb) +{ + g_free(sb->sb_data); +} + +bool sbdrop(struct sbuf *sb, size_t num) +{ + uint32_t limit = sb->sb_datalen / 2; + + DEBUG_CALL("sbdrop"); + DEBUG_ARG("num = %" SLIRP_PRIsize_t, num); + + g_warn_if_fail(num <= sb->sb_cc); + if (num > sb->sb_cc) + num = sb->sb_cc; + + if (sizeof(num) > sizeof(int) && num > UINT32_MAX) + g_critical("sbdrop: sizeof(num) > sizeof(int), num = %" SLIRP_PRIsize_t "\n", num); + + sb->sb_cc -= (uint32_t) num; + sb->sb_rptr += num; + if (sb->sb_rptr >= sb->sb_data + sb->sb_datalen) + sb->sb_rptr -= sb->sb_datalen; + + if (sb->sb_cc < limit && sb->sb_cc + num >= limit) + return true; + + return false; +} + +void sbreserve(struct sbuf *sb, size_t size) +{ + if (sizeof(size) > sizeof(int) && size > UINT32_MAX) + g_critical("sbreserve: sizeof(size) > sizeof(int), size = %" SLIRP_PRIsize_t "\n", size); + + sb->sb_wptr = sb->sb_rptr = sb->sb_data = g_realloc(sb->sb_data, size); + sb->sb_cc = 0; + sb->sb_datalen = (uint32_t) size; +} + +void sbappend(struct socket *so, struct mbuf *m) +{ + int ret = 0; + + DEBUG_CALL("sbappend"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", 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, + * otherwise it'll arrive out of order, and hence corrupt + */ + if (!so->so_rcv.sb_cc) { + slirp_ssize_t n_sent = slirp_send(so, m->m_data, m->m_len, 0); + + /* Almost impossible for this g_critical() to execute, except in a OS or hardware + * error. */ + if (sizeof(n_sent) > sizeof(int) && n_sent > (slirp_ssize_t) m->m_len) + g_critical("sbappend: n_sent > m->m_len, n_sent = %" SLIRP_PRIssize_t "\n", n_sent); + + ret = (int) n_sent; + } + + 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 mbuf */ + m_free(m); +} + +/* + * Copy the data from m into sb + * The caller is responsible to make sure there's enough room + */ +static void sbappendsb(struct sbuf *sb, const struct mbuf *m) +{ + slirp_ssize_t len, n; + + 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 */ + slirp_ssize_t 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 += (uint32_t) n; + sb->sb_wptr += n; + if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) + sb->sb_wptr -= sb->sb_datalen; +} + +void sbcopy(struct sbuf *sb, size_t off, size_t len, char *to) +{ + char *from; + const char *right_edge = sb->sb_data + sb->sb_datalen; + slirp_ssize_t ptr_diff = sb->sb_wptr - sb->sb_rptr; + + if (ptr_diff < 0) + ptr_diff += sb->sb_datalen; + + DEBUG_CALL("sbcopy"); + DEBUG_ARG("len = %" SLIRP_PRIsize_t, len); + DEBUG_ARG("off = %" SLIRP_PRIsize_t, off); + DEBUG_ARG("sb->sb_cc = %u", sb->sb_cc); + DEBUG_ARG("ptr diff = %" SLIRP_PRIssize_t, ptr_diff); + + /* Ensure that sb->sb_cc is consistent when the read and write pointers are + * on top of each other: either the socket buffer is full or it's empty. + * + * Likewise, ensure that ptr_diff == sb->sb_cc and len + off <= sb->sb_cc. + * + * g_critical() allows us to output more info even when debugging is not + * enabled. */ + if (ptr_diff == 0 && (sb->sb_cc != 0 || sb->sb_cc != sb->sb_datalen)) { + g_critical("sbcopy: ptr_diff == 0: sb->sb_cc (%" PRIu32 ") != sb->sb_datalen (%" PRIu32 "), sb->sb_cc != 0\n", + sb->sb_cc, sb->sb_datalen); + } else if (ptr_diff != (size_t) sb->sb_cc) { + g_error("sbcopy: ptr_diff (%" SLIRP_PRIssize_t ") != sb->sb_cc (%" PRIu32 ")\n", ptr_diff, sb->sb_cc); + } else if (len + off > (size_t) sb->sb_cc) { + g_error("sbcopy: len (%" SLIRP_PRIsize_t ") + off (%" SLIRP_PRIsize_t ") > sb->sb_cc (%" PRIu32 ")\n", + len, off, sb->sb_cc); + } + + from = sb->sb_rptr + off; + /* Jumped off the sbuf's right edge? Wrap around relative to the left edge + * (i.e., modulo sb_datalen.) */ + if (from >= right_edge) + from -= sb->sb_datalen; + + if (from < sb->sb_wptr) { + /* Simple linear copy from the sbuf. */ + memcpy(to, from, len); + } else { + size_t n_right = MIN(len, (size_t) (right_edge - from)); + + /* Copy the right side of the circular buffer. */ + memcpy(to, from, n_right); + if (n_right < len) { + /* Then the left side, if anything remains to be copied. */ + memcpy(to + n_right, sb->sb_data, len - n_right); + } + } +} diff --git a/libslirp/src/sbuf.h b/libslirp/src/sbuf.h new file mode 100644 index 000000000..a3eaf4968 --- /dev/null +++ b/libslirp/src/sbuf.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef SBUF_H +#define SBUF_H + +/* How many bytes are free in the sbuf */ +#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) + +struct sbuf { + uint32_t sb_cc; /* actual chars in buffer */ + uint32_t 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 */ +}; + +/* Release the sbuf */ +void sbfree(struct sbuf *sb); + +/* Drop len bytes from the reading end of the sbuf */ +bool sbdrop(struct sbuf *sb, size_t len); + +/* (re)Allocate sbuf buffer to store size bytes */ +void sbreserve(struct sbuf *sb, size_t size); + +/* + * 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(struct socket *sb, struct mbuf *mb); + +/* + * 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(struct sbuf *sb, size_t off, size_t len, char *p); + +#endif diff --git a/libslirp/src/slirp.c b/libslirp/src/slirp.c new file mode 100644 index 000000000..bccee535b --- /dev/null +++ b/libslirp/src/slirp.c @@ -0,0 +1,1712 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp glue + * + * Copyright (c) 2004-2008 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" + +#ifndef g_warning_once +#define g_warning_once g_warning +#endif + + +#ifndef _WIN32 +#include +#endif + +/* https://gitlab.freedesktop.org/slirp/libslirp/issues/18 */ +#if defined(__NetBSD__) && defined(if_mtu) +#undef if_mtu +#endif + +#if defined(_WIN32) + +#define INITIAL_DNS_ADDR_BUF_SIZE 32 * 1024 +#define REALLOC_RETRIES 5 + +// Broadcast site local DNS resolvers. We do not use these because they are +// highly unlikely to be valid. +// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt +static const struct in6_addr SITE_LOCAL_DNS_BROADCAST_ADDRS[] = { + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }} + }, + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }} + }, + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + }} + }, +}; + +#endif + +unsigned int slirp_debug; + +/* Define to 1 if you want KEEPALIVE timers */ +bool slirp_do_keepalive; + +/* host loopback address */ +struct in_addr loopback_addr; +/* host loopback network mask */ +unsigned long loopback_mask; + +/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ +static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00, + 0x00, 0x00, 0x00 }; + +unsigned curtime; + +static struct in_addr dns_addr; +static struct in6_addr dns6_addr; +static uint32_t dns6_scope_id; +static unsigned dns_addr_time; +static unsigned dns6_addr_time; + +#define TIMEOUT_FAST 2 /* milliseconds */ +#define TIMEOUT_SLOW 499 /* milliseconds */ +/* for the aging of certain requests like DNS */ +#define TIMEOUT_DEFAULT 1000 /* milliseconds */ + +#if defined(_WIN32) + +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; + + if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { + *pdns_addr = dns_addr; + return 0; + } + + 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", (unsigned)ret); + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return -1; + } + + pIPAddr = &(FixedInfo->DnsServerList); + inet_aton(pIPAddr->IpAddress.String, &tmp_addr); + *pdns_addr = tmp_addr; + dns_addr = tmp_addr; + dns_addr_time = curtime; + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return 0; +} + +static int is_site_local_dns_broadcast(struct in6_addr *address) +{ + int i; + for (i = 0; i < G_N_ELEMENTS(SITE_LOCAL_DNS_BROADCAST_ADDRS); i++) { + if (in6_equal(address, &SITE_LOCAL_DNS_BROADCAST_ADDRS[i])) { + return 1; + } + } + return 0; +} + +static void print_dns_v6_address(struct in6_addr address) +{ + char address_str[INET6_ADDRSTRLEN] = ""; + if (inet_ntop(AF_INET6, &address, address_str, INET6_ADDRSTRLEN) + == NULL) { + DEBUG_ERROR("Failed to stringify IPv6 address for logging."); + return; + } + DEBUG_RAW_CALL("IPv6 DNS server found: %s", address_str); +} + +// Gets the first valid DNS resolver with an IPv6 address. +// Ignores any site local broadcast DNS servers, as these +// are on deprecated addresses and not generally expected +// to work. Further details at: +// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt +static int get_ipv6_dns_server(struct in6_addr *dns_server_address, uint32_t *scope_id) +{ + PIP_ADAPTER_ADDRESSES addresses = NULL; + PIP_ADAPTER_ADDRESSES address = NULL; + IP_ADAPTER_DNS_SERVER_ADDRESS *dns_server = NULL; + struct sockaddr_in6 *dns_v6_addr = NULL; + + ULONG buf_size = INITIAL_DNS_ADDR_BUF_SIZE; + DWORD res = ERROR_BUFFER_OVERFLOW; + int i; + + for (i = 0; i < REALLOC_RETRIES; i++) { + // If non null, we hit buffer overflow, free it so we can try again. + if (addresses != NULL) { + g_free(addresses); + } + + addresses = g_malloc(buf_size); + res = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, + addresses, &buf_size); + + if (res != ERROR_BUFFER_OVERFLOW) { + break; + } + } + + if (res != NO_ERROR) { + DEBUG_ERROR("Failed to get IPv6 DNS addresses due to error %lX", res); + goto failure; + } + + address = addresses; + for (address = addresses; address != NULL; address = address->Next) { + for (dns_server = address->FirstDnsServerAddress; + dns_server != NULL; + dns_server = dns_server->Next) { + + if (dns_server->Address.lpSockaddr->sa_family != AF_INET6) { + continue; + } + + dns_v6_addr = (struct sockaddr_in6 *)dns_server->Address.lpSockaddr; + if (is_site_local_dns_broadcast(&dns_v6_addr->sin6_addr) == 0) { + print_dns_v6_address(dns_v6_addr->sin6_addr); + *dns_server_address = dns_v6_addr->sin6_addr; + *scope_id = dns_v6_addr->sin6_scope_id; + + g_free(addresses); + return 0; + } + } + } + + DEBUG_ERROR("No IPv6 DNS servers found.\n"); + +failure: + g_free(addresses); + return -1; +} + +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) +{ + if (!in6_zero(&dns6_addr) && (curtime - dns6_addr_time) < TIMEOUT_DEFAULT) { + *pdns6_addr = dns6_addr; + *scope_id = dns6_scope_id; + return 0; + } + + if (get_ipv6_dns_server(pdns6_addr, scope_id) == 0) { + dns6_addr = *pdns6_addr; + dns6_addr_time = curtime; + dns6_scope_id = *scope_id; + return 0; + } + + return -1; +} + +static void winsock_cleanup(void) +{ + WSACleanup(); +} + +#elif defined(__APPLE__) + +#include + +static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, + socklen_t addrlen, unsigned *cached_time) +{ + if (curtime - *cached_time < TIMEOUT_DEFAULT) { + memcpy(pdns_addr, cached_addr, addrlen); + return 0; + } + return 1; +} + +static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr, + socklen_t addrlen, + uint32_t *scope_id, uint32_t *cached_scope_id, + unsigned *cached_time) +{ + struct __res_state state; + union res_sockaddr_union servers[NI_MAXSERV]; + int count; + int found; + void *addr; + + // we only support IPv4 and IPv4, we assume it's one or the other + assert(af == AF_INET || af == AF_INET6); + + if (res_ninit(&state) != 0) { + return -1; + } + + count = res_getservers(&state, servers, NI_MAXSERV); + found = 0; + DEBUG_MISC("IP address of your DNS(s):"); + for (int i = 0; i < count; i++) { + if (af == servers[i].sin.sin_family) { + found++; + } + if (af == AF_INET) { + addr = &servers[i].sin.sin_addr; + } else { // af == AF_INET6 + addr = &servers[i].sin6.sin6_addr; + } + + // we use the first found entry + if (found == 1) { + memcpy(pdns_addr, addr, addrlen); + memcpy(cached_addr, addr, addrlen); + if (scope_id) { + *scope_id = 0; + } + if (cached_scope_id) { + *cached_scope_id = 0; + } + *cached_time = curtime; + } + + if (found > 3) { + DEBUG_MISC(" (more)"); + break; + } else if (slirp_debug & SLIRP_DBG_MISC) { + char s[INET6_ADDRSTRLEN]; + const char *res = inet_ntop(af, addr, s, sizeof(s)); + if (!res) { + res = " (string conversion error)"; + } + DEBUG_MISC(" %s", res); + } + } + + res_ndestroy(&state); + if (!found) + return -1; + return 0; +} + +int get_dns_addr(struct in_addr *pdns_addr) +{ + if (dns_addr.s_addr != 0) { + int ret; + ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr), + &dns_addr_time); + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_libresolv(AF_INET, pdns_addr, &dns_addr, + sizeof(dns_addr), NULL, NULL, &dns_addr_time); +} + +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) +{ + if (!in6_zero(&dns6_addr)) { + int ret; + ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), + &dns6_addr_time); + if (ret == 0) { + *scope_id = dns6_scope_id; + } + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_libresolv(AF_INET6, pdns6_addr, &dns6_addr, + sizeof(dns6_addr), + scope_id, &dns6_scope_id, &dns6_addr_time); +} + +#else // !defined(_WIN32) && !defined(__APPLE__) + +#if defined(__HAIKU__) +#define RESOLV_CONF_PATH "/boot/system/settings/network/resolv.conf" +#else +#define RESOLV_CONF_PATH "/etc/resolv.conf" +#endif + +static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, + socklen_t addrlen, struct stat *cached_stat, + unsigned *cached_time) +{ + struct stat old_stat; + if (curtime - *cached_time < TIMEOUT_DEFAULT) { + memcpy(pdns_addr, cached_addr, addrlen); + return 0; + } + old_stat = *cached_stat; + if (stat(RESOLV_CONF_PATH, cached_stat) != 0) { + return -1; + } + if (cached_stat->st_dev == old_stat.st_dev && + cached_stat->st_ino == old_stat.st_ino && + cached_stat->st_size == old_stat.st_size && + cached_stat->st_mtime == old_stat.st_mtime) { + memcpy(pdns_addr, cached_addr, addrlen); + return 0; + } + return 1; +} + +static bool try_and_setdns_server(int af, unsigned found, unsigned if_index, + const char *buff2, void *pdns_addr, void *cached_addr, + socklen_t addrlen, uint32_t *scope_id, uint32_t *cached_scope_id, + unsigned *cached_time) +{ + union { + struct in_addr dns_addr; + struct in6_addr dns6_addr; + } tmp_addr; + + assert(sizeof(tmp_addr) >= addrlen); + + if (!inet_pton(af, buff2, &tmp_addr)) + return false; + + /* If it's the first one, set it to dns_addr */ + if (!found) { + memcpy(pdns_addr, &tmp_addr, addrlen); + memcpy(cached_addr, &tmp_addr, addrlen); + if (scope_id) { + *scope_id = if_index; + } + if (cached_scope_id) { + *cached_scope_id = if_index; + } + *cached_time = curtime; + } + + if (found > 2) { + DEBUG_MISC(" (more)"); + } else if (slirp_debug & SLIRP_DBG_MISC) { + char s[INET6_ADDRSTRLEN]; + const char *res = inet_ntop(af, &tmp_addr, s, sizeof(s)); + if (!res) { + res = " (string conversion error)"; + } + DEBUG_MISC(" %s", res); + } + + return true; +} + +static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr, + socklen_t addrlen, + uint32_t *scope_id, uint32_t *cached_scope_id, + unsigned *cached_time) +{ + char buff[512]; + char buff2[257]; + FILE *f; + int found = 0; + unsigned if_index; + unsigned nameservers = 0; + + f = fopen(RESOLV_CONF_PATH, "r"); + if (!f) + return -1; + + DEBUG_MISC("IP address of your DNS(s):"); + while (fgets(buff, 512, f) != NULL) { + if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { + char *c = strchr(buff2, '%'); + if (c) { + if_index = if_nametoindex(c + 1); + *c = '\0'; + } else { + if_index = 0; + } + + nameservers++; + + if (!try_and_setdns_server(af, found, if_index, buff2, pdns_addr, + cached_addr, addrlen, scope_id, + cached_scope_id, cached_time)) + continue; + + if (++found > 3) + break; + } + } + fclose(f); + if (nameservers && !found) + return -1; + if (!nameservers) { + found += try_and_setdns_server(af, found, 0, "127.0.0.1", + pdns_addr, cached_addr, addrlen, scope_id, + cached_scope_id, cached_time); + found += try_and_setdns_server(af, found, 0, "::1", + pdns_addr, cached_addr, addrlen, scope_id, + cached_scope_id, cached_time); + } + + return found ? 0 : -1; +} + +int get_dns_addr(struct in_addr *pdns_addr) +{ + static struct stat dns_addr_stat; + + if (dns_addr.s_addr != 0) { + int ret; + ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr), + &dns_addr_stat, &dns_addr_time); + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr, + sizeof(dns_addr), + NULL, NULL, &dns_addr_time); +} + +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) +{ + static struct stat dns6_addr_stat; + + if (!in6_zero(&dns6_addr)) { + int ret; + ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), + &dns6_addr_stat, &dns6_addr_time); + if (ret == 0) { + *scope_id = dns6_scope_id; + } + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr, + sizeof(dns6_addr), + scope_id, &dns6_scope_id, &dns6_addr_time); +} + +#endif + +static void slirp_init_once(void) +{ + static int initialized; + const char *debug; +#ifdef _WIN32 + WSADATA Data; +#endif + + if (initialized) { + return; + } + initialized = 1; + +#ifdef _WIN32 + WSAStartup(MAKEWORD(2, 0), &Data); + atexit(winsock_cleanup); +#endif + + loopback_addr.s_addr = htonl(INADDR_LOOPBACK); + loopback_mask = htonl(IN_CLASSA_NET); + + debug = g_getenv("SLIRP_DEBUG"); + if (debug) { + const GDebugKey keys[] = { + { "call", SLIRP_DBG_CALL }, + { "misc", SLIRP_DBG_MISC }, + { "error", SLIRP_DBG_ERROR }, + { "tftp", SLIRP_DBG_TFTP }, + { "verbose_call", SLIRP_DBG_VERBOSE_CALL }, + }; + slirp_debug = g_parse_debug_string(debug, keys, G_N_ELEMENTS(keys)); + } +} + +static void ra_timer_handler_cb(void *opaque) +{ + Slirp *slirp = opaque; + + ra_timer_handler(slirp, NULL); +} + +void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque) +{ + g_return_if_fail(id >= 0 && id < SLIRP_TIMER_NUM); + + switch (id) { + case SLIRP_TIMER_RA: + ra_timer_handler(slirp, cb_opaque); + return; + default: + abort(); + } +} + +void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque) +{ + g_return_val_if_fail(id >= 0 && id < SLIRP_TIMER_NUM, NULL); + + if (slirp->cfg_version >= 4 && slirp->cb->timer_new_opaque) { + return slirp->cb->timer_new_opaque(id, cb_opaque, slirp->opaque); + } + + switch (id) { + case SLIRP_TIMER_RA: + g_return_val_if_fail(cb_opaque == NULL, NULL); + return slirp->cb->timer_new(ra_timer_handler_cb, slirp, slirp->opaque); + + default: + abort(); + } +} + +Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque) +{ + Slirp *slirp; + + g_return_val_if_fail(cfg != NULL, NULL); + g_return_val_if_fail(cfg->version >= SLIRP_CONFIG_VERSION_MIN, NULL); + g_return_val_if_fail(cfg->version <= SLIRP_CONFIG_VERSION_MAX, NULL); + g_return_val_if_fail(cfg->if_mtu >= IF_MTU_MIN || cfg->if_mtu == 0, NULL); + g_return_val_if_fail(cfg->if_mtu <= IF_MTU_MAX, NULL); + g_return_val_if_fail(cfg->if_mru >= IF_MRU_MIN || cfg->if_mru == 0, NULL); + g_return_val_if_fail(cfg->if_mru <= IF_MRU_MAX, NULL); + g_return_val_if_fail(!cfg->bootfile || + (strlen(cfg->bootfile) < + G_SIZEOF_MEMBER(struct bootp_t, bp_file)), NULL); + + slirp = g_malloc0(sizeof(Slirp)); + + slirp_init_once(); + + slirp->cfg_version = cfg->version; + slirp->opaque = opaque; + slirp->cb = callbacks; + slirp->grand = g_rand_new(); + slirp->restricted = cfg->restricted; + + slirp->in_enabled = cfg->in_enabled; + slirp->in6_enabled = cfg->in6_enabled; + + if_init(slirp); + ip_init(slirp); + + m_init(slirp); + + slirp->vnetwork_addr = cfg->vnetwork; + slirp->vnetwork_mask = cfg->vnetmask; + slirp->vhost_addr = cfg->vhost; + slirp->vprefix_addr6 = cfg->vprefix_addr6; + slirp->vprefix_len = cfg->vprefix_len; + slirp->vhost_addr6 = cfg->vhost6; + if (cfg->vhostname) { + slirp_pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), + cfg->vhostname); + } + slirp->tftp_prefix = g_strdup(cfg->tftp_path); + slirp->bootp_filename = g_strdup(cfg->bootfile); + slirp->vdomainname = g_strdup(cfg->vdomainname); + slirp->vdhcp_startaddr = cfg->vdhcp_start; + slirp->vnameserver_addr = cfg->vnameserver; + slirp->vnameserver_addr6 = cfg->vnameserver6; + slirp->tftp_server_name = g_strdup(cfg->tftp_server_name); + + if (cfg->vdnssearch) { + translate_dnssearch(slirp, cfg->vdnssearch); + } + slirp->if_mtu = cfg->if_mtu == 0 ? IF_MTU_DEFAULT : cfg->if_mtu; + slirp->if_mru = cfg->if_mru == 0 ? IF_MRU_DEFAULT : cfg->if_mru; + slirp->disable_host_loopback = cfg->disable_host_loopback; + slirp->enable_emu = cfg->enable_emu; + + if (cfg->version >= 2) { + slirp->outbound_addr = cfg->outbound_addr; + slirp->outbound_addr6 = cfg->outbound_addr6; + } else { + slirp->outbound_addr = NULL; + slirp->outbound_addr6 = NULL; + } + + if (cfg->version >= 3) { + slirp->disable_dns = cfg->disable_dns; + } else { + slirp->disable_dns = false; + } + + if (cfg->version >= 4) { + slirp->disable_dhcp = cfg->disable_dhcp; + } else { + slirp->disable_dhcp = false; + } + + if (slirp->cfg_version >= 4 && slirp->cb->init_completed) { + slirp->cb->init_completed(slirp, slirp->opaque); + } + + if (cfg->version >= 5) { + slirp->mfr_id = cfg->mfr_id; + memcpy(slirp->oob_eth_addr, cfg->oob_eth_addr, ETH_ALEN); + } else { + slirp->mfr_id = 0; + memset(slirp->oob_eth_addr, 0, ETH_ALEN); + } + + ip6_post_init(slirp); + return slirp; +} + +Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, + struct in_addr vnetmask, struct in_addr vhost, + bool in6_enabled, struct in6_addr vprefix_addr6, + uint8_t vprefix_len, struct in6_addr vhost6, + const char *vhostname, const char *tftp_server_name, + const char *tftp_path, const char *bootfile, + struct in_addr vdhcp_start, struct in_addr vnameserver, + struct in6_addr vnameserver6, const char **vdnssearch, + const char *vdomainname, const SlirpCb *callbacks, + void *opaque) +{ + SlirpConfig cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.version = 1; + cfg.restricted = restricted; + cfg.in_enabled = in_enabled; + cfg.vnetwork = vnetwork; + cfg.vnetmask = vnetmask; + cfg.vhost = vhost; + cfg.in6_enabled = in6_enabled; + cfg.vprefix_addr6 = vprefix_addr6; + cfg.vprefix_len = vprefix_len; + cfg.vhost6 = vhost6; + cfg.vhostname = vhostname; + cfg.tftp_server_name = tftp_server_name; + cfg.tftp_path = tftp_path; + cfg.bootfile = bootfile; + cfg.vdhcp_start = vdhcp_start; + cfg.vnameserver = vnameserver; + cfg.vnameserver6 = vnameserver6; + cfg.vdnssearch = vdnssearch; + cfg.vdomainname = vdomainname; + return slirp_new(&cfg, callbacks, opaque); +} + +void slirp_cleanup(Slirp *slirp) +{ + struct gfwd_list *e, *next; + + for (e = slirp->guestfwd_list; e; e = next) { + next = e->ex_next; + g_free(e->ex_exec); + g_free(e->ex_unix); + g_free(e); + } + + ip_cleanup(slirp); + ip6_cleanup(slirp); + m_cleanup(slirp); + tftp_cleanup(slirp); + + g_rand_free(slirp->grand); + + g_free(slirp->vdnssearch); + g_free(slirp->tftp_prefix); + g_free(slirp->bootp_filename); + g_free(slirp->vdomainname); + g_free(slirp); +} + +#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) + +static void slirp_update_timeout(Slirp *slirp, uint32_t *timeout) +{ + uint32_t t; + + if (*timeout <= TIMEOUT_FAST) { + return; + } + + t = MIN(1000, *timeout); + + /* If we have tcp timeout with slirp, then we will fill @timeout with + * more precise value. + */ + if (slirp->time_fasttimo) { + *timeout = TIMEOUT_FAST; + return; + } + if (slirp->do_slowtimo) { + t = MIN(TIMEOUT_SLOW, t); + } + *timeout = t; +} + +void slirp_pollfds_fill_socket(Slirp *slirp, uint32_t *timeout, + SlirpAddPollSocketCb add_poll, void *opaque) +{ + struct socket *so, *so_next; + + /* + * First, TCP sockets + */ + + /* + * *_slowtimo needs calling if there are IP fragments + * in the fragment queue, or there are TCP connections active + */ + slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || + (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); + + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) { + int events = 0; + + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if we need a tcp_fasttimo + */ + if (slirp->time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) { + slirp->time_fasttimo = curtime; /* Flag when 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 || not_valid_socket(so->s)) { + continue; + } + + /* + * Set for reading sockets which are accepting + */ + if (so->so_state & SS_FACCEPTCONN) { + so->pollfds_idx = add_poll( + so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + continue; + } + + /* + * Set for writing sockets which are connecting + */ + if (so->so_state & SS_ISFCONNECTING) { + so->pollfds_idx = + add_poll(so->s, SLIRP_POLL_OUT | SLIRP_POLL_ERR, opaque); + 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) { + events |= SLIRP_POLL_OUT | SLIRP_POLL_ERR; + } + + /* + * Set for reading (and urgent data) if we are connected, can + * receive more, and we have room for it. + * + * If sb is already half full, we will wait for the guest to consume it, + * and notify again in sbdrop() when the sb becomes less than half full. + */ + if (CONN_CANFRCV(so) && + (so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) { + events |= SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR | + SLIRP_POLL_PRI; + } + + if (events) { + so->pollfds_idx = add_poll(so->s, events, opaque); + } + } + + /* + * UDP sockets + */ + for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + udp_detach(so); + continue; + } else { + slirp->do_slowtimo = true; /* 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) { + so->pollfds_idx = add_poll( + so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + } + } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + slirp->do_slowtimo = true; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + so->pollfds_idx = add_poll( + so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + } + } + + slirp_update_timeout(slirp, timeout); +} + +struct PollCbWrap { + SlirpAddPollCb add_poll; + void *opaque; +}; + +static int slirp_pollfds_fill_wrap(slirp_os_socket socket, int events, void *opaque) +{ + struct PollCbWrap *wrap = opaque; + int fd = (int) socket; + if ((slirp_os_socket) fd != socket) + g_warning_once("Truncating socket to int failed!"); + return wrap->add_poll(fd, events, wrap->opaque); +} + +void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout, + SlirpAddPollCb add_poll, void *opaque) +{ + struct PollCbWrap wrap = { + .add_poll = add_poll, + .opaque = opaque, + }; + slirp_pollfds_fill_socket(slirp, timeout, slirp_pollfds_fill_wrap, &wrap); +} + +void slirp_register_poll_socket(struct socket *so) +{ + Slirp *slirp = so->slirp; + int fd; + if (slirp->cfg_version >= 6 && slirp->cb->register_poll_socket) { + slirp->cb->register_poll_socket(so->s, slirp->opaque); + } else { + fd = (int) so->s; + if ((slirp_os_socket) fd != so->s) + g_warning_once("Truncating socket to int failed!"); +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + slirp->cb->register_poll_fd(fd, slirp->opaque); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } +} + +void slirp_unregister_poll_socket(struct socket *so) +{ + Slirp *slirp = so->slirp; + int fd; + if (slirp->cfg_version >= 6 && slirp->cb->unregister_poll_socket) { + slirp->cb->unregister_poll_socket(so->s, slirp->opaque); + } else { + fd = (int) so->s; + if ((slirp_os_socket) fd != so->s) + g_warning_once("Truncating socket to int failed!"); +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + slirp->cb->unregister_poll_fd(fd, slirp->opaque); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } +} + +void slirp_pollfds_poll(Slirp *slirp, int select_error, + SlirpGetREventsCb get_revents, void *opaque) +{ + struct socket *so, *so_next; + int ret; + + curtime = (unsigned int) (slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS); + + /* + * See if anything has timed out + */ + if (slirp->time_fasttimo && + ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { + tcp_fasttimo(slirp); + slirp->time_fasttimo = 0; + } + if (slirp->do_slowtimo && + ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { + ip_slowtimo(slirp); + tcp_slowtimo(slirp); + slirp->last_slowtimo = curtime; + } + + /* + * Check sockets + */ + if (!select_error) { + /* + * Check TCP sockets + */ + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (so->so_state & SS_NOFDREF || not_valid_socket(so->s)) { + continue; + } + +#ifndef __APPLE__ + /* + * Check for URG data + * This will soread as well, so no need to + * test for SLIRP_POLL_IN below if this succeeds. + * + * This is however disabled on MacOS, which apparently always + * reports data as PRI when it is the last data of the + * connection. We would then report it out of band, which the guest + * would most probably not be ready for. + */ + if (revents & SLIRP_POLL_PRI) { + ret = sorecvoob(so); + if (ret < 0) { + /* Socket error might have resulted in the socket being + * removed, do not try to do anything more with it. */ + continue; + } + } + /* + * Check sockets for reading + */ + else +#endif + if (revents & + (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR | SLIRP_POLL_PRI)) { + /* + * 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)); + } + if (ret < 0) { + /* Socket error might have resulted in the socket being + * removed, do not try to do anything more with it. */ + continue; + } + } + + /* + * Check sockets for writing + */ + if (!(so->so_state & SS_NOFDREF) && + (revents & (SLIRP_POLL_OUT | SLIRP_POLL_ERR))) { + /* + * Check for non-blocking, still-connecting sockets + */ + if (so->so_state & SS_ISFCONNECTING) { + /* Connected */ + so->so_state &= ~SS_ISFCONNECTING; + + ret = send(so->s, (const void *)&ret, 0, 0); + 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_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; + } + /* else so->so_state &= ~SS_ISFCONNECTING; */ + + /* + * Continue tcp_input + */ + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, + so->so_ffamily); + /* continue; */ + } else { + ret = sowrite(so); + if (ret > 0) { + /* Call tcp_output in case we need to send a window + * update to the guest, otherwise it will be stuck + * until it sends a window probe. */ + tcp_output(sototcpcb(so)); + } + } + } + } + + /* + * Now UDP sockets. + * Incoming packets are sent straight away, they're not buffered. + * Incoming UDP data isn't buffered either. + */ + for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (have_valid_socket(so->s) && + (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { + sorecvfrom(so); + } + } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (have_valid_socket(so->s) && + (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { + if (so->so_type == IPPROTO_IPV6 || so->so_type == IPPROTO_ICMPV6) + icmp6_receive(so); + else + icmp_receive(so); + } + } + } + + if_start(slirp); +} + +static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + const struct slirp_arphdr *ah = + (const struct slirp_arphdr *)(pkt + ETH_HLEN); + uint8_t arp_reply[MAX(2 + ETH_HLEN + sizeof(struct slirp_arphdr), 2 + 64)]; + struct ethhdr *reh = (struct ethhdr *)(arp_reply + 2); + struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_reply + 2 + ETH_HLEN); + int ar_op; + struct gfwd_list *ex_ptr; + + if (!slirp->in_enabled) { + return; + } + + if (pkt_len < ETH_HLEN + sizeof(struct slirp_arphdr)) { + return; /* packet too short */ + } + + ar_op = ntohs(ah->ar_op); + switch (ar_op) { + case ARPOP_REQUEST: + if (ah->ar_tip == ah->ar_sip) { + /* Gratuitous ARP */ + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + return; + } + + if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + if (ah->ar_tip == slirp->vnameserver_addr.s_addr || + ah->ar_tip == slirp->vhost_addr.s_addr) + goto arp_ok; + /* TODO: IPv6 */ + for (ex_ptr = slirp->guestfwd_list; ex_ptr; + ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_addr.s_addr == ah->ar_tip) + goto arp_ok; + } + return; + arp_ok: + memset(arp_reply, 0, sizeof(arp_reply)); + + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + + /* ARP request for alias/dns mac address */ + memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); + memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); + memcpy(&reh->h_source[2], &ah->ar_tip, 4); + 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); + rah->ar_sip = ah->ar_tip; + memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); + rah->ar_tip = ah->ar_sip; + slirp_send_packet_all(slirp, arp_reply + 2, sizeof(arp_reply) - 2); + } + break; + case ARPOP_REPLY: + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + break; + default: + break; + } +} + +void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + struct mbuf *m; + int proto; + + if (pkt_len < ETH_HLEN) + return; + + proto = (((uint16_t)pkt[12]) << 8) + pkt[13]; + switch (proto) { + case ETH_P_ARP: + arp_input(slirp, pkt, pkt_len); + break; + case ETH_P_IP: + case ETH_P_IPV6: + m = m_get(slirp); + if (!m) + return; + /* Note: we add 2 to align the IP header on 8 bytes despite the ethernet + * header, and add the margin for the tcpiphdr overhead */ + if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) { + m_inc(m, pkt_len + TCPIPHDR_DELTA + 2); + } + m->m_len = pkt_len + TCPIPHDR_DELTA + 2; + memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len); + + m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN; + m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN; + + if (proto == ETH_P_IP) { + ip_input(m); + } else if (proto == ETH_P_IPV6) { + ip6_input(m); + } + break; + + case ETH_P_NCSI: + ncsi_input(slirp, pkt, pkt_len); + break; + + default: + break; + } +} + +/* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no + * packet should be sent, 0 if the packet must be re-queued, 2 if the packet + * is ready to go. + */ +static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) +{ + const struct ip *iph = (const struct ip *)ifm->m_data; + + if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { + uint8_t arp_req[2 + ETH_HLEN + sizeof(struct slirp_arphdr)]; + struct ethhdr *reh = (struct ethhdr *)(arp_req + 2); + struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_req + 2 + ETH_HLEN); + + if (!ifm->resolution_requested) { + /* If the client addr is not known, send an ARP request */ + memset(reh->h_dest, 0xff, ETH_ALEN); + memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); + memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); + 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_REQUEST); + + /* source hw addr */ + memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); + memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); + + /* source IP */ + rah->ar_sip = slirp->vhost_addr.s_addr; + + /* target hw addr (none) */ + memset(rah->ar_tha, 0, ETH_ALEN); + + /* target IP */ + rah->ar_tip = iph->ip_dst.s_addr; + slirp->client_ipaddr = iph->ip_dst; + slirp_send_packet_all(slirp, arp_req + 2, sizeof(arp_req) - 2); + ifm->resolution_requested = true; + + /* Expire request and drop outgoing packet after 1 second */ + ifm->expiration_date = + slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL; + } + return 0; + } else { + memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); + /* XXX: not correct */ + memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); + eh->h_proto = htons(ETH_P_IP); + + /* Send this */ + return 2; + } +} + +/* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no + * packet should be sent, 0 if the packet must be re-queued, 2 if the packet + * is ready to go. + */ +static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) +{ + const struct ip6 *ip6h = mtod(ifm, const struct ip6 *); + if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) { + if (!ifm->resolution_requested) { + ndp_send_ns(slirp, ip6h->ip_dst); + ifm->resolution_requested = true; + ifm->expiration_date = + slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL; + } + return 0; + } else { + eh->h_proto = htons(ETH_P_IPV6); + in6_compute_ethaddr(ip6h->ip_src, eh->h_source); + + /* Send this */ + return 2; + } +} + +/* Output the IP packet to the ethernet device. Returns 0 if the packet must be + * re-queued. + */ +int if_encap(Slirp *slirp, struct mbuf *ifm) +{ + uint8_t buf[IF_MTU_MAX + 100]; + struct ethhdr *eh = (struct ethhdr *)(buf + 2); + uint8_t ethaddr[ETH_ALEN]; + const struct ip *iph = (const struct ip *)ifm->m_data; + int ret; + char ethaddr_str[ETH_ADDRSTRLEN]; + + if (ifm->m_len + ETH_HLEN > sizeof(buf) - 2) { + return 1; + } + + switch (iph->ip_v) { + case IPVERSION: + ret = if_encap4(slirp, ifm, eh, ethaddr); + if (ret < 2) { + return ret; + } + break; + + case IP6VERSION: + ret = if_encap6(slirp, ifm, eh, ethaddr); + if (ret < 2) { + return ret; + } + break; + + default: + g_assert_not_reached(); + } + + memcpy(eh->h_dest, ethaddr, ETH_ALEN); + DEBUG_ARG("src = %s", slirp_ether_ntoa(eh->h_source, ethaddr_str, + sizeof(ethaddr_str))); + DEBUG_ARG("dst = %s", slirp_ether_ntoa(eh->h_dest, ethaddr_str, + sizeof(ethaddr_str))); + memcpy(buf + 2 + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); + slirp_send_packet_all(slirp, buf + 2, ifm->m_len + ETH_HLEN); + return 1; +} + +/* Drop host forwarding rule, return 0 if found. */ +int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port) +{ + struct socket *so; + struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb); + struct sockaddr_in addr; + int port = htons(host_port); + socklen_t addr_len; + + for (so = head->so_next; so != head; so = so->so_next) { + addr_len = sizeof(addr); + if ((so->so_state & SS_HOSTFWD) && + getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && + addr_len == sizeof(addr) && + addr.sin_family == AF_INET && + addr.sin_addr.s_addr == host_addr.s_addr && + addr.sin_port == port) { + slirp_unregister_poll_socket(so); + closesocket(so->s); + sofree(so); + return 0; + } + } + + return -1; +} + +int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port, struct in_addr guest_addr, int guest_port) +{ + if (!guest_addr.s_addr) { + guest_addr = slirp->vdhcp_startaddr; + } + if (is_udp) { + if (!udp_listen(slirp, host_addr.s_addr, htons(host_port), + guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) + return -1; + } else { + if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port), + guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) + return -1; + } + return 0; +} + +int slirp_remove_hostxfwd(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + int flags) +{ + struct socket *so; + struct socket *head = (flags & SLIRP_HOSTFWD_UDP ? &slirp->udb : &slirp->tcb); + struct sockaddr_storage addr; + socklen_t addr_len; + + for (so = head->so_next; so != head; so = so->so_next) { + addr_len = sizeof(addr); + if ((so->so_state & SS_HOSTFWD) && + getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && + sockaddr_equal(&addr, (const struct sockaddr_storage *) haddr)) { + slirp_unregister_poll_socket(so); + closesocket(so->s); + sofree(so); + return 0; + } + } + + return -1; +} + +int slirp_add_hostxfwd(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *gaddr, socklen_t gaddrlen, + int flags) +{ + struct sockaddr_in gdhcp_addr; + int fwd_flags = SS_HOSTFWD; + + if (flags & SLIRP_HOSTFWD_V6ONLY) + fwd_flags |= SS_HOSTFWD_V6ONLY; + + if (gaddr->sa_family == AF_INET) { + const struct sockaddr_in *gaddr_in = (const struct sockaddr_in *) gaddr; + + if (gaddrlen < sizeof(struct sockaddr_in)) { + errno = EINVAL; + return -1; + } + + if (!gaddr_in->sin_addr.s_addr) { + gdhcp_addr = *gaddr_in; + gdhcp_addr.sin_addr = slirp->vdhcp_startaddr; + gaddr = (struct sockaddr *) &gdhcp_addr; + gaddrlen = sizeof(gdhcp_addr); + } + } else { + if (gaddrlen < sizeof(struct sockaddr_in6)) { + errno = EINVAL; + return -1; + } + + /* + * Libslirp currently only provides a stateless DHCPv6 server, thus + * we can't translate "addr-any" to the guest here. Instead, we defer + * performing the translation to when it's needed. See + * soassign_guest_addr_if_needed(). + */ + } + + if (flags & SLIRP_HOSTFWD_UDP) { + if (!udpx_listen(slirp, haddr, haddrlen, + gaddr, gaddrlen, + fwd_flags)) + return -1; + } else { + if (!tcpx_listen(slirp, haddr, haddrlen, + gaddr, gaddrlen, + fwd_flags)) + return -1; + } + return 0; +} + +/* TODO: IPv6 */ +static bool check_guestfwd(Slirp *slirp, struct in_addr *guest_addr, + int guest_port) +{ + struct gfwd_list *tmp_ptr; + + if (!guest_addr->s_addr) { + guest_addr->s_addr = slirp->vnetwork_addr.s_addr | + (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr); + } + if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) != + slirp->vnetwork_addr.s_addr || + guest_addr->s_addr == slirp->vhost_addr.s_addr || + guest_addr->s_addr == slirp->vnameserver_addr.s_addr) { + return false; + } + + /* check if the port is "bound" */ + for (tmp_ptr = slirp->guestfwd_list; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { + if (guest_port == tmp_ptr->ex_fport && + guest_addr->s_addr == tmp_ptr->ex_addr.s_addr) + return false; + } + + return true; +} + +int slirp_add_exec(Slirp *slirp, const char *cmdline, + struct in_addr *guest_addr, int guest_port) +{ + if (!check_guestfwd(slirp, guest_addr, guest_port)) { + return -1; + } + + add_exec(&slirp->guestfwd_list, cmdline, *guest_addr, htons(guest_port)); + return 0; +} + +int slirp_add_unix(Slirp *slirp, const char *unixsock, + struct in_addr *guest_addr, int guest_port) +{ +#ifdef G_OS_UNIX + if (!check_guestfwd(slirp, guest_addr, guest_port)) { + return -1; + } + + add_unix(&slirp->guestfwd_list, unixsock, *guest_addr, htons(guest_port)); + return 0; +#else + g_warn_if_reached(); + return -1; +#endif +} + +int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque, + struct in_addr *guest_addr, int guest_port) +{ + if (!check_guestfwd(slirp, guest_addr, guest_port)) { + return -1; + } + + add_guestfwd(&slirp->guestfwd_list, write_cb, opaque, *guest_addr, + htons(guest_port)); + return 0; +} + +int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr, + int guest_port) +{ + return remove_guestfwd(&slirp->guestfwd_list, guest_addr, + htons(guest_port)); +} + +slirp_ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) +{ + if (not_valid_socket(so->s) && so->guestfwd) { + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + so->guestfwd->write_cb(buf, len, so->guestfwd->opaque); + return len; + } + + if (not_valid_socket(so->s)) { + /* + * This should in theory not happen but it is hard to be + * sure because some code paths will end up with so->s == -1 + * on a failure but don't dispose of the struct socket. + * Check specifically, so we don't pass -1 to send(). + */ + errno = EBADF; + return -1; + } + + return send(so->s, buf, len, flags); +} + +struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, + int guest_port) +{ + struct socket *so; + + /* TODO: IPv6 */ + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { + if (so->so_faddr.s_addr == guest_addr.s_addr && + htons(so->so_fport) == guest_port) { + return so; + } + } + return NULL; +} + +size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, + int guest_port) +{ + struct iovec iov[2]; + struct socket *so; + + so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); + + if (!so || so->so_state & SS_NOFDREF) { + return 0; + } + + if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) { + /* If the sb is already half full, we will wait for the guest to consume it, + * and notify again in sbdrop() when the sb becomes less than half full. */ + return 0; + } + + return sopreprbuf(so, iov, NULL); +} + +void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, + const uint8_t *buf, int size) +{ + int ret; + struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); + + if (!so) + return; + + ret = soreadbuf(so, (const char *)buf, size); + + if (ret > 0) + tcp_output(sototcpcb(so)); +} + +void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len) +{ + slirp_ssize_t ret; + + if (len < ETH_MINLEN) { + char tmp[ETH_MINLEN]; + memcpy(tmp, buf, len); + memset(tmp + len, 0, ETH_MINLEN - len); + + ret = slirp->cb->send_packet(tmp, ETH_MINLEN, slirp->opaque); + } else { + ret = slirp->cb->send_packet(buf, len, slirp->opaque); + } + + if (ret < 0) { + g_critical("Failed to send packet, ret: %ld", (long)ret); + } else if (ret < len) { + DEBUG_ERROR("send_packet() didn't send all data: %ld < %lu", (long)ret, + (unsigned long)len); + } +} diff --git a/libslirp/src/slirp.h b/libslirp/src/slirp.h new file mode 100644 index 000000000..532e114d6 --- /dev/null +++ b/libslirp/src/slirp.h @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef SLIRP_H +#define SLIRP_H + +#ifdef _WIN32 + +#if defined(TARGET_WINVER) +/* TARGET_WINVER defined on the compiler command line? */ + +# undef WINVER +# undef _WIN32_WINNT +# define WINVER TARGET_WINVER +# define _WIN32_WINNT TARGET_WINVER + +#elif !defined(WINVER) +/* Default WINVER to Windows 7 API, same as glib. */ +# undef _WIN32_WINNT + +# define WINVER 0x0601 +# define _WIN32_WINNT WINVER + +#endif + +/* Ensure that _WIN32_WINNT matches WINVER */ +#if defined(WINVER) && !defined(_WIN32_WINNT) +#define _WIN32_WINNT WINVER +#endif + +/* reduces the number of implicitly included headers */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#include + +/* Paranoia includes: */ +#include +#include +#include + +#else +#define O_BINARY 0 +#endif + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + +#ifdef __APPLE__ +#include +#endif + +#include "debug.h" +#include "util.h" + +#include "libslirp.h" +#include "ip.h" +#include "ip6.h" +#include "tcp.h" +#include "tcp_timer.h" +#include "tcp_var.h" +#include "tcpip.h" +#include "udp.h" +#include "ip_icmp.h" +#include "ip6_icmp.h" +#include "mbuf.h" +#include "sbuf.h" +#include "socket.h" +#include "if.h" +#include "main.h" +#include "misc.h" + +#include "bootp.h" +#include "tftp.h" + +#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 */ +}; + +SLIRP_PACKED_BEGIN +struct slirp_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... + */ + uint8_t ar_sha[ETH_ALEN]; /* sender hardware address */ + uint32_t ar_sip; /* sender IP address */ + uint8_t ar_tha[ETH_ALEN]; /* target hardware address */ + uint32_t ar_tip; /* target IP address */ +} SLIRP_PACKED_END; + +#define ARP_TABLE_SIZE 16 + +typedef struct ArpTable { + struct slirp_arphdr table[ARP_TABLE_SIZE]; + int next_victim; +} ArpTable; + +/* Add a new ARP entry for the given addresses */ +void arp_table_add(Slirp *slirp, uint32_t ip_addr, + const uint8_t ethaddr[ETH_ALEN]); + +/* Look for an ARP entry for the given IP address */ +bool arp_table_search(Slirp *slirp, uint32_t ip_addr, + uint8_t out_ethaddr[ETH_ALEN]); + +struct ndpentry { + uint8_t eth_addr[ETH_ALEN]; /* sender hardware address */ + struct in6_addr ip_addr; /* sender IP address */ +}; + +#define NDP_TABLE_SIZE 16 + +typedef struct NdpTable { + struct ndpentry table[NDP_TABLE_SIZE]; + /* + * The table is a cache with old entries overwritten when the table fills. + * Preserve the first entry: it is the guest, which is needed for lazy + * hostfwd guest address assignment. + */ + struct in6_addr guest_in6_addr; + int next_victim; +} NdpTable; + +/* Add a new NDP entry for the given addresses */ +void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, + uint8_t ethaddr[ETH_ALEN]); + +/* Look for an NDP entry for the given IPv6 address */ +bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, + uint8_t out_ethaddr[ETH_ALEN]); + +/* Slirp configuration, specified by the application */ +struct Slirp { + int cfg_version; + + unsigned time_fasttimo; + unsigned last_slowtimo; + bool do_slowtimo; + + bool in_enabled, in6_enabled; + + /* virtual network configuration */ + struct in_addr vnetwork_addr; + struct in_addr vnetwork_mask; + struct in_addr vhost_addr; + struct in6_addr vprefix_addr6; + uint8_t vprefix_len; + struct in6_addr vhost_addr6; + bool disable_dhcp; /* slirp will not reply to any DHCP requests */ + struct in_addr vdhcp_startaddr; + struct in_addr vnameserver_addr; + struct in6_addr vnameserver_addr6; + + struct in_addr client_ipaddr; + char client_hostname[33]; + + int restricted; + struct gfwd_list *guestfwd_list; + + int if_mtu; + int if_mru; + + bool disable_host_loopback; + + uint32_t mfr_id; + uint8_t oob_eth_addr[ETH_ALEN]; + + /* mbuf states */ + struct slirp_quehead m_freelist; + struct slirp_quehead m_usedlist; + int mbuf_alloced; + + /* if states */ + struct slirp_quehead if_fastq; /* fast queue (for interactive data) */ + struct slirp_quehead if_batchq; /* queue for non-interactive data */ + bool if_start_busy; /* avoid if_start recursion */ + + /* ip states */ + struct ipq ipq; /* ip reass. queue */ + uint16_t ip_id; /* ip packet ctr, for ids */ + + /* bootp/dhcp states */ + BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; + char *bootp_filename; + size_t vdnssearch_len; + uint8_t *vdnssearch; + char *vdomainname; + + /* tcp states */ + struct socket tcb; + struct socket *tcp_last_so; + tcp_seq tcp_iss; /* tcp initial send seq # */ + uint32_t tcp_now; /* for RFC 1323 timestamps */ + + /* udp states */ + struct socket udb; + struct socket *udp_last_so; + + /* icmp states */ + struct socket icmp; + struct socket *icmp_last_so; + + /* tftp states */ + char *tftp_prefix; + struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + char *tftp_server_name; + + ArpTable arp_table; + NdpTable ndp_table; + + GRand *grand; + void *ra_timer; + + bool enable_emu; + + const SlirpCb *cb; + void *opaque; + + struct sockaddr_in *outbound_addr; + struct sockaddr_in6 *outbound_addr6; + bool disable_dns; /* slirp will not redirect/serve any DNS packet */ +}; + +/* + * Send one packet from each session. + * If there are packets on the fastq, they are sent FIFO, before + * everything else. Then we choose the first packet from each + * batchq session (socket) and send it. + * For example, if there are 3 ftp sessions 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. + */ +void if_start(Slirp *); + +/* Get the address of the DNS server on the host side */ +int get_dns_addr(struct in_addr *pdns_addr); + +/* Get the IPv6 address of the DNS server on the host side */ +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id); + +/* ncsi.c */ + +/* Process NCSI packet coming from the guest */ +void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); + +#ifndef _WIN32 +#include +#endif + +/* Whether we should send TCP keepalive packets */ +extern bool slirp_do_keepalive; + +#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL) + +/* dnssearch.c */ +/* Translate from vdnssearch in configuration, into Slirp */ +int translate_dnssearch(Slirp *s, const char **names); + +/* cksum.c */ +/* Compute the checksum of the mbuf */ +uint16_t cksum(struct mbuf *m, size_t len); +/* Compute the checksum of the mbuf which contains an IPv6 packet */ +uint16_t ip6_cksum(struct mbuf *m); + +/* if.c */ +/* Called from slirp_new */ +void if_init(Slirp *); +/* Queue packet into an output queue (fast or batch), for sending to the guest */ +void if_output(struct socket *, struct mbuf *); + +/* ip_input.c */ +/* Called from slirp_new */ +void ip_init(Slirp *); +/* Called from slirp_cleanup */ +void ip_cleanup(Slirp *); +/* Process IPv4 packet coming from the guest */ +void ip_input(struct mbuf *); +/* + * IP timer processing; + * if a timer expires on a reassembly + * queue, discard it. + */ +void ip_slowtimo(Slirp *); +/* + * Strip out IP options, at higher + * level protocol in the kernel. + */ +void ip_stripoptions(register struct mbuf *); + +/* ip_output.c */ +/* Send IPv4 packet to the guest */ +int ip_output(struct socket *, struct mbuf *); + +/* ip6_input.c */ +/* Called from slirp_new, but after other initialization */ +void ip6_post_init(Slirp *); +/* Called from slirp_cleanup */ +void ip6_cleanup(Slirp *); +/* Process IPv6 packet coming from the guest */ +void ip6_input(struct mbuf *); + +/* ip6_output */ +/* Send IPv6 packet to the guest */ +int ip6_output(struct socket *, struct mbuf *, int fast); + +/* tcp_input.c */ +/* Process TCP datagram coming from the guest */ +void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af); +/* Determine a reasonable value for maxseg size */ +int tcp_mss(register struct tcpcb *, unsigned offer); + +/* tcp_output.c */ +/* Send TCP datagram to the guest */ +int tcp_output(register struct tcpcb *); +/* Start/restart persistence timer */ +void tcp_setpersist(register struct tcpcb *); + +/* tcp_subr.c */ +/* Called from slirp_new */ +void tcp_init(Slirp *); +/* Called from slirp_cleanup */ +void tcp_cleanup(Slirp *); +/* + * 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. + */ +void tcp_template(struct tcpcb *); +/* + * Send a single message to the TCP at address specified by + * the given TCP/IP header. + */ +void tcp_respond(struct tcpcb *, register struct tcpiphdr *, + register struct mbuf *, tcp_seq, tcp_seq, int, unsigned short); +/* + * Create a new TCP control block, making an + * empty reassembly queue and hooking it to the argument + * protocol control block. + */ +struct tcpcb *tcp_newtcpcb(struct socket *); +/* + * Close a TCP control block: + * discard all space held by the tcp + * discard internet protocol block + * wake up any sleepers + */ +struct tcpcb *tcp_close(register struct tcpcb *); +/* The Internet socket got closed, tell the guest */ +void tcp_sockclosed(struct tcpcb *); +/* + * Connect to a host on the Internet + * Called by tcp_input + */ +int tcp_fconnect(struct socket *, unsigned short af); +/* Accept the connection from the Internet, and connect to the guest */ +void tcp_connect(struct socket *); +/* Attach a TCPCB to a socket */ +void tcp_attach(struct socket *); +/* * Return TOS according to the ports */ +uint8_t tcp_tos(struct socket *); +/* + * We received a packet from the guest. + * + * 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 + */ +int tcp_emu(struct socket *, struct mbuf *); +/* Configure the socket, now that the guest completed accepting the connection */ +int tcp_ctl(struct socket *); +/* + * 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); + +/* Find the socket for the guest address and port */ +struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, + int guest_port); + +/* Send a frame to the virtual Ethernet board, i.e. call the application send_packet callback */ +void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len); + +/* Create a new timer, i.e. call the application timer_new callback */ +void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque); + +/* Call slirp->cb->register_poll_socket (or register_poll_fd for compatibility) */ +void slirp_register_poll_socket(struct socket *so); + +/* Call slirp->cb->unregister_poll_socket (or unregister_poll_fd for compatibility) */ +void slirp_unregister_poll_socket(struct socket *so); + +#if !defined(HAVE__LSEEK) +#define slirp_os_lseek lseek +#else +#define slirp_os_lseek _lseek +#endif + +#if !defined(HAVE__READ) +#define slirp_os_read read +#else +#define slirp_os_read _read +#endif + +#if !defined(HAVE__CLOSE) +#define slirp_os_close close +#else +#define slirp_os_close _close +#endif + +#if !defined(HAVE_SSCANF_S) +#define slirp_sscanf sscanf +#define sscanf_buf_arg(buf) buf +#else +#define slirp_sscanf sscanf_s +#define sscanf_buf_arg(buf) buf, (int) sizeof(buf) +#endif + +#endif diff --git a/libslirp/src/socket.c b/libslirp/src/socket.c new file mode 100644 index 000000000..278bf27c6 --- /dev/null +++ b/libslirp/src/socket.c @@ -0,0 +1,1249 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#include "slirp.h" +#include "ip_icmp.h" +#ifdef __sun__ +#include +#endif +#ifdef __linux__ +#include +#endif + +static void sofcantrcvmore(struct socket *so); +static void sofcantsendmore(struct socket *so); + +struct socket *solookup(struct socket **last, struct socket *head, + const struct sockaddr_storage *lhost, + const struct sockaddr_storage *fhost) +{ + struct socket *so = *last; + + /* Optimisation */ + if (so != head && sockaddr_equal(&(so->lhost.ss), lhost) && + (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + return so; + } + + for (so = head->so_next; so != head; so = so->so_next) { + if (sockaddr_equal(&(so->lhost.ss), lhost) && + (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { + *last = so; + return so; + } + } + + return (struct socket *)NULL; +} + +/* + * Create a new socket, initialise the fields + * It is the responsibility of the caller to + * slirp_insque() it into the correct linked-list + */ +struct socket *socreate(Slirp *slirp, int type) +{ + struct socket *so = g_new(struct socket, 1); + + memset(so, 0, sizeof(struct socket)); + so->so_type = type; + so->so_state = SS_NOFDREF; + so->s = SLIRP_INVALID_SOCKET; + so->s_aux = SLIRP_INVALID_SOCKET; + so->slirp = slirp; + so->pollfds_idx = -1; + + return so; +} + +/* + * Remove references to so from the given message queue. + */ +static void soqfree(const struct socket *so, struct slirp_quehead *qh) +{ + struct mbuf *ifq; + + for (ifq = (struct mbuf *)qh->qh_link; (struct slirp_quehead *)ifq != qh; + ifq = ifq->m_next) { + if (ifq->m_so == so) { + struct mbuf *ifm; + ifq->m_so = NULL; + for (ifm = ifq->m_nextpkt; ifm != ifq; ifm = ifm->m_nextpkt) { + ifm->m_so = NULL; + } + } + } +} + +/* + * slirp_remque and free a socket, clobber cache + */ +void sofree(struct socket *so) +{ + Slirp *slirp = so->slirp; + + if (have_valid_socket(so->s_aux)) { + closesocket(so->s_aux); + } + + soqfree(so, &slirp->if_fastq); + soqfree(so, &slirp->if_batchq); + + if (so == slirp->tcp_last_so) { + slirp->tcp_last_so = &slirp->tcb; + } else if (so == slirp->udp_last_so) { + slirp->udp_last_so = &slirp->udb; + } else if (so == slirp->icmp_last_so) { + slirp->icmp_last_so = &slirp->icmp; + } + m_free(so->so_m); + + if (so->so_next && so->so_prev) + slirp_remque(so); /* crashes if so is not in a queue */ + + if (so->so_tcpcb) { + g_free(so->so_tcpcb); + } + g_free(so); +} + +size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np) +{ + int n, lss, total; + struct sbuf *sb = &so->so_snd; + int len = sb->sb_datalen - sb->sb_cc; + int mss = so->so_tcpcb->t_maxseg; + + DEBUG_CALL("sopreprbuf"); + DEBUG_ARG("so = %p", so); + + if (len <= 0) + return 0; + + iov[0].iov_base = sb->sb_wptr; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; + 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; + } + } + if (np) + *np = n; + + return iov[0].iov_len + (n - 1) * iov[1].iov_len; +} + +/* + * 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(struct socket *so) +{ + int n, nn; + size_t buf_len; + struct sbuf *sb = &so->so_snd; + struct iovec iov[2]; + + DEBUG_CALL("soread"); + DEBUG_ARG("so = %p", so); + + /* + * No need to check if there's enough room to read. + * soread wouldn't have been called if there weren't + */ + buf_len = sopreprbuf(so, iov, &n); + assert(buf_len != 0); + + nn = recv(so->s, iov[0].iov_base, iov[0].iov_len, 0); + if (nn <= 0) { + if (nn < 0 && (errno == EINTR || errno == EAGAIN)) + return 0; + else { + int err; + socklen_t elen = sizeof err; + struct sockaddr_storage addr; + struct sockaddr *paddr = (struct sockaddr *)&addr; + socklen_t alen = sizeof addr; + + err = errno; + if (nn == 0) { + int shutdown_wr = so->so_state & SS_FCANTSENDMORE; + + if (!shutdown_wr && getpeername(so->s, paddr, &alen) < 0) { + err = errno; + } else { + getsockopt(so->s, SOL_SOCKET, SO_ERROR, &err, &elen); + } + } + + DEBUG_MISC(" --- soread() disconnected, nn = %d, errno = %d-%s", nn, + errno, g_strerror(errno)); + sofcantrcvmore(so); + + if (err == ECONNABORTED || err == ECONNRESET || err == ECONNREFUSED || + err == ENOTCONN || err == EPIPE) { + tcp_drop(sototcpcb(so), err); + } else { + tcp_sockclosed(sototcpcb(so)); + } + return -1; + } + } + + /* + * 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 won't (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(" ... read nn = %d bytes", nn); + + /* 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; +} + +int soreadbuf(struct socket *so, const char *buf, int size) +{ + int n, nn, copy = size; + struct sbuf *sb = &so->so_snd; + struct iovec iov[2]; + + DEBUG_CALL("soreadbuf"); + DEBUG_ARG("so = %p", so); + + /* + * No need to check if there's enough room to read. + * soread wouldn't have been called if there weren't + */ + assert(size > 0); + if (sopreprbuf(so, iov, &n) < size) + goto err; + + nn = MIN(iov[0].iov_len, copy); + memcpy(iov[0].iov_base, buf, nn); + + copy -= nn; + buf += nn; + + if (copy == 0) + goto done; + + memcpy(iov[1].iov_base, buf, copy); + +done: + /* Update fields */ + sb->sb_cc += size; + sb->sb_wptr += size; + if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) + sb->sb_wptr -= sb->sb_datalen; + return size; +err: + + sofcantrcvmore(so); + tcp_sockclosed(sototcpcb(so)); + g_critical("soreadbuf buffer too small"); + return -1; +} + +/* + * 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 + */ +int sorecvoob(struct socket *so) +{ + struct tcpcb *tp = sototcpcb(so); + int ret; + + DEBUG_CALL("sorecvoob"); + DEBUG_ARG("so = %p", 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. + */ + ret = soread(so); + if (ret > 0) { + tp->snd_up = tp->snd_una + so->so_snd.sb_cc; + tp->t_force = 1; + tcp_output(tp); + tp->t_force = 0; + } + + return ret; +} + +/* + * Send urgent data + * There's a lot duplicated code here, but... + */ +int sosendoob(struct socket *so) +{ + struct sbuf *sb = &so->so_rcv; + char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ + + int n; + + DEBUG_CALL("sosendoob"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); + + if (so->so_urgc > sizeof(buff)) + so->so_urgc = sizeof(buff); /* XXXX */ + + if (sb->sb_rptr < sb->sb_wptr) { + /* We can send it directly */ + n = slirp_send(so, sb->sb_rptr, so->so_urgc, + (MSG_OOB)); /* |MSG_DONTWAIT)); */ + } else { + /* + * Since there's no sendv or sendtov like writev, + * we must copy all data to a linear buffer then + * send it all + */ + uint32_t urgc = so->so_urgc; /* Amount of room left in buff */ + int len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + if (len > urgc) { + len = urgc; + } + memcpy(buff, sb->sb_rptr, len); + urgc -= len; + if (urgc) { + /* We still have some room for the rest */ + n = sb->sb_wptr - sb->sb_data; + if (n > urgc) { + n = urgc; + } + memcpy((buff + len), sb->sb_data, n); + len += n; + } + n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ +#ifdef SLIRP_DEBUG + if (n != len) { + DEBUG_ERROR("Didn't send all data urgently XXXXX"); + } +#endif + } + + if (n < 0) { + return n; + } + so->so_urgc -= n; + DEBUG_MISC(" ---2 sent %d bytes urgent data, %d urgent bytes left", 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(struct socket *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 = %p", so); + + if (so->so_urgc) { + uint32_t expected = so->so_urgc; + if (sosendoob(so) < expected) { + /* Treat a short write as a fatal error too, + * rather than continuing on and sending the urgent + * data as if it were non-urgent and leaving the + * so_urgc count wrong. + */ + goto err_disconnected; + } + if (sb->sb_cc == 0) + return 0; + } + + /* + * No need to check if there's something to write, + * sowrite wouldn't have been called otherwise + */ + + iov[0].iov_base = sb->sb_rptr; + iov[1].iov_base = NULL; + iov[1].iov_len = 0; + 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 */ + + nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len, 0); + /* This should never happen, but people tell me it does *shrug* */ + if (nn < 0 && (errno == EAGAIN || errno == EINTR)) + return 0; + + if (nn <= 0) { + goto err_disconnected; + } + + if (n == 2 && nn == iov[0].iov_len) { + int ret; + ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len, 0); + if (ret > 0) + nn += ret; + } + DEBUG_MISC(" ... wrote nn = %d bytes", nn); + + /* 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; + +err_disconnected: + DEBUG_MISC(" --- sowrite disconnected, so->so_state = %x, errno = %d", + so->so_state, errno); + sofcantsendmore(so); + tcp_sockclosed(sototcpcb(so)); + return -1; +} + +/* + * recvfrom() a UDP socket + */ +void sorecvfrom(struct socket *so) +{ + struct sockaddr_storage addr; + struct sockaddr_storage saddr, daddr; + socklen_t addrlen = sizeof(struct sockaddr_storage); + char buff[256]; + +#ifdef __linux__ + ssize_t size; + struct msghdr msg; + struct iovec iov; + char control[1024]; + + /* First look for errors */ + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &saddr; + msg.msg_namelen = sizeof(saddr); + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + iov.iov_base = buff; + iov.iov_len = sizeof(buff); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + size = recvmsg(so->s, &msg, MSG_ERRQUEUE); + if (size >= 0) { + struct cmsghdr *cmsg; + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level == IPPROTO_IP && + cmsg->cmsg_type == IP_RECVERR) { + struct sock_extended_err *ee = + (struct sock_extended_err *) CMSG_DATA(cmsg); + + if (ee->ee_origin == SO_EE_ORIGIN_ICMP) { + /* Got an ICMP error, forward it */ + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *) SO_EE_OFFENDER(ee); + icmp_forward_error(so->so_m, ee->ee_type, ee->ee_code, + 0, NULL, &sin->sin_addr); + } + } + else if (cmsg->cmsg_level == IPPROTO_IPV6 && + cmsg->cmsg_type == IPV6_RECVERR) { + struct sock_extended_err *ee = + (struct sock_extended_err *) CMSG_DATA(cmsg); + + if (ee->ee_origin == SO_EE_ORIGIN_ICMP6) { + /* Got an ICMPv6 error, forward it */ + struct sockaddr_in6 *sin6; + + sin6 = (struct sockaddr_in6 *) SO_EE_OFFENDER(ee); + icmp6_forward_error(so->so_m, ee->ee_type, ee->ee_code, + &sin6->sin6_addr); + } + } + } + return; + } +#endif + + DEBUG_CALL("sorecvfrom"); + DEBUG_ARG("so = %p", so); + + if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ + int len; + + len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); + /* XXX Check if reply is "correct"? */ + + if (len == -1 || len == 0) { + uint8_t code = ICMP_UNREACH_PORT; + + if (errno == EHOSTUNREACH) + code = ICMP_UNREACH_HOST; + else if (errno == ENETUNREACH) + code = ICMP_UNREACH_NET; + + DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, g_strerror(errno)); + icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, g_strerror(errno)); + } else { + icmp_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + /* No need for this socket anymore, udp_detach it */ + udp_detach(so); + } else if (so->so_type == IPPROTO_ICMPV6) { /* This is a "ping" reply */ + int len; + + len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); + /* XXX Check if reply is "correct"? */ + + if (len == -1 || len == 0) { + uint8_t code = ICMP6_UNREACH_PORT; + + if (errno == EHOSTUNREACH) + code = ICMP6_UNREACH_ADDRESS; + else if (errno == ENETUNREACH) + code = ICMP6_UNREACH_NO_ROUTE; + + DEBUG_MISC(" udp icmp6 rx errno = %d-%s", errno, g_strerror(errno)); + icmp6_send_error(so->so_m, ICMP_UNREACH, code); + } else { + icmp6_reflect(so->so_m); + so->so_m = NULL; /* Don't m_free() it again! */ + } + /* No need for this socket anymore, udp_detach it */ + udp_detach(so); + } else { /* A "normal" UDP packet */ + struct mbuf *m; + int len; +#ifdef _WIN32 + unsigned long n; +#else + int n; +#endif + + if (ioctlsocket(so->s, FIONREAD, &n) != 0) { + DEBUG_MISC(" ioctlsocket errno = %d-%s\n", errno, g_strerror(errno)); + return; + } + + m = m_get(so->slirp); + if (!m) { + return; + } + switch (so->so_ffamily) { + case AF_INET: + m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr); + break; + case AF_INET6: + m->m_data += + IF_MAXLINKHDR + sizeof(struct ip6) + sizeof(struct udphdr); + break; + default: + g_assert_not_reached(); + } + + /* + * 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)) { */ + + 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(" did recvfrom %d, errno = %d-%s", m->m_len, errno, + g_strerror(errno)); + if (m->m_len < 0) { + if (errno == ENOTCONN) { + /* + * UDP socket got burnt, e.g. by suspend on iOS. Tear it down + * and let it get re-created if the guest still needs it + */ + udp_detach(so); + } else { + /* Report error as ICMP */ + switch (so->so_lfamily) { + uint8_t code; + case AF_INET: + code = ICMP_UNREACH_PORT; + + if (errno == EHOSTUNREACH) { + code = ICMP_UNREACH_HOST; + } else if (errno == ENETUNREACH) { + code = ICMP_UNREACH_NET; + } + + DEBUG_MISC(" rx error, tx icmp ICMP_UNREACH:%i", code); + icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, + g_strerror(errno)); + break; + case AF_INET6: + code = ICMP6_UNREACH_PORT; + + if (errno == EHOSTUNREACH) { + code = ICMP6_UNREACH_ADDRESS; + } else if (errno == ENETUNREACH) { + code = ICMP6_UNREACH_NO_ROUTE; + } + + DEBUG_MISC(" rx error, tx icmp6 ICMP_UNREACH:%i", code); + icmp6_send_error(so->so_m, ICMP6_UNREACH, code); + break; + default: + g_assert_not_reached(); + } + 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 this packet was destined for CTL_ADDR, + * make it look like that's where it came from + */ + saddr = addr; + sotranslate_in(so, &saddr); + + /* Perform lazy guest IP address resolution if needed. */ + if (so->so_state & SS_HOSTFWD) { + if (soassign_guest_addr_if_needed(so) < 0) { + DEBUG_MISC(" guest address not available yet"); + switch (so->so_lfamily) { + case AF_INET: + icmp_send_error(so->so_m, ICMP_UNREACH, + ICMP_UNREACH_HOST, 0, + "guest address not available yet"); + break; + case AF_INET6: + icmp6_send_error(so->so_m, ICMP6_UNREACH, + ICMP6_UNREACH_ADDRESS); + break; + default: + g_assert_not_reached(); + } + m_free(m); + return; + } + } + daddr = so->lhost.ss; + + switch (so->so_ffamily) { + case AF_INET: + udp_output(so, m, (struct sockaddr_in *)&saddr, + (struct sockaddr_in *)&daddr, so->so_iptos); + break; + case AF_INET6: + udp6_output(so, m, (struct sockaddr_in6 *)&saddr, + (struct sockaddr_in6 *)&daddr); + break; + default: + g_assert_not_reached(); + } + } /* rx error */ + } /* if ping packet */ +} + +/* + * sendto() a socket + */ +int sosendto(struct socket *so, struct mbuf *m) +{ + int ret; + struct sockaddr_storage addr; + + DEBUG_CALL("sosendto"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); + + addr = so->fhost.ss; + DEBUG_CALL(" sendto()ing)"); + if (sotranslate_out(so, &addr) < 0) { + return -1; + } + + /* Don't care what port we get */ + ret = sendto(so->s, m->m_data, m->m_len, 0, (struct sockaddr *)&addr, + sockaddr_size(&addr)); + 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_PERSISTENT_MASK; + so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */ + return 0; +} + +struct socket *tcpx_listen(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *laddr, socklen_t laddrlen, + int flags) +{ + struct socket *so; + slirp_os_socket s; + int opt = 1; + socklen_t addrlen; + + DEBUG_CALL("tcpx_listen"); + /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */ + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + int ret; + switch (haddr->sa_family) { + case AF_INET: + case AF_INET6: + ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_ARG("hfamily = INET"); + DEBUG_ARG("haddr = %s", addrstr); + DEBUG_ARG("hport = %s", portstr); + break; +#ifndef _WIN32 + case AF_UNIX: + DEBUG_ARG("hfamily = UNIX"); + DEBUG_ARG("hpath = %s", ((struct sockaddr_un *) haddr)->sun_path); + break; +#endif + default: + g_assert_not_reached(); + } + switch (laddr->sa_family) { + case AF_INET: + case AF_INET6: + ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_ARG("laddr = %s", addrstr); + DEBUG_ARG("lport = %s", portstr); + break; + default: + g_assert_not_reached(); + } + DEBUG_ARG("flags = %x", flags); + + /* + * SS_HOSTFWD sockets can be accepted multiple times, so they can't be + * SS_FACCEPTONCE. Also, SS_HOSTFWD connections can be accepted and + * immediately closed if the guest address isn't available yet, which is + * incompatible with the "accept once" concept. Correct code will never + * request both, so disallow their combination by assertion. + */ + g_assert(!((flags & SS_HOSTFWD) && (flags & SS_FACCEPTONCE))); + + so = socreate(slirp, IPPROTO_TCP); + + /* Don't tcp_attach... we don't need so_snd nor so_rcv */ + so->so_tcpcb = tcp_newtcpcb(so); + slirp_insque(so, &slirp->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_PERSISTENT_MASK; + so->so_state |= (SS_FACCEPTCONN | flags); + + sockaddr_copy(&so->lhost.sa, sizeof(so->lhost), laddr, laddrlen); + + s = slirp_socket(haddr->sa_family, SOCK_STREAM, 0); + if ((not_valid_socket(s)) || + (haddr->sa_family == AF_INET6 && slirp_socket_set_v6only(s, (flags & SS_HOSTFWD_V6ONLY) != 0) < 0) || + (slirp_socket_set_fast_reuse(s) < 0) || + (bind(s, haddr, haddrlen) < 0) || + (listen(s, 1) < 0)) { + int tmperrno = errno; /* Don't clobber the real reason we failed */ + if (have_valid_socket(s)) { + closesocket(s); + } + sofree(so); + /* Restore the real errno */ + errno = tmperrno; + + return NULL; + } + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + slirp_socket_set_nodelay(s); + + addrlen = sizeof(so->fhost); + getsockname(s, &so->fhost.sa, &addrlen); + sotranslate_accept(so); + + so->s = s; + slirp_register_poll_socket(so); + + return so; +} + +struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, + uint32_t laddr, unsigned lport, int flags) +{ + struct sockaddr_in hsa, lsa; + + memset(&hsa, 0, sizeof(hsa)); + hsa.sin_family = AF_INET; + hsa.sin_addr.s_addr = haddr; + hsa.sin_port = hport; + + memset(&lsa, 0, sizeof(lsa)); + lsa.sin_family = AF_INET; + lsa.sin_addr.s_addr = laddr; + lsa.sin_port = lport; + + return tcpx_listen(slirp, (const struct sockaddr *) &hsa, sizeof(hsa), (struct sockaddr *) &lsa, sizeof(lsa), flags); +} + +/* + * 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(struct socket *so) +{ + so->so_state &= ~(SS_NOFDREF | SS_ISFCONNECTED | SS_FCANTRCVMORE | + SS_FCANTSENDMORE | SS_FWDRAIN); + so->so_state |= SS_ISFCONNECTING; /* Clobber other states */ +} + +void soisfconnected(struct socket *so) +{ + so->so_state &= ~(SS_ISFCONNECTING | SS_FWDRAIN | SS_NOFDREF); + so->so_state |= SS_ISFCONNECTED; /* Clobber other states */ +} + +static void sofcantrcvmore(struct socket *so) +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s, 0); + } + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTSENDMORE) { + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; /* Don't select it */ + } else { + so->so_state |= SS_FCANTRCVMORE; + } +} + +static void sofcantsendmore(struct socket *so) +{ + if ((so->so_state & SS_NOFDREF) == 0) { + shutdown(so->s, 1); /* send FIN to fhost */ + } + so->so_state &= ~(SS_ISFCONNECTING); + if (so->so_state & SS_FCANTRCVMORE) { + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; /* as above */ + } else { + so->so_state |= SS_FCANTSENDMORE; + } +} + +void sofwdrain(struct socket *so) +{ + if (so->so_rcv.sb_cc) + so->so_state |= SS_FWDRAIN; + else + sofcantsendmore(so); +} + +static bool sotranslate_out4(Slirp *s, struct socket *so, struct sockaddr_in *sin) +{ + if (!s->disable_dns && so->so_faddr.s_addr == s->vnameserver_addr.s_addr) { + return so->so_fport == htons(53) && get_dns_addr(&sin->sin_addr) >= 0; + } + + if (so->so_faddr.s_addr == s->vhost_addr.s_addr || + so->so_faddr.s_addr == 0xffffffff) { + if (s->disable_host_loopback) { + return false; + } + + sin->sin_addr = loopback_addr; + } + + return true; +} + +static bool sotranslate_out6(Slirp *s, struct socket *so, struct sockaddr_in6 *sin) +{ + if (!s->disable_dns && in6_equal(&so->so_faddr6, &s->vnameserver_addr6)) { + uint32_t scope_id; + if (so->so_fport == htons(53) && get_dns6_addr(&sin->sin6_addr, &scope_id) >= 0) { + sin->sin6_scope_id = scope_id; + return true; + } + return false; + } + + if (in6_equal_net(&so->so_faddr6, &s->vprefix_addr6, s->vprefix_len) || + in6_equal(&so->so_faddr6, &(struct in6_addr)ALLNODES_MULTICAST)) { + if (s->disable_host_loopback) { + return false; + } + + sin->sin6_addr = in6addr_loopback; + } + + return true; +} + + +int sotranslate_out(struct socket *so, struct sockaddr_storage *addr) +{ + bool ok = true; + + switch (addr->ss_family) { + case AF_INET: + ok = sotranslate_out4(so->slirp, so, (struct sockaddr_in *)addr); + break; + case AF_INET6: + ok = sotranslate_out6(so->slirp, so, (struct sockaddr_in6 *)addr); + break; + } + + if (!ok) { + errno = EPERM; + return -1; + } + + return 0; +} + +void sotranslate_in(struct socket *so, struct sockaddr_storage *addr) +{ + Slirp *slirp = so->slirp; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + + switch (addr->ss_family) { + case AF_INET: + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; + + if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { + sin->sin_addr = slirp->vhost_addr; + } else if (sin->sin_addr.s_addr == loopback_addr.s_addr || + so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + sin->sin_addr = so->so_faddr; + } + } + break; + + case AF_INET6: + if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6, + slirp->vprefix_len)) { + if (in6_equal(&sin6->sin6_addr, &in6addr_loopback) || + !in6_equal(&so->so_faddr6, &slirp->vhost_addr6)) { + sin6->sin6_addr = so->so_faddr6; + } + } + break; + + default: + break; + } +} + +void sotranslate_accept(struct socket *so) +{ + Slirp *slirp = so->slirp; + + switch (so->so_ffamily) { + case AF_INET: + if (so->so_faddr.s_addr == INADDR_ANY || + (so->so_faddr.s_addr & loopback_mask) == + (loopback_addr.s_addr & loopback_mask)) { + so->so_faddr = slirp->vhost_addr; + } + break; + + case AF_INET6: + if (in6_equal(&so->so_faddr6, &in6addr_any) || + in6_equal(&so->so_faddr6, &in6addr_loopback)) { + so->so_faddr6 = slirp->vhost_addr6; + } + break; + + case AF_UNIX: { + /* Translate Unix socket to random ephemeral source port. We obtain + * this source port by binding to port 0 so that the OS allocates a + * port for us. If this fails, we fall back to choosing a random port + * with a random number generator. */ + slirp_os_socket s; + struct sockaddr_in in_addr; + struct sockaddr_in6 in6_addr; + socklen_t in_addr_len; + + if (so->slirp->in_enabled) { + so->so_ffamily = AF_INET; + so->so_faddr = slirp->vhost_addr; + so->so_fport = 0; + + switch (so->so_type) { + case IPPROTO_TCP: + s = slirp_socket(PF_INET, SOCK_STREAM, 0); + break; + case IPPROTO_UDP: + s = slirp_socket(PF_INET, SOCK_DGRAM, 0); + break; + default: + g_assert_not_reached(); + break; + } + if (not_valid_socket(s)) { + g_error("Ephemeral slirp_socket() allocation failed"); + goto unix2inet_cont; + } + memset(&in_addr, 0, sizeof(in_addr)); + in_addr.sin_family = AF_INET; + in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + in_addr.sin_port = htons(0); + if (bind(s, (struct sockaddr *) &in_addr, sizeof(in_addr))) { + g_error("Ephemeral bind() failed"); + closesocket(s); + goto unix2inet_cont; + } + in_addr_len = sizeof(in_addr); + if (getsockname(s, (struct sockaddr *) &in_addr, &in_addr_len)) { + g_error("Ephemeral getsockname() failed"); + closesocket(s); + goto unix2inet_cont; + } + so->s_aux = s; + so->so_fport = in_addr.sin_port; + +unix2inet_cont: + if (!so->so_fport) { + g_warning("Falling back to random port allocation"); + so->so_fport = htons(g_rand_int_range(slirp->grand, 49152, 65536)); + } + } else if (so->slirp->in6_enabled) { + so->so_ffamily = AF_INET6; + so->so_faddr6 = slirp->vhost_addr6; + so->so_fport6 = 0; + + switch (so->so_type) { + case IPPROTO_TCP: + s = slirp_socket(PF_INET6, SOCK_STREAM, 0); + break; + case IPPROTO_UDP: + s = slirp_socket(PF_INET6, SOCK_DGRAM, 0); + break; + default: + g_assert_not_reached(); + break; + } + if (not_valid_socket(s)) { + g_error("Ephemeral slirp_socket() allocation failed"); + goto unix2inet6_cont; + } + memset(&in6_addr, 0, sizeof(in6_addr)); + in6_addr.sin6_family = AF_INET6; + in6_addr.sin6_addr = in6addr_loopback; + in6_addr.sin6_port = htons(0); + if (bind(s, (struct sockaddr *) &in6_addr, sizeof(in6_addr))) { + g_error("Ephemeral bind() failed"); + closesocket(s); + goto unix2inet6_cont; + } + in_addr_len = sizeof(in6_addr); + if (getsockname(s, (struct sockaddr *) &in6_addr, &in_addr_len)) { + g_error("Ephemeral getsockname() failed"); + closesocket(s); + goto unix2inet6_cont; + } + so->s_aux = s; + so->so_fport6 = in6_addr.sin6_port; + +unix2inet6_cont: + if (!so->so_fport6) { + g_warning("Falling back to random port allocation"); + so->so_fport6 = htons(g_rand_int_range(slirp->grand, 49152, 65536)); + } + } else { + g_assert_not_reached(); + } + break; + } /* case AF_UNIX */ + + default: + break; + } +} + +void sodrop(struct socket *s, int num) +{ + if (sbdrop(&s->so_snd, num)) { + s->slirp->cb->notify(s->slirp->opaque); + } +} + +/* + * Translate "addr-any" in so->lhost to the guest's actual address. + * Returns 0 for success, or -1 if the guest doesn't have an address yet + * with errno set to EHOSTUNREACH. + * + * The guest address is taken from the first entry in the ARP table for IPv4 + * and the first entry in the NDP table for IPv6. + * Note: The IPv4 path isn't exercised yet as all hostfwd "" guest translations + * are handled immediately by using slirp->vdhcp_startaddr. + */ +int soassign_guest_addr_if_needed(struct socket *so) +{ + Slirp *slirp = so->slirp; + /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */ + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + + g_assert(so->so_state & SS_HOSTFWD); + + switch (so->so_ffamily) { + case AF_INET: + if (so->so_laddr.s_addr == INADDR_ANY) { + g_assert_not_reached(); + } + break; + + case AF_INET6: + if (in6_zero(&so->so_laddr6)) { + int ret; + if (in6_zero(&slirp->ndp_table.guest_in6_addr)) { + errno = EHOSTUNREACH; + return -1; + } + so->so_laddr6 = slirp->ndp_table.guest_in6_addr; + ret = getnameinfo((const struct sockaddr *) &so->lhost.ss, + sizeof(so->lhost.ss), addrstr, sizeof(addrstr), + portstr, sizeof(portstr), + NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_MISC("%s: new ip = [%s]:%s", __func__, addrstr, portstr); + } + break; + + default: + break; + } + + return 0; +} diff --git a/libslirp/src/socket.h b/libslirp/src/socket.h new file mode 100644 index 000000000..c7c683401 --- /dev/null +++ b/libslirp/src/socket.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef SLIRP_SOCKET_H +#define SLIRP_SOCKET_H + +#include + +#ifndef _WIN32 +#include +#endif + +#include "misc.h" +#include "sbuf.h" + +#define SO_EXPIRE 240000 +#define SO_EXPIREFAST 10000 + +/* Helps unify some in/in6 routines. */ +union in4or6_addr { + struct in_addr addr4; + struct in6_addr addr6; +}; +typedef union in4or6_addr in4or6_addr; + +/* + * Our socket structure + */ + +union slirp_sockaddr { + struct sockaddr sa; + struct sockaddr_storage ss; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +struct socket { + struct socket *so_next, *so_prev; /* For a linked list of sockets */ + + slirp_os_socket s; /* The actual socket */ + slirp_os_socket s_aux; /* An auxiliary socket for miscellaneous use. Currently used to + * reserve OS ports in UNIX-to-inet translation. */ + struct gfwd_list *guestfwd; + + int pollfds_idx; /* GPollFD GArray index */ + + Slirp *slirp; /* managing slirp instance */ + + /* XXX union these with not-yet-used sbuf params */ + struct mbuf *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 */ + uint32_t so_urgc; + union slirp_sockaddr fhost; /* Foreign host */ +#define so_faddr fhost.sin.sin_addr +#define so_fport fhost.sin.sin_port +#define so_faddr6 fhost.sin6.sin6_addr +#define so_fport6 fhost.sin6.sin6_port +#define so_ffamily fhost.ss.ss_family + + union slirp_sockaddr lhost; /* Local host */ +#define so_laddr lhost.sin.sin_addr +#define so_lport lhost.sin.sin_port +#define so_laddr6 lhost.sin6.sin6_addr +#define so_lport6 lhost.sin6.sin6_port +#define so_lfamily lhost.ss.ss_family + + uint8_t so_iptos; /* Type of service */ + uint8_t so_emu; /* Is the socket emulated? */ + + uint8_t so_type; /* Protocol of the socket. May be 0 if loading old + * states. */ + int32_t so_state; /* internal state flags SS_*, below */ + + struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ + unsigned 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 */ +}; + + +/* + * 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_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 */ + +#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */ +#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ +#define SS_INCOMING \ + 0x2000 /* Connection was initiated by a host on the internet */ +#define SS_HOSTFWD_V6ONLY 0x4000 /* Only bind on v6 addresses */ + +/* Check that two addresses are equal */ +static inline int sockaddr_equal(const struct sockaddr_storage *a, + const struct sockaddr_storage *b) +{ + if (a->ss_family != b->ss_family) { + return 0; + } + + switch (a->ss_family) { + case AF_INET: { + const struct sockaddr_in *a4 = (const struct sockaddr_in *)a; + const struct sockaddr_in *b4 = (const struct sockaddr_in *)b; + return a4->sin_addr.s_addr == b4->sin_addr.s_addr && + a4->sin_port == b4->sin_port; + } + case AF_INET6: { + const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)a; + const struct sockaddr_in6 *b6 = (const struct sockaddr_in6 *)b; + return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) && + a6->sin6_port == b6->sin6_port); + } +#ifndef _WIN32 + case AF_UNIX: { + const struct sockaddr_un *aun = (const struct sockaddr_un *)a; + const struct sockaddr_un *bun = (const struct sockaddr_un *)b; + return strncmp(aun->sun_path, bun->sun_path, sizeof(aun->sun_path)) == 0; + } +#endif + default: + g_assert_not_reached(); + } + + return 0; +} + +/* Get the size of an address */ +static inline socklen_t sockaddr_size(const struct sockaddr_storage *a) +{ + switch (a->ss_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); +#ifndef _WIN32 + case AF_UNIX: + return sizeof(struct sockaddr_un); +#endif + default: + g_assert_not_reached(); + } +} + +/* Copy an address */ +static inline void sockaddr_copy(struct sockaddr *dst, socklen_t dstlen, const struct sockaddr *src, socklen_t srclen) +{ + socklen_t len = sockaddr_size((const struct sockaddr_storage *) src); + g_assert(len <= srclen); + g_assert(len <= dstlen); + memcpy(dst, src, len); +} + +/* Find the socket corresponding to lhost & fhost, trying last as a guess */ +struct socket *solookup(struct socket **last, struct socket *head, + const struct sockaddr_storage *lhost, + const struct sockaddr_storage *fhost); +/* Create a new socket */ +struct socket *socreate(Slirp *, int); +/* Release a socket */ +void sofree(struct socket *); +/* Receive the available data from the Internet socket and queue it on the sb */ +int soread(struct socket *); +/* Receive the available OOB data from the Internet socket and try to send it immediately */ +int sorecvoob(struct socket *); +/* Send OOB data to the Internet socket */ +int sosendoob(struct socket *); +/* Send data to the Internet socket */ +int sowrite(struct socket *); +/* Receive the available data from the Internet UDP socket, and send it to the guest */ +void sorecvfrom(struct socket *); +/* Send data to the Internet UDP socket */ +int sosendto(struct socket *, struct mbuf *); +/* Listen for incoming TCPv4 connections on this haddr+hport */ +struct socket *tcp_listen(Slirp *, uint32_t haddr, unsigned hport, uint32_t laddr, unsigned lport, int flags); +/* + * Listen for incoming TCP connections on this haddr + * On failure errno contains the reason. + */ +struct socket *tcpx_listen(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *laddr, socklen_t laddrlen, + int flags); +/* Note that the socket is connecting */ +void soisfconnecting(register struct socket *); +/* Note that the socket is connected */ +void soisfconnected(register struct socket *); +/* + * Set write drain mode + * Set CANTSENDMORE once all data has been write()n + */ +void sofwdrain(struct socket *); +struct iovec; /* For win32 */ +/* Prepare iov for storing into the sb */ +size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); +/* Get data from the buffer and queue it on the sb */ +int soreadbuf(struct socket *so, const char *buf, int size); + +/* Translate addr into host addr when it is a virtual address, before sending to the Internet */ +int sotranslate_out(struct socket *, struct sockaddr_storage *); +/* Translate addr into virtual address when it is host, before sending to the guest */ +void sotranslate_in(struct socket *, struct sockaddr_storage *); +/* Translate connections from localhost to the real hostname */ +void sotranslate_accept(struct socket *); +/* Drop num bytes from the reading end of the socket */ +void sodrop(struct socket *, int num); +/* Forwarding a connection to the guest, try to find the guest address to use, fill lhost with it */ +int soassign_guest_addr_if_needed(struct socket *so); + +#endif /* SLIRP_SOCKET_H */ diff --git a/libslirp/src/state.c b/libslirp/src/state.c new file mode 100644 index 000000000..f10edf071 --- /dev/null +++ b/libslirp/src/state.c @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp + * + * Copyright (c) 2004-2008 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" +#include "vmstate.h" +#include "stream.h" + +static int slirp_tcp_post_load(void *opaque, int version) +{ + tcp_template((struct tcpcb *)opaque); + + return 0; +} + +static const VMStateDescription vmstate_slirp_tcp = { + .name = "slirp-tcp", + .version_id = 0, + .post_load = slirp_tcp_post_load, + .fields = (VMStateField[]){ VMSTATE_INT16(t_state, struct tcpcb), + VMSTATE_INT16_ARRAY(t_timer, struct tcpcb, + TCPT_NTIMERS), + VMSTATE_INT16(t_rxtshift, struct tcpcb), + VMSTATE_INT16(t_rxtcur, struct tcpcb), + VMSTATE_INT16(t_dupacks, struct tcpcb), + VMSTATE_UINT16(t_maxseg, struct tcpcb), + VMSTATE_UINT8(t_force, struct tcpcb), + VMSTATE_UINT16(t_flags, struct tcpcb), + VMSTATE_UINT32(snd_una, struct tcpcb), + VMSTATE_UINT32(snd_nxt, struct tcpcb), + VMSTATE_UINT32(snd_up, struct tcpcb), + VMSTATE_UINT32(snd_wl1, struct tcpcb), + VMSTATE_UINT32(snd_wl2, struct tcpcb), + VMSTATE_UINT32(iss, struct tcpcb), + VMSTATE_UINT32(snd_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_nxt, struct tcpcb), + VMSTATE_UINT32(rcv_up, struct tcpcb), + VMSTATE_UINT32(irs, struct tcpcb), + VMSTATE_UINT32(rcv_adv, struct tcpcb), + VMSTATE_UINT32(snd_max, struct tcpcb), + VMSTATE_UINT32(snd_cwnd, struct tcpcb), + VMSTATE_UINT32(snd_ssthresh, struct tcpcb), + VMSTATE_INT16(t_idle, struct tcpcb), + VMSTATE_INT16(t_rtt, struct tcpcb), + VMSTATE_UINT32(t_rtseq, struct tcpcb), + VMSTATE_INT16(t_srtt, struct tcpcb), + VMSTATE_INT16(t_rttvar, struct tcpcb), + VMSTATE_UINT16(t_rttmin, struct tcpcb), + VMSTATE_UINT32(max_sndwnd, struct tcpcb), + VMSTATE_UINT8(t_oobflags, struct tcpcb), + VMSTATE_UINT8(t_iobc, struct tcpcb), + VMSTATE_INT16(t_softerror, struct tcpcb), + VMSTATE_UINT8(snd_scale, struct tcpcb), + VMSTATE_UINT8(rcv_scale, struct tcpcb), + VMSTATE_UINT8(request_r_scale, struct tcpcb), + VMSTATE_UINT8(requested_s_scale, struct tcpcb), + VMSTATE_UINT32(ts_recent, struct tcpcb), + VMSTATE_UINT32(ts_recent_age, struct tcpcb), + VMSTATE_UINT32(last_ack_sent, struct tcpcb), + VMSTATE_END_OF_LIST() } +}; + +/* The sbuf has a pair of pointers that are migrated as offsets; + * we calculate the offsets and restore the pointers using + * pre_save/post_load on a tmp structure. + */ +struct sbuf_tmp { + struct sbuf *parent; + uint32_t roff, woff; +}; + +static int sbuf_tmp_pre_save(void *opaque) +{ + struct sbuf_tmp *tmp = opaque; + tmp->woff = tmp->parent->sb_wptr - tmp->parent->sb_data; + tmp->roff = tmp->parent->sb_rptr - tmp->parent->sb_data; + + return 0; +} + +static int sbuf_tmp_post_load(void *opaque, int version) +{ + struct sbuf_tmp *tmp = opaque; + uint32_t requested_len = tmp->parent->sb_datalen; + + /* Allocate the buffer space used by the field after the tmp */ + sbreserve(tmp->parent, tmp->parent->sb_datalen); + + if (tmp->woff >= requested_len || tmp->roff >= requested_len) { + g_critical("invalid sbuf offsets r/w=%u/%u len=%u", tmp->roff, + tmp->woff, requested_len); + return -EINVAL; + } + + tmp->parent->sb_wptr = tmp->parent->sb_data + tmp->woff; + tmp->parent->sb_rptr = tmp->parent->sb_data + tmp->roff; + + return 0; +} + + +static const VMStateDescription vmstate_slirp_sbuf_tmp = { + .name = "slirp-sbuf-tmp", + .post_load = sbuf_tmp_post_load, + .pre_save = sbuf_tmp_pre_save, + .version_id = 0, + .fields = (VMStateField[]){ VMSTATE_UINT32(woff, struct sbuf_tmp), + VMSTATE_UINT32(roff, struct sbuf_tmp), + VMSTATE_END_OF_LIST() } +}; + +static const VMStateDescription vmstate_slirp_sbuf = { + .name = "slirp-sbuf", + .version_id = 0, + .fields = (VMStateField[]){ VMSTATE_UINT32(sb_cc, struct sbuf), + VMSTATE_UINT32(sb_datalen, struct sbuf), + VMSTATE_WITH_TMP(struct sbuf, struct sbuf_tmp, + vmstate_slirp_sbuf_tmp), + VMSTATE_VBUFFER_UINT32(sb_data, struct sbuf, 0, + NULL, sb_datalen), + VMSTATE_END_OF_LIST() } +}; + +static bool slirp_older_than_v4(void *opaque, int version_id) +{ + return version_id < 4; +} + +static bool slirp_family_inet(void *opaque, int version_id) +{ + union slirp_sockaddr *ssa = (union slirp_sockaddr *)opaque; + return ssa->ss.ss_family == AF_INET; +} + +static int slirp_socket_pre_load(void *opaque) +{ + struct socket *so = opaque; + + tcp_attach(so); + /* Older versions don't load these fields */ + so->so_ffamily = AF_INET; + so->so_lfamily = AF_INET; + return 0; +} + +#ifndef _WIN32 +#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_UINT32_TEST(f, s, t) +#else +/* Win uses u_long rather than uint32_t - but it's still 32bits long */ +#define VMSTATE_SIN4_ADDR(f, s, t) \ + VMSTATE_SINGLE_TEST(f, s, t, 0, slirp_vmstate_info_uint32, u_long) +#endif + +/* The OS provided ss_family field isn't that portable; it's size + * and type varies (16/8 bit, signed, unsigned) + * and the values it contains aren't fully portable. + */ +typedef struct SS_FamilyTmpStruct { + union slirp_sockaddr *parent; + uint16_t portable_family; +} SS_FamilyTmpStruct; + +#define SS_FAMILY_MIG_IPV4 2 /* Linux, BSD, Win... */ +#define SS_FAMILY_MIG_IPV6 10 /* Linux */ +#define SS_FAMILY_MIG_OTHER 0xffff + +static int ss_family_pre_save(void *opaque) +{ + SS_FamilyTmpStruct *tss = opaque; + + tss->portable_family = SS_FAMILY_MIG_OTHER; + + if (tss->parent->ss.ss_family == AF_INET) { + tss->portable_family = SS_FAMILY_MIG_IPV4; + } else if (tss->parent->ss.ss_family == AF_INET6) { + tss->portable_family = SS_FAMILY_MIG_IPV6; + } + + return 0; +} + +static int ss_family_post_load(void *opaque, int version_id) +{ + SS_FamilyTmpStruct *tss = opaque; + + switch (tss->portable_family) { + case SS_FAMILY_MIG_IPV4: + tss->parent->ss.ss_family = AF_INET; + break; + case SS_FAMILY_MIG_IPV6: + case 23: /* compatibility: AF_INET6 from mingw */ + case 28: /* compatibility: AF_INET6 from FreeBSD sys/socket.h */ + tss->parent->ss.ss_family = AF_INET6; + break; + default: + g_critical("invalid ss_family type %x", tss->portable_family); + return -EINVAL; + } + + return 0; +} + +static const VMStateDescription vmstate_slirp_ss_family = { + .name = "slirp-socket-addr/ss_family", + .pre_save = ss_family_pre_save, + .post_load = ss_family_post_load, + .fields = + (VMStateField[]){ VMSTATE_UINT16(portable_family, SS_FamilyTmpStruct), + VMSTATE_END_OF_LIST() } +}; + +static const VMStateDescription vmstate_slirp_socket_addr = { + .name = "slirp-socket-addr", + .version_id = 4, + .fields = + (VMStateField[]){ + VMSTATE_WITH_TMP(union slirp_sockaddr, SS_FamilyTmpStruct, + vmstate_slirp_ss_family), + VMSTATE_SIN4_ADDR(sin.sin_addr.s_addr, union slirp_sockaddr, + slirp_family_inet), + VMSTATE_UINT16_TEST(sin.sin_port, union slirp_sockaddr, + slirp_family_inet), + +#if 0 + /* Untested: Needs checking by someone with IPv6 test */ + VMSTATE_BUFFER_TEST(sin6.sin6_addr, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT16_TEST(sin6.sin6_port, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_flowinfo, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_scope_id, union slirp_sockaddr, + slirp_family_inet6), +#endif + + VMSTATE_END_OF_LIST() } +}; + +static const VMStateDescription vmstate_slirp_socket = { + .name = "slirp-socket", + .version_id = 4, + .pre_load = slirp_socket_pre_load, + .fields = + (VMStateField[]){ + VMSTATE_UINT32(so_urgc, struct socket), + /* Pre-v4 versions */ + VMSTATE_SIN4_ADDR(so_faddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_SIN4_ADDR(so_laddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_fport, struct socket, slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_lport, struct socket, slirp_older_than_v4), + /* v4 and newer */ + VMSTATE_STRUCT(fhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + VMSTATE_STRUCT(lhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + + VMSTATE_UINT8(so_iptos, struct socket), + VMSTATE_UINT8(so_emu, struct socket), + VMSTATE_UINT8(so_type, struct socket), + VMSTATE_INT32(so_state, struct socket), + VMSTATE_STRUCT(so_rcv, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT(so_snd, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT_POINTER(so_tcpcb, struct socket, vmstate_slirp_tcp, + struct tcpcb), + VMSTATE_END_OF_LIST() } +}; + +static const VMStateDescription vmstate_slirp_bootp_client = { + .name = "slirp_bootpclient", + .fields = (VMStateField[]){ VMSTATE_UINT16(allocated, BOOTPClient), + VMSTATE_BUFFER(macaddr, BOOTPClient), + VMSTATE_END_OF_LIST() } +}; + +static const VMStateDescription vmstate_slirp = { + .name = "slirp", + .version_id = 4, + .fields = (VMStateField[]){ VMSTATE_UINT16_V(ip_id, Slirp, 2), + VMSTATE_STRUCT_ARRAY( + bootp_clients, Slirp, NB_BOOTP_CLIENTS, 3, + vmstate_slirp_bootp_client, BOOTPClient), + VMSTATE_END_OF_LIST() } +}; + +int slirp_state_save(Slirp *slirp, SlirpWriteCb write_cb, void *opaque) +{ + struct gfwd_list *ex_ptr; + SlirpOStream f = { + .write_cb = write_cb, + .opaque = opaque, + }; + + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) + if (ex_ptr->write_cb) { + struct socket *so; + so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, + ntohs(ex_ptr->ex_fport)); + if (!so) { + continue; + } + + slirp_ostream_write_u8(&f, 42); + slirp_vmstate_save_state(&f, &vmstate_slirp_socket, so); + } + slirp_ostream_write_u8(&f, 0); + + slirp_vmstate_save_state(&f, &vmstate_slirp, slirp); + + return 0; +} + + +int slirp_state_load(Slirp *slirp, int version_id, SlirpReadCb read_cb, + void *opaque) +{ + struct gfwd_list *ex_ptr; + SlirpIStream f = { + .read_cb = read_cb, + .opaque = opaque, + }; + + while (slirp_istream_read_u8(&f)) { + int ret; + struct socket *so = socreate(slirp, -1); + + ret = + slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id); + if (ret < 0) { + return ret; + } + + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) != + slirp->vnetwork_addr.s_addr) { + return -EINVAL; + } + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->write_cb && + so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr && + so->so_fport == ex_ptr->ex_fport) { + break; + } + } + if (!ex_ptr) { + return -EINVAL; + } + + so->guestfwd = ex_ptr; + } + + return slirp_vmstate_load_state(&f, &vmstate_slirp, slirp, version_id); +} + +int slirp_state_version(void) +{ + return 4; +} diff --git a/libslirp/src/stream.c b/libslirp/src/stream.c new file mode 100644 index 000000000..6cf326f66 --- /dev/null +++ b/libslirp/src/stream.c @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp io streams + * + * Copyright (c) 2018 Red Hat, Inc. + * + * 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 "stream.h" +#include + +bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size) +{ + return f->read_cb(buf, size, f->opaque) == size; +} + +bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size) +{ + return f->write_cb(buf, size, f->opaque) == size; +} + +uint8_t slirp_istream_read_u8(SlirpIStream *f) +{ + uint8_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return b; + } + + return 0; +} + +bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b) +{ + return slirp_ostream_write(f, &b, sizeof(b)); +} + +uint16_t slirp_istream_read_u16(SlirpIStream *f) +{ + uint16_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GUINT16_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b) +{ + b = GUINT16_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +uint32_t slirp_istream_read_u32(SlirpIStream *f) +{ + uint32_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GUINT32_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b) +{ + b = GUINT32_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +int16_t slirp_istream_read_i16(SlirpIStream *f) +{ + int16_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GINT16_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b) +{ + b = GINT16_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +int32_t slirp_istream_read_i32(SlirpIStream *f) +{ + int32_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GINT32_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b) +{ + b = GINT32_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} diff --git a/libslirp/src/stream.h b/libslirp/src/stream.h new file mode 100644 index 000000000..4cdc2369c --- /dev/null +++ b/libslirp/src/stream.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef STREAM_H_ +#define STREAM_H_ + +#include "libslirp.h" + +typedef struct SlirpIStream { + SlirpReadCb read_cb; + void *opaque; +} SlirpIStream; + +typedef struct SlirpOStream { + SlirpWriteCb write_cb; + void *opaque; +} SlirpOStream; + +/* Read exactly size bytes from stream, return 1 if all ok, 0 otherwise */ +bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size); +/* Write exactly size bytes to stream, return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size); + +/* Read exactly one byte from stream, return it, otherwise return 0 */ +uint8_t slirp_istream_read_u8(SlirpIStream *f); +/* Write exactly one byte to stream, return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b); + +/* Read exactly two bytes from big-endian stream, return it, otherwise return 0 */ +uint16_t slirp_istream_read_u16(SlirpIStream *f); +/* Write exactly two bytes to big-endian stream, return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b); + +/* Read exactly four bytes from big-endian stream, return it, otherwise return 0 */ +uint32_t slirp_istream_read_u32(SlirpIStream *f); +/* Write exactly four bytes to big-endian stream, return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b); + +/* Read exactly two bytes from big-endian stream (signed), return it, otherwise return 0 */ +int16_t slirp_istream_read_i16(SlirpIStream *f); +/* Write exactly two bytes to big-endian stream (signed), return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b); + +/* Read exactly four bytes from big-endian stream (signed), return it, otherwise return 0 */ +int32_t slirp_istream_read_i32(SlirpIStream *f); +/* Write exactly four bytes to big-endian stream (signed), return 1 if all ok, 0 otherwise */ +bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b); + +#endif /* STREAM_H_ */ diff --git a/libslirp/src/tcp.h b/libslirp/src/tcp.h new file mode 100644 index 000000000..f678eaeab --- /dev/null +++ b/libslirp/src/tcp.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 + +#include + +typedef uint32_t tcp_seq; + +#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ +#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ + +#define TCP_SNDSPACE 1024 * 128 +#define TCP_RCVSPACE 1024 * 128 +#define TCP_MAXSEG_MAX 32768 + +/* + * TCP header. + * Per RFC 793, September, 1981. + */ +#define tcphdr slirp_tcphdr +struct tcphdr { + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ + tcp_seq th_seq; /* sequence number */ + tcp_seq th_ack; /* acknowledgement number */ +#if (G_BYTE_ORDER == G_BIG_ENDIAN) && !defined(_MSC_VER) + uint8_t th_off : 4, /* data offset */ + th_x2 : 4; /* (unused) */ +#else + uint8_t th_x2 : 4, /* (unused) */ + th_off : 4; /* data offset */ +#endif + uint8_t th_flags; + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ +}; + +#include "tcp_var.h" + +#ifndef TH_FIN +#define TH_FIN 0x01 +#define TH_SYN 0x02 +#define TH_RST 0x04 +#define TH_PUSH 0x08 +#define TH_ACK 0x10 +#define TH_URG 0x20 +#endif + +#ifndef TCPOPT_EOL +#define TCPOPT_EOL 0 +#define TCPOPT_NOP 1 +#define TCPOPT_MAXSEG 2 +#define TCPOPT_WINDOW 3 +#define TCPOPT_SACK_PERMITTED 4 /* Experimental */ +#define TCPOPT_SACK 5 /* Experimental */ +#define TCPOPT_TIMESTAMP 8 + +#define TCPOPT_TSTAMP_HDR \ + (TCPOPT_NOP << 24 | TCPOPT_NOP << 16 | TCPOPT_TIMESTAMP << 8 | \ + TCPOLEN_TIMESTAMP) +#endif + +#ifndef TCPOLEN_MAXSEG +#define TCPOLEN_MAXSEG 4 +#define TCPOLEN_WINDOW 3 +#define TCPOLEN_SACK_PERMITTED 2 +#define TCPOLEN_TIMESTAMP 10 +#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP + 2) /* appendix A */ +#endif + +#undef TCP_MAXWIN +#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ + +#undef TCP_MAX_WINSHIFT +#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 + +/* + * 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 */ + +#endif diff --git a/libslirp/src/tcp_input.c b/libslirp/src/tcp_input.c new file mode 100644 index 000000000..5bee7d6c8 --- /dev/null +++ b/libslirp/src/tcp_input.c @@ -0,0 +1,1566 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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. + */ + +#include "slirp.h" +#include "ip_icmp.h" + +#define TCPREXMTTHRESH 3 + +#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. + * Set DELACK for segments received in order, but ack immediately + * when segments are out of order (so fast retransmit can work). + */ + +static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, + struct tcpiphdr *ti); +static void tcp_xmit_timer(register struct tcpcb *tp, int rtt); + +static int tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, + struct mbuf *m) +{ + register struct tcpiphdr *q; + struct socket *so = tp->t_socket; + int flags; + + /* + * Call with ti==NULL after become established to + * force pre-ESTABLISHED data up to user socket. + */ + if (ti == NULL) + goto present; + + /* + * Find a segment which begins after this one does. + */ + for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp); + q = tcpiphdr_next(q)) + 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 (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) { + register int i; + q = tcpiphdr_prev(q); + /* 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) { + m_free(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 = tcpiphdr_next(q); + } + ti->ti_mbuf = m; + + /* + * While we overlap succeeding segments trim them or, + * if they are completely covered, dequeue them. + */ + while (!tcpfrag_list_end(q, tp)) { + register 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(q->ti_mbuf, i); + break; + } + q = tcpiphdr_next(q); + m = tcpiphdr_prev(q)->ti_mbuf; + slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(q))); + m_free(m); + } + + /* + * Stick new segment in its place. + */ + slirp_insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); + +present: + /* + * Present data to user, advancing rcv_nxt through + * completed sequence space. + */ + if (!TCPS_HAVEESTABLISHED(tp->t_state)) + return (0); + ti = tcpfrag_list_first(tp); + if (tcpfrag_list_end(ti, 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; + slirp_remque(tcpiphdr2qlink(ti)); + m = ti->ti_mbuf; + ti = tcpiphdr_next(ti); + if (so->so_state & SS_FCANTSENDMORE) + m_free(m); + else { + if (so->so_emu) { + if (tcp_emu(so, m)) + sbappend(so, m); + } else + sbappend(so, m); + } + } while (!tcpfrag_list_end(ti, tp) && ti->ti_seq == tp->rcv_nxt); + return (flags); +} + +/* + * TCP input routine, follows pages 65-76 of the + * protocol specification dated September, 1981 very closely. + */ +void tcp_input(struct mbuf *m, int iphlen, struct socket *inso, + unsigned short af) +{ + struct ip save_ip, *ip; + struct ip6 save_ip6, *ip6; + register struct tcpiphdr *ti; + char *optp = NULL; + int optlen = 0; + int len, tlen, off; + register struct tcpcb *tp = NULL; + register int tiflags; + struct socket *so = NULL; + int todrop, acked, ourfinisacked, needoutput = 0; + int iss = 0; + uint32_t tiwin; + int ret; + struct sockaddr_storage lhost, fhost; + struct sockaddr_in *lhost4, *fhost4; + struct sockaddr_in6 *lhost6, *fhost6; + struct gfwd_list *ex_ptr; + Slirp *slirp; + + DEBUG_CALL("tcp_input"); + DEBUG_ARG("m = %p iphlen = %2d inso = %p", m, iphlen, inso); + + memset(&lhost, 0, sizeof(struct sockaddr_storage)); + memset(&fhost, 0, sizeof(struct sockaddr_storage)); + + /* + * If called with m == 0, then we're continuing the connect + */ + if (m == NULL) { + so = inso; + slirp = so->slirp; + + /* Re-set a few variables */ + tp = sototcpcb(so); + m = so->so_m; + so->so_m = NULL; + ti = so->so_ti; + tiwin = ti->ti_win; + tiflags = ti->ti_flags; + + goto cont_conn; + } + slirp = m->slirp; + switch (af) { + case AF_INET: + M_DUP_DEBUG(slirp, m, 0, + sizeof(struct qlink) + sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr)); + break; + case AF_INET6: + M_DUP_DEBUG(slirp, m, 0, + sizeof(struct qlink) + sizeof(struct tcpiphdr) - sizeof(struct ip6) - sizeof(struct tcphdr)); + break; + } + + ip = mtod(m, struct ip *); + ip6 = mtod(m, struct ip6 *); + + switch (af) { + case AF_INET: + if (iphlen > sizeof(struct ip)) { + ip_stripoptions(m); + 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. + */ + save_ip = *ip; + save_ip.ip_len += iphlen; + + /* + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. + */ + m->m_data -= + sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr); + m->m_len += + sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr); + ti = mtod(m, struct tcpiphdr *); + + /* + * Checksum extended TCP header and data. + */ + tlen = ip->ip_len; + tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; + memset(&ti->ih_mbuf, 0, sizeof(struct mbuf_ptr)); + memset(&ti->ti, 0, sizeof(ti->ti)); + ti->ti_x0 = 0; + ti->ti_src = save_ip.ip_src; + ti->ti_dst = save_ip.ip_dst; + ti->ti_pr = save_ip.ip_p; + ti->ti_len = htons((uint16_t)tlen); + break; + + case AF_INET6: + /* + * Save a copy of the IP header in case we want restore it + * for sending an ICMP error message in response. + */ + save_ip6 = *ip6; + /* + * Get IP and TCP header together in first mbuf. + * Note: IP leaves IP header in first mbuf. + */ + m->m_data -= sizeof(struct tcpiphdr) - + (sizeof(struct ip6) + sizeof(struct tcphdr)); + m->m_len += sizeof(struct tcpiphdr) - + (sizeof(struct ip6) + sizeof(struct tcphdr)); + ti = mtod(m, struct tcpiphdr *); + + tlen = ip6->ip_pl; + tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; + memset(&ti->ih_mbuf, 0, sizeof(struct mbuf_ptr)); + memset(&ti->ti, 0, sizeof(ti->ti)); + ti->ti_x0 = 0; + ti->ti_src6 = save_ip6.ip_src; + ti->ti_dst6 = save_ip6.ip_dst; + ti->ti_nh6 = save_ip6.ip_nh; + ti->ti_len = htons((uint16_t)tlen); + break; + + default: + g_assert_not_reached(); + } + + len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen); + if (cksum(m, len)) { + 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) { + goto drop; + } + tlen -= off; + ti->ti_len = tlen; + if (off > sizeof(struct tcphdr)) { + optlen = off - sizeof(struct tcphdr); + optp = mtod(m, char *) + sizeof(struct tcpiphdr); + } + 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: + lhost.ss_family = af; + fhost.ss_family = af; + switch (af) { + case AF_INET: + lhost4 = (struct sockaddr_in *)&lhost; + lhost4->sin_addr = ti->ti_src; + lhost4->sin_port = ti->ti_sport; + fhost4 = (struct sockaddr_in *)&fhost; + fhost4->sin_addr = ti->ti_dst; + fhost4->sin_port = ti->ti_dport; + break; + case AF_INET6: + lhost6 = (struct sockaddr_in6 *)&lhost; + lhost6->sin6_addr = ti->ti_src6; + lhost6->sin6_port = ti->ti_sport; + fhost6 = (struct sockaddr_in6 *)&fhost; + fhost6->sin6_addr = ti->ti_dst6; + fhost6->sin6_port = ti->ti_dport; + break; + default: + g_assert_not_reached(); + } + + so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost); + + /* + * 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 == NULL) { + /* TODO: IPv6 */ + if (slirp->restricted) { + /* Any hostfwds will have an existing socket, so we only get here + * for non-hostfwd connections. These should be dropped, unless it + * happens to be a guestfwd. + */ + for (ex_ptr = slirp->guestfwd_list; ex_ptr; + ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_fport == ti->ti_dport && + ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) { + break; + } + } + if (!ex_ptr) { + goto dropwithreset; + } + } + + if ((tiflags & (TH_SYN | TH_FIN | TH_RST | TH_URG | TH_ACK)) != TH_SYN) + goto dropwithreset; + + so = socreate(slirp, IPPROTO_TCP); + tcp_attach(so); + + sbreserve(&so->so_snd, TCP_SNDSPACE); + sbreserve(&so->so_rcv, TCP_RCVSPACE); + + so->lhost.ss = lhost; + so->fhost.ss = fhost; + + so->so_iptos = tcp_tos(so); + if (so->so_iptos == 0) { + switch (af) { + case AF_INET: + so->so_iptos = ((struct ip *)ti)->ip_tos; + break; + case AF_INET6: + break; + default: + g_assert_not_reached(); + } + } + + 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 == NULL) + goto dropwithreset; + if (tp->t_state == TCPS_CLOSED) + goto drop; + + tiwin = ti->ti_win; + + /* + * Segment received on connection. + * Reset idle time and keep-alive timer. + */ + tp->t_idle = 0; + if (slirp_do_keepalive) + tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; + else + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; + + /* + * 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, (uint8_t *)optp, optlen, ti); + + /* + * 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 && + ti->ti_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && + tp->snd_nxt == tp->snd_max) { + 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. + */ + 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; + sodrop(so, acked); + tp->snd_una = ti->ti_ack; + m_free(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; + + /* + * 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) + tcp_output(tp); + + return; + } + } else if (ti->ti_ack == tp->snd_una && tcpfrag_list_empty(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. + */ + tp->rcv_nxt += ti->ti_len; + /* + * Add data to socket buffer. + */ + if (so->so_emu) { + if (tcp_emu(so, m)) + sbappend(so, m); + } else + sbappend(so, m); + + /* + * 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 + */ + /* TODO: IPv6 */ + if (af == AF_INET && + (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr && + so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) { + /* May be an add exec */ + for (ex_ptr = slirp->guestfwd_list; ex_ptr; + ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_fport == so->so_fport && + so->so_faddr.s_addr == ex_ptr->ex_addr.s_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, so->so_ffamily) == -1) && (errno != EAGAIN) && + (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { + uint8_t code; + DEBUG_MISC(" tcp fconnect errno = %d-%s", errno, g_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, af); + } else { + switch (af) { + case AF_INET: + code = ICMP_UNREACH_NET; + if (errno == EHOSTUNREACH) { + code = ICMP_UNREACH_HOST; + } + break; + case AF_INET6: + code = ICMP6_UNREACH_NO_ROUTE; + if (errno == EHOSTUNREACH) { + code = ICMP6_UNREACH_ADDRESS; + } + break; + default: + g_assert_not_reached(); + } + 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); + switch (af) { + case AF_INET: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip) - + sizeof(struct tcphdr); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct ip) - + sizeof(struct tcphdr); + *ip = save_ip; + icmp_send_error(m, ICMP_UNREACH, code, 0, g_strerror(errno)); + break; + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - + (sizeof(struct ip6) + sizeof(struct tcphdr)); + m->m_len -= sizeof(struct tcpiphdr) - + (sizeof(struct ip6) + sizeof(struct tcphdr)); + *ip6 = save_ip6; + icmp6_send_error(m, ICMP6_UNREACH, code); + break; + default: + g_assert_not_reached(); + } + } + tcp_close(tp); + m_free(m); + } else { + /* + * Haven't connected yet, save the current mbuf + * 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; + /* + * Initialize receive sequence numbers now so that we can send a + * valid RST if the remote end rejects our connection. + */ + tp->irs = ti->ti_seq; + tcp_rcvseqinit(tp); + tcp_template(tp); + } + 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, (uint8_t *)optp, optlen, ti); + + if (iss) + tp->iss = iss; + else + tp->iss = slirp->tcp_iss; + slirp->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; + 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 (g_getenv("SLIRP_FUZZING") && + /* Align seq numbers on what the fuzzing trace says */ + tp->iss == 1 && ti->ti_ack != 0) { + tp->iss = ti->ti_ack - 1; + tp->snd_max = tp->iss + 1; + } + + 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) { + 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)) { + soisfconnected(so); + tp->t_state = TCPS_ESTABLISHED; + + tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)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; + } + 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. + * 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. + */ + 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; + } + 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); + 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) { + if (todrop >= 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; + } else { + goto dropafterack; + } + } + m_adj(m, -todrop); + ti->ti_len -= todrop; + tiflags &= ~(TH_PUSH | TH_FIN); + } + + /* + * 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: + case TCPS_ESTABLISHED: + case TCPS_FIN_WAIT_1: + case TCPS_FIN_WAIT_2: + case TCPS_CLOSE_WAIT: + tp->t_state = TCPS_CLOSED; + tcp_close(tp); + goto drop; + + case TCPS_CLOSING: + case TCPS_LAST_ACK: + case TCPS_TIME_WAIT: + 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 (g_getenv("SLIRP_FUZZING") && + /* Align seq numbers on what the fuzzing trace says */ + tp->iss == 1 && ti->ti_ack != 0) { + tp->iss = ti->ti_ack - 1; + tp->snd_max = tp->iss + 1; + tp->snd_una = ti->ti_ack; + } + + if (SEQ_GT(tp->snd_una, ti->ti_ack) || SEQ_GT(ti->ti_ack, tp->snd_max)) + goto dropwithreset; + 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_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; /* CTL_CMD */ + } else { + needoutput = 1; + tp->t_state = TCPS_FIN_WAIT_1; + } + } else { + soisfconnected(so); + } + + tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)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) { + DEBUG_MISC(" dup ack m = %p so = %p", m, 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; + unsigned 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; + 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; + 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)) { + goto dropafterack; + } + acked = ti->ti_ack - tp->snd_una; + + /* + * If 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 (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). + */ + { + register unsigned cw = tp->snd_cwnd; + register unsigned incr = tp->t_maxseg; + + if (cw > tp->snd_ssthresh) + incr = incr * incr / cw; + tp->snd_cwnd = MIN(cw + incr, TCP_MAXWIN << tp->snd_scale); + } + if (acked > so->so_snd.sb_cc) { + tp->snd_wnd -= so->so_snd.sb_cc; + sodrop(so, (int)so->so_snd.sb_cc); + ourfinisacked = 1; + } else { + sodrop(so, acked); + tp->snd_wnd -= acked; + ourfinisacked = 0; + } + 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) { + 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; + } + 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) { + 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))))) { + 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: + + /* + * If this is a small packet, then ACK now - with Nagel + * congestion avoidance sender won't send more until + * he gets an ACK. + */ + if (ti->ti_len && (unsigned)ti->ti_len <= 5 && + ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { + tp->t_flags |= TF_ACKNOW; + } + + /* + * 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) { + + /* + * segment is the next to be received on an established + * connection, and the queue is empty, avoid linkage into and + * removal from the queue and repetition of various + * conversions from tcp_reass(). + */ + if (ti->ti_seq == tp->rcv_nxt && tcpfrag_list_empty(tp) && + tp->t_state == TCPS_ESTABLISHED) { + tp->t_flags |= TF_DELACK; + tp->rcv_nxt += ti->ti_len; + tiflags = ti->ti_flags & TH_FIN; + if (so->so_emu) { + if (tcp_emu(so, m)) + sbappend(so, m); + } else + sbappend(so, m); + } else { + tiflags = tcp_reass(tp, ti, m); + tp->t_flags |= TF_ACKNOW; + } + } 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. + */ + 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; + 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; + } + } + + /* + * Return any desired output. + */ + if (needoutput || (tp->t_flags & TF_ACKNOW)) { + 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_free(m); + tp->t_flags |= TF_ACKNOW; + 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, af); + 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, af); + } + + return; + +drop: + /* + * Drop space held by incoming segment and return. + */ + m_free(m); +} + +static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, + struct tcpiphdr *ti) +{ + uint16_t mss; + int opt, optlen; + + DEBUG_CALL("tcp_dooptions"); + DEBUG_ARG("tp = %p cnt=%i", 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); + tcp_mss(tp, mss); /* sets t_maxseg */ + break; + } + } +} + +/* + * Collect new round-trip time estimate + * and update averages and current timeout. + */ + +static void tcp_xmit_timer(register struct tcpcb *tp, int rtt) +{ + register short delta; + + DEBUG_CALL("tcp_xmit_timer"); + DEBUG_ARG("tp = %p", tp); + DEBUG_ARG("rtt = %d", rtt); + + 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 mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES + * to utilize large mbufs. 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(struct tcpcb *tp, unsigned offer) +{ + struct socket *so = tp->t_socket; + int mss; + + DEBUG_CALL("tcp_mss"); + DEBUG_ARG("tp = %p", tp); + DEBUG_ARG("offer = %d", offer); + + switch (so->so_ffamily) { + case AF_INET: + mss = MIN(so->slirp->if_mtu, so->slirp->if_mru) - + sizeof(struct tcphdr) - sizeof(struct ip); + break; + case AF_INET6: + mss = MIN(so->slirp->if_mtu, so->slirp->if_mru) - + sizeof(struct tcphdr) - sizeof(struct ip6); + break; + default: + g_assert_not_reached(); + } + + if (offer) + mss = MIN(mss, offer); + mss = MAX(mss, 32); + if (mss < tp->t_maxseg || offer != 0) + tp->t_maxseg = MIN(mss, TCP_MAXSEG_MAX); + + 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(" returning mss = %d", mss); + + return mss; +} diff --git a/libslirp/src/tcp_output.c b/libslirp/src/tcp_output.c new file mode 100644 index 000000000..77c1204a5 --- /dev/null +++ b/libslirp/src/tcp_output.c @@ -0,0 +1,513 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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. + */ + +#include "slirp.h" + +static const uint8_t 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, +}; + + +#undef MAX_TCPOPTLEN +#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(struct tcpcb *tp) +{ + register struct socket *so = tp->t_socket; + register long len, win; + int off, flags, error; + register struct mbuf *m; + register struct tcpiphdr *ti, tcpiph_save; + struct ip *ip; + struct ip6 *ip6; + uint8_t opt[MAX_TCPOPTLEN]; + unsigned optlen, hdrlen; + int idle, sendalot; + + DEBUG_CALL("tcp_output"); + DEBUG_ARG("tp = %p", 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(" --- tcp_output flags = 0x%x", 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. + */ + 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 mbuf, 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) { + uint16_t mss; + + opt[0] = TCPOPT_MAXSEG; + opt[1] = 4; + mss = htons((uint16_t)tcp_mss(tp, 0)); + memcpy((char *)(opt + 2), (char *)&mss, sizeof(mss)); + optlen = 4; + } + } + + 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 mbuf, attaching a copy of data to + * be transmitted, and initialize the header from + * the template for sends on this connection. + */ + if (len) { + m = m_get(so->slirp); + if (m == NULL) { + error = 1; + goto out; + } + m->m_data += IF_MAXLINKHDR; + m->m_len = hdrlen; + + sbcopy(&so->so_snd, off, (int)len, mtod(m, char *) + hdrlen); + m->m_len += len; + + /* + * 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 { + m = m_get(so->slirp); + if (m == NULL) { + error = 1; + goto out; + } + m->m_data += IF_MAXLINKHDR; + m->m_len = hdrlen; + } + + ti = mtod(m, struct tcpiphdr *); + + memcpy((char *)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((char *)(ti + 1), (char *)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((uint16_t)(win >> tp->rcv_scale)); + + if (SEQ_GT(tp->snd_up, tp->snd_una)) { + ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); + 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((uint16_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; + } + } + + /* + * 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 */ + tcpiph_save = *mtod(m, struct tcpiphdr *); + + switch (so->so_ffamily) { + case AF_INET: + m->m_data += + sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); + m->m_len -= + sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); + ip = mtod(m, struct ip *); + + ip->ip_len = m->m_len; + ip->ip_dst = tcpiph_save.ti_dst; + ip->ip_src = tcpiph_save.ti_src; + ip->ip_p = tcpiph_save.ti_pr; + + ip->ip_ttl = IPDEFTTL; + ip->ip_tos = so->so_iptos; + error = ip_output(so, m); + break; + + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - + sizeof(struct ip6); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - + sizeof(struct ip6); + ip6 = mtod(m, struct ip6 *); + + ip6->ip_pl = tcpiph_save.ti_len; + ip6->ip_dst = tcpiph_save.ti_dst6; + ip6->ip_src = tcpiph_save.ti_src6; + ip6->ip_nh = tcpiph_save.ti_nh6; + + error = ip6_output(so, m, 0); + break; + + default: + g_assert_not_reached(); + } + + if (error) { + out: + return (error); + } + + /* + * 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(struct tcpcb *tp) +{ + int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; + + 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/libslirp/src/tcp_subr.c b/libslirp/src/tcp_subr.c new file mode 100644 index 000000000..91c2b4e6b --- /dev/null +++ b/libslirp/src/tcp_subr.c @@ -0,0 +1,1007 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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. + */ + +#include "slirp.h" + +#if defined(_WIN32) +#include +#endif + +/* patchable/settable parameters for tcp */ +/* Don't do rfc1323 performance enhancements */ +#define TCP_DO_RFC1323 0 + +/* + * Tcp initialization + */ +void tcp_init(Slirp *slirp) +{ + slirp->tcp_iss = 1; /* wrong */ + slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb; + slirp->tcp_last_so = &slirp->tcb; +} + +void tcp_cleanup(Slirp *slirp) +{ + while (slirp->tcb.so_next != &slirp->tcb) { + tcp_close(sototcpcb(slirp->tcb.so_next)); + } +} + +void tcp_template(struct tcpcb *tp) +{ + struct socket *so = tp->t_socket; + register struct tcpiphdr *n = &tp->t_template; + + n->ti_mbuf = NULL; + memset(&n->ti, 0, sizeof(n->ti)); + n->ti_x0 = 0; + switch (so->so_ffamily) { + case AF_INET: + n->ti_pr = IPPROTO_TCP; + n->ti_len = htons(sizeof(struct tcphdr)); + n->ti_src = so->so_faddr; + n->ti_dst = so->so_laddr; + n->ti_sport = so->so_fport; + n->ti_dport = so->so_lport; + break; + + case AF_INET6: + n->ti_nh6 = IPPROTO_TCP; + n->ti_len = htons(sizeof(struct tcphdr)); + n->ti_src6 = so->so_faddr6; + n->ti_dst6 = so->so_laddr6; + n->ti_sport = so->so_fport6; + n->ti_dport = so->so_lport6; + break; + + default: + g_assert_not_reached(); + } + + 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 mbuf containing it and any other + * attached mbufs. + * + * In any case the ack and sequence number of the transmitted + * segment are as specified by the parameters. + */ +void tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, + tcp_seq ack, tcp_seq seq, int flags, unsigned short af) +{ + register int tlen; + int win = 0; + + DEBUG_CALL("tcp_respond"); + DEBUG_ARG("tp = %p", tp); + DEBUG_ARG("ti = %p", ti); + DEBUG_ARG("m = %p", 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 == NULL) { + if (!tp || (m = m_get(tp->t_socket->slirp)) == NULL) + return; + tlen = 0; + m->m_data += IF_MAXLINKHDR; + *mtod(m, struct tcpiphdr *) = *ti; + ti = mtod(m, struct tcpiphdr *); + switch (af) { + case AF_INET: + ti->ti.ti_i4.ih_x1 = 0; + break; + case AF_INET6: + ti->ti.ti_i6.ih_x1 = 0; + break; + default: + g_assert_not_reached(); + } + flags = TH_ACK; + } else { + /* + * ti points into m so the next line is just making + * the mbuf point to ti + */ + m->m_data = (char *)ti; + + m->m_len = sizeof(struct tcpiphdr); + tlen = 0; +#define xchg(a, b, type) \ + { \ + type t; \ + t = a; \ + a = b; \ + b = t; \ + } + switch (af) { + case AF_INET: + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); + break; + case AF_INET6: + xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); + break; + default: + g_assert_not_reached(); + } +#undef xchg + } + ti->ti_len = htons((uint16_t)(sizeof(struct tcphdr) + tlen)); + tlen += sizeof(struct tcpiphdr); + m->m_len = tlen; + + ti->ti_mbuf = NULL; + ti->ti_x0 = 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((uint16_t)(win >> tp->rcv_scale)); + else + ti->ti_win = htons((uint16_t)win); + ti->ti_urp = 0; + ti->ti_sum = 0; + ti->ti_sum = cksum(m, tlen); + + struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *)); + struct ip *ip; + struct ip6 *ip6; + + switch (af) { + case AF_INET: + m->m_data += + sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); + m->m_len -= + sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); + ip = mtod(m, struct ip *); + ip->ip_len = m->m_len; + ip->ip_dst = tcpiph_save.ti_dst; + ip->ip_src = tcpiph_save.ti_src; + ip->ip_p = tcpiph_save.ti_pr; + + if (flags & TH_RST) { + ip->ip_ttl = MAXTTL; + } else { + ip->ip_ttl = IPDEFTTL; + } + + ip_output(NULL, m); + break; + + case AF_INET6: + m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - + sizeof(struct ip6); + m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - + sizeof(struct ip6); + ip6 = mtod(m, struct ip6 *); + ip6->ip_pl = tcpiph_save.ti_len; + ip6->ip_dst = tcpiph_save.ti_dst6; + ip6->ip_src = tcpiph_save.ti_src6; + ip6->ip_nh = tcpiph_save.ti_nh6; + + ip6_output(NULL, m, 0); + break; + + default: + g_assert_not_reached(); + } +} + +struct tcpcb *tcp_newtcpcb(struct socket *so) +{ + register struct tcpcb *tp; + + tp = g_new0(struct tcpcb, 1); + tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp; + /* + * 40: length of IPv4 header (20) + TCP header (20) + * 60: length of IPv6 header (40) + TCP header (20) + */ + tp->t_maxseg = + MIN(so->slirp->if_mtu - ((so->so_ffamily == AF_INET) ? 40 : 60), + TCP_MAXSEG_MAX); + + 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 = TCPTV_SRTTDFLT << 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); +} + +struct tcpcb *tcp_drop(struct tcpcb *tp, int err) +{ + DEBUG_CALL("tcp_drop"); + DEBUG_ARG("tp = %p", tp); + DEBUG_ARG("errno = %d", errno); + + if (TCPS_HAVERCVDSYN(tp->t_state)) { + tp->t_state = TCPS_CLOSED; + tcp_output(tp); + } + return (tcp_close(tp)); +} + +struct tcpcb *tcp_close(struct tcpcb *tp) +{ + register struct tcpiphdr *t; + struct socket *so = tp->t_socket; + Slirp *slirp = so->slirp; + register struct mbuf *m; + + DEBUG_CALL("tcp_close"); + DEBUG_ARG("tp = %p", tp); + + /* free the reassembly queue, if any */ + t = tcpfrag_list_first(tp); + while (!tcpfrag_list_end(t, tp)) { + t = tcpiphdr_next(t); + m = tcpiphdr_prev(t)->ti_mbuf; + slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(t))); + m_free(m); + } + g_free(tp); + so->so_tcpcb = NULL; + /* clobber input socket cache if we're closing the cached connection */ + if (so == slirp->tcp_last_so) + slirp->tcp_last_so = &slirp->tcb; + slirp_unregister_poll_socket(so); + closesocket(so->s); + sbfree(&so->so_rcv); + sbfree(&so->so_snd); + sofree(so); + return ((struct tcpcb *)0); +} + +/* + * 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(struct tcpcb *tp) +{ + DEBUG_CALL("tcp_sockclosed"); + DEBUG_ARG("tp = %p", tp); + + if (!tp) { + return; + } + + switch (tp->t_state) { + case TCPS_CLOSED: + case TCPS_LISTEN: + case TCPS_SYN_SENT: + tp->t_state = TCPS_CLOSED; + tcp_close(tp); + return; + + 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; + } + tcp_output(tp); +} + +/* + * 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(struct socket *so, unsigned short af) +{ + int ret = 0; + + DEBUG_CALL("tcp_fconnect"); + DEBUG_ARG("so = %p", so); + + so->s = slirp_socket(af, SOCK_STREAM, 0); + ret = have_valid_socket(so->s) ? 0 : -1; + if (ret == 0) { + ret = slirp_bind_outbound(so, af); + if (ret < 0) { + // bind failed - close socket + closesocket(so->s); + so->s = SLIRP_INVALID_SOCKET; + return (ret); + } + } + + if (ret >= 0) { + int opt; + slirp_os_socket s = so->s; + struct sockaddr_storage addr; + + slirp_set_nonblock(s); + slirp_register_poll_socket(so); + slirp_socket_set_fast_reuse(s); + opt = 1; + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (const void *) &opt, sizeof(opt)); + opt = 1; + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *) &opt, sizeof(opt)); + + addr = so->fhost.ss; + DEBUG_CALL(" connect()ing"); + if (sotranslate_out(so, &addr) < 0) { + return -1; + } + + /* We don't care what port we get */ + ret = connect(s, (struct sockaddr *)&addr, sockaddr_size(&addr)); + + /* + * If it's not in progress, it failed, so we just return 0, + * without clearing SS_NOFDREF + */ + soisfconnecting(so); + } + + return (ret); +} + +/* + * 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(struct socket *inso) +{ + Slirp *slirp = inso->slirp; + struct socket *so; + struct sockaddr_storage addr; + socklen_t addrlen; + struct tcpcb *tp; + slirp_os_socket s; + int opt, ret; + /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */ + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; + + DEBUG_CALL("tcp_connect"); + DEBUG_ARG("inso = %p", inso); + switch (inso->lhost.ss.ss_family) { + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; + default: + g_assert_not_reached(); + } + ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, addrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_ARG("ip = [%s]:%s", addrstr, portstr); + DEBUG_ARG("so_state = 0x%x", inso->so_state); + + /* Perform lazy guest IP address resolution if needed. */ + if (inso->so_state & SS_HOSTFWD) { + /* + * We can only reject the connection request by accepting it and + * then immediately closing it. Note that SS_FACCEPTONCE sockets can't + * get here. + */ + if (soassign_guest_addr_if_needed(inso) < 0) { + /* + * Guest address isn't available yet. We could either try to defer + * completing this connection request until the guest address is + * available, or punt. It's easier to punt. Otherwise we need to + * complicate the mechanism by which we're called to defer calling + * us again until the guest address is available. + */ + DEBUG_MISC(" guest address not available yet"); + addrlen = sizeof(addr); + s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); + if (have_valid_socket(s)) { + closesocket(s); + } + return; + } + } + + /* + * 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 { + so = socreate(slirp, IPPROTO_TCP); + tcp_attach(so); + so->lhost = inso->lhost; + so->so_ffamily = inso->so_ffamily; + } + + tcp_mss(sototcpcb(so), 0); + + addrlen = sizeof(addr); + s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); + if (not_valid_socket(s)) { + tcp_close(sototcpcb(so)); /* This will sofree() as well */ + return; + } + so->s = s; + slirp_set_nonblock(s); + slirp_register_poll_socket(so); + slirp_socket_set_fast_reuse(s); + opt = 1; + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (const void *) &opt, sizeof(int)); + slirp_socket_set_nodelay(s); + + so->fhost.ss = addr; + sotranslate_accept(so); + + /* Close the accept() socket, set right state */ + if (inso->so_state & SS_FACCEPTONCE) { + /* If we only accept once, close the accept() socket */ + slirp_unregister_poll_socket(so); + closesocket(so->s); + + /* Don't select it yet, even though we have an FD */ + /* if it's not FACCEPTONCE, it's already NOFDREF */ + so->so_state = SS_NOFDREF; + } + so->so_state |= SS_INCOMING; + + so->so_iptos = tcp_tos(so); + tp = sototcpcb(so); + + tcp_template(tp); + + tp->t_state = TCPS_SYN_SENT; + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; + tp->iss = slirp->tcp_iss; + slirp->tcp_iss += TCP_ISSINCR / 2; + tcp_sendseqinit(tp); + tcp_output(tp); +} + +void tcp_attach(struct socket *so) +{ + so->so_tcpcb = tcp_newtcpcb(so); + slirp_insque(so, &so->slirp->tcb); +} + +/* + * Set the socket's type of service field + */ +static const 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, 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 } +}; + +uint8_t tcp_tos(struct socket *so) +{ + int i = 0; + + 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))) { + if (so->slirp->enable_emu) + so->so_emu = tcptos[i].emu; + return tcptos[i].tos; + } + i++; + } + return 0; +} + +/* + * 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 If there is more than one command in the packet, the others may + * be truncated. + * XXX If the command is too long, it may be truncated. + * + * 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 mbuf m is still valid and should be + * sbappend()ed + * + * NOTE: if you return 0 you MUST m_free() the mbuf! + */ +int tcp_emu(struct socket *so, struct mbuf *m) +{ + Slirp *slirp = so->slirp; + unsigned n1, n2, n3, n4, n5, n6; + char buff[257]; + uint32_t laddr; + unsigned lport; + char *bptr; + + DEBUG_CALL("tcp_emu"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); + + switch (so->so_emu) { + int x, i; + + /* TODO: IPv6 */ + case EMU_IDENT: + /* + * Identification protocol as per rfc-1413 + */ + + { + struct socket *tmpso; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + char *eol = g_strstr_len(m->m_data, m->m_len, "\r\n"); + + if (!eol) { + return 1; + } + + *eol = '\0'; + + if (slirp_sscanf(m->m_data, "%u%*[ ,]%u", &n1, &n2) == 2) { + HTONS(n1); + HTONS(n2); + /* n2 is the one on our host */ + for (tmpso = slirp->tcb.so_next; tmpso != &slirp->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 = addr.sin_port; + break; + } + } + NTOHS(n1); + NTOHS(n2); + m_inc(m, g_snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1); + m->m_len = slirp_fmt(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); + } else { + *eol = '\r'; + } + + return 1; + } + + case EMU_FTP: /* ftp */ + m_inc(m, m->m_len + 1); + if ((bptr = (char *)g_strstr_len(m->m_data, m->m_len, "ORT")) != NULL) { + struct socket * control_so = so; + + /* + * Need to emulate the PORT command + */ + x = slirp_sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256s[^\177]", &n1, &n2, + &n3, &n4, &n5, &n6, sscanf_buf_arg(buff)); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr, lport, + SS_FACCEPTONCE)) == NULL) { + return 1; + } + n6 = ntohs(so->so_fport); + + n5 = (n6 >> 8) & 0xff; + n6 &= 0xff; + + if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + laddr = ntohl(so->so_faddr.s_addr); + } else { + /* local side address of control conn */ + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + if (getsockname(control_so->s, (struct sockaddr *) &addr, &addrlen) == 0) { + laddr = ntohl(addr.sin_addr.s_addr); + } else { + /* fall back */ + laddr = ntohl(slirp->vhost_addr.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 += slirp_fmt(bptr, M_FREEROOM(m), + "ORT %d,%d,%d,%d,%d,%d\r\n%s", + n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); + return 1; + } else if ((bptr = g_strstr_len(m->m_data, m->m_len, "27 Entering")) != NULL) { + /* + * Need to emulate the PASV response + */ + x = slirp_sscanf( + bptr, + "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256s[^\177]", + &n1, &n2, &n3, &n4, &n5, &n6, sscanf_buf_arg(buff)); + if (x < 6) + return 1; + + laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); + lport = htons((n5 << 8) | (n6)); + + if ((so = tcp_listen(slirp, INADDR_ANY, 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 += slirp_fmt(bptr, M_FREEROOM(m), + "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 = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr, + htons(lport), SS_FACCEPTONCE)) != NULL) + m->m_len = slirp_fmt0(m->m_data, M_ROOM(m), + "%d", ntohs(so->so_fport)); + return 1; + + case EMU_IRC: + /* + * Need to emulate DCC CHAT, DCC SEND and DCC MOVE + */ + m_inc(m, m->m_len + 1); + *(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 (slirp_sscanf(bptr, "DCC CHAT %256s %u %u", sscanf_buf_arg(buff), &laddr, &lport) == 3) { + if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), + htons(lport), SS_FACCEPTONCE)) == NULL) { + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += slirp_fmt(bptr, M_FREEROOM(m), + "DCC CHAT chat %lu %u%c\n", + (unsigned long)ntohl(so->so_faddr.s_addr), + ntohs(so->so_fport), 1); + } else if (slirp_sscanf(bptr, "DCC SEND %256s %u %u %u", sscanf_buf_arg(buff), &laddr, &lport, + &n1) == 4) { + if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), + htons(lport), SS_FACCEPTONCE)) == NULL) { + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += slirp_fmt(bptr, M_FREEROOM(m), + "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 (slirp_sscanf(bptr, "DCC MOVE %256s %u %u %u", sscanf_buf_arg(buff), &laddr, &lport, + &n1) == 4) { + if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), + htons(lport), SS_FACCEPTONCE)) == NULL) { + return 1; + } + m->m_len = bptr - m->m_data; /* Adjust length */ + m->m_len += slirp_fmt(bptr, M_FREEROOM(m), + "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) { + uint16_t 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: + if (bptr == m->m_data + m->m_len - 1) + return 1; /* We need two bytes */ + + /* + * 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. + */ + + if (bptr == m->m_data + m->m_len - 1) + return 1; /* We need two bytes */ + + lport = (((uint8_t *)bptr)[0] << 8) + ((uint8_t *)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(slirp, INADDR_ANY, htons(p), + so->so_laddr.s_addr, htons(lport), + SS_FACCEPTONCE)) { + break; + } + } + if (p == 7071) + p = 0; + *(uint8_t *)bptr++ = (p >> 8) & 0xff; + *(uint8_t *)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(struct socket *so) +{ + Slirp *slirp = so->slirp; + struct sbuf *sb = &so->so_snd; + struct gfwd_list *ex_ptr; + + DEBUG_CALL("tcp_ctl"); + DEBUG_ARG("so = %p", so); + + /* TODO: IPv6 */ + if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { + /* Check if it's pty_exec */ + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_fport == so->so_fport && + so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { + if (ex_ptr->write_cb) { + so->s = SLIRP_INVALID_SOCKET; + so->guestfwd = ex_ptr; + return 1; + } + DEBUG_MISC(" executing %s", ex_ptr->ex_exec); + if (ex_ptr->ex_unix) + return open_unix(so, ex_ptr->ex_unix); + else + return fork_exec(so, ex_ptr->ex_exec); + } + } + } + sb->sb_cc = slirp_fmt(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data), + "Error: No application configured.\r\n"); + sb->sb_wptr += sb->sb_cc; + return 0; +} diff --git a/libslirp/src/tcp_timer.c b/libslirp/src/tcp_timer.c new file mode 100644 index 000000000..aeb610fb3 --- /dev/null +++ b/libslirp/src/tcp_timer.c @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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" + +static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer); + +/* + * Fast timeout routine for processing delayed acks + */ +void tcp_fasttimo(Slirp *slirp) +{ + register struct socket *so; + register struct tcpcb *tp; + + DEBUG_CALL("tcp_fasttimo"); + + so = slirp->tcb.so_next; + if (so) + for (; so != &slirp->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; + 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(Slirp *slirp) +{ + register struct socket *ip, *ipnxt; + register struct tcpcb *tp; + register int i; + + DEBUG_CALL("tcp_slowtimo"); + + /* + * Search through tcb's and update active timers. + */ + ip = slirp->tcb.so_next; + if (ip == NULL) { + return; + } + for (; ip != &slirp->tcb; ip = ipnxt) { + ipnxt = ip->so_next; + tp = sototcpcb(ip); + if (tp == NULL) { + 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:; + } + slirp->tcp_iss += TCP_ISSINCR / PR_SLOWHZ; /* increment iss */ + slirp->tcp_now++; /* for timestamps */ +} + +void tcp_canceltimers(struct tcpcb *tp) +{ + register int i; + + for (i = 0; i < TCPT_NTIMERS; i++) + tp->t_timer[i] = 0; +} + +const int tcp_backoff[TCP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, + 64, 64, 64, 64, 64, 64 }; + +/* + * TCP timer processing. + */ +static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer) +{ + register 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] = TCPTV_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; + 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; + } + 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) { + 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.) + */ + { + unsigned 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; + } + tcp_output(tp); + break; + + /* + * Persistence timer into zero window. + * Force a byte to be output, if possible. + */ + case TCPT_PERSIST: + tcp_setpersist(tp); + tp->t_force = 1; + 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: + if (tp->t_state < TCPS_ESTABLISHED) + goto dropit; + + if (slirp_do_keepalive && tp->t_state <= TCPS_CLOSE_WAIT) { + if (tp->t_idle >= TCPTV_KEEP_IDLE + 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. + */ + tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL, tp->rcv_nxt, + tp->snd_una - 1, 0, tp->t_socket->so_ffamily); + tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; + } else + tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; + break; + + dropit: + tp = tcp_drop(tp, 0); + break; + } + + return (tp); +} diff --git a/slirp/tcp_timer.h b/libslirp/src/tcp_timer.h similarity index 67% rename from slirp/tcp_timer.h rename to libslirp/src/tcp_timer.h index 7b155ef9b..3a2e9448c 100644 --- a/slirp/tcp_timer.h +++ b/libslirp/src/tcp_timer.h @@ -1,127 +1,133 @@ -/* - * 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 ( 12*PR_SLOWHZ) /* max allowable REXMT value */ - -#define TCP_LINGERTIME 120 /* linger at most 2 minutes */ - -#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ - - -/* - * 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 const int tcp_backoff[]; - -struct tcpcb; - -void tcp_fasttimo(Slirp *); -void tcp_slowtimo(Slirp *); -void tcp_canceltimers(struct tcpcb *); - -#endif +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 (12 * PR_SLOWHZ) /* max allowable REXMT value */ + +#define TCP_LINGERTIME 120 /* linger at most 2 minutes */ + +#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ + + +/* + * 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 const int tcp_backoff[]; + +struct tcpcb; + +/* Process fast time-outs */ +void tcp_fasttimo(Slirp *); +/* Process slow time-outs */ +void tcp_slowtimo(Slirp *); +/* Cancel all timers for TCP tp */ +void tcp_canceltimers(struct tcpcb *); + +#endif diff --git a/libslirp/src/tcp_var.h b/libslirp/src/tcp_var.h new file mode 100644 index 000000000..c8da8cbd1 --- /dev/null +++ b/libslirp/src/tcp_var.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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" + +/* + * Tcp control block, one per tcp; fields: + */ +struct tcpcb { + struct tcpiphdr *seg_next; /* sequencing queue */ + struct tcpiphdr *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 */ + uint16_t t_maxseg; /* maximum segment size */ + uint8_t t_force; /* 1 if forcing out a byte */ + uint16_t 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 */ + + struct tcpiphdr t_template; /* static skeletal packet for xmit */ + + struct socket *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 */ + uint32_t snd_wnd; /* send window */ + /* receive sequence variables */ + uint32_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) + */ + uint32_t snd_cwnd; /* congestion-controlled window */ + uint32_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 */ + uint16_t t_rttmin; /* minimum rtt allowed */ + uint32_t max_sndwnd; /* largest window peer has offered */ + + /* out-of-band data */ + uint8_t t_oobflags; /* have some */ + uint8_t t_iobc; /* input character */ +#define TCPOOB_HAVEDATA 0x01 +#define TCPOOB_HADDATA 0x02 + short t_softerror; /* possible error not yet reported */ + + /* RFC 1323 variables */ + uint8_t snd_scale; /* window scaling for send window */ + uint8_t rcv_scale; /* window scaling for recv window */ + uint8_t request_r_scale; /* pending window scaling */ + uint8_t requested_s_scale; + uint32_t ts_recent; /* timestamp echo data */ + uint32_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) + +#endif diff --git a/slirp/tcpip.h b/libslirp/src/tcpip.h similarity index 52% rename from slirp/tcpip.h rename to libslirp/src/tcpip.h index eb31a6acc..e9c794bd0 100644 --- a/slirp/tcpip.h +++ b/libslirp/src/tcpip.h @@ -1,77 +1,105 @@ -/* - * 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_mbuf ti_i.ih_mbuf.mptr -#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 - -#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink))) -#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink))) -#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next) -#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev) -#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next) -#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T)) -#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T)) - -/* - * Just a clean way to get to the first byte - * of the packet - */ -struct tcpiphdr_2 { - struct tcpiphdr dummy; - char first_char; -}; - -#endif +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 mbuf_ptr ih_mbuf; /* backpointer to mbuf */ + union { + struct { + struct in_addr ih_src; /* source internet address */ + struct in_addr ih_dst; /* destination internet address */ + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + } ti_i4; + struct { + struct in6_addr ih_src; + struct in6_addr ih_dst; + uint8_t ih_x1; + uint8_t ih_nh; + } ti_i6; + } ti; + uint16_t ti_x0; + uint16_t ti_len; /* protocol length */ + struct tcphdr ti_t; /* tcp header */ +}; +#define ti_mbuf ih_mbuf.mptr +#define ti_pr ti.ti_i4.ih_pr +#define ti_src ti.ti_i4.ih_src +#define ti_dst ti.ti_i4.ih_dst +#define ti_src6 ti.ti_i6.ih_src +#define ti_dst6 ti.ti_i6.ih_dst +#define ti_nh6 ti.ti_i6.ih_nh +#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 + +#define tcpiphdr2qlink(T) \ + ((struct qlink *)(((char *)(T)) - sizeof(struct qlink))) +#define qlink2tcpiphdr(Q) \ + ((struct tcpiphdr *)(((char *)(Q)) + sizeof(struct qlink))) +#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next) +#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev) +#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next) +#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink *)(T)) +#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr *)(T)) + +/* This is the difference between the size of a tcpiphdr structure, and the + * size of actual ip+tcp headers, rounded up since we need to align data. */ +#define TCPIPHDR_DELTA \ + (MAX(0, ((int) sizeof(struct qlink) + \ + (int) sizeof(struct tcpiphdr) - (int) sizeof(struct ip) - \ + (int) sizeof(struct tcphdr) + 7) & \ + ~7)) + +/* + * 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/libslirp/src/tftp.c b/libslirp/src/tftp.c new file mode 100644 index 000000000..67f0b9735 --- /dev/null +++ b/libslirp/src/tftp.c @@ -0,0 +1,498 @@ +/* SPDX-License-Identifier: MIT */ +/* + * 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" + +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#endif + +static inline int tftp_session_in_use(struct tftp_session *spt) +{ + return (spt->slirp != NULL); +} + +static inline void tftp_session_update(struct tftp_session *spt) +{ + spt->timestamp = curtime; +} + +static void tftp_session_terminate(struct tftp_session *spt) +{ + if (spt->fd >= 0) { + slirp_os_close(spt->fd); + spt->fd = -1; + } + g_free(spt->filename); + spt->slirp = NULL; +} + +static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftphdr *hdr) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &slirp->tftp_sessions[k]; + + if (!tftp_session_in_use(spt)) + goto found; + + /* sessions time out after 5 inactive seconds */ + if ((int)(curtime - spt->timestamp) > 5000) { + tftp_session_terminate(spt); + goto found; + } + } + + return -1; + +found: + memset(spt, 0, sizeof(*spt)); + memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas)); + spt->fd = -1; + spt->block_size = 512; + spt->client_port = hdr->udp.uh_sport; + spt->slirp = slirp; + + tftp_session_update(spt); + + return k; +} + +static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftphdr *hdr) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &slirp->tftp_sessions[k]; + + if (tftp_session_in_use(spt)) { + if (sockaddr_equal(&spt->client_addr, srcsas)) { + if (spt->client_port == hdr->udp.uh_sport) { + return k; + } + } + } + } + + return -1; +} + +void tftp_cleanup(Slirp *slirp) +{ + struct tftp_session *spt; + int k; + + for (k = 0; k < TFTP_SESSIONS_MAX; k++) { + spt = &slirp->tftp_sessions[k]; + + if (tftp_session_in_use(spt)) { + tftp_session_terminate(spt); + } + } +} + +static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr, + uint8_t *buf, int len) +{ + int bytes_read = 0; + int open_ret = -1; + + if (spt->fd < 0) { +#if !defined(HAVE__SOPEN_S) + spt->fd = open(spt->filename, O_RDONLY | O_BINARY); + open_ret = spt->fd; +#else + /* MS extension. Opens the file readonly, binary mode, others can't write to it, + * and if it gets created (dubiously), it's readonly. */ + open_ret = _sopen_s(&spt->fd, spt->filename, _O_RDONLY | _O_BINARY, _SH_DENYWR, + _S_IREAD); +#endif + } + + if (open_ret < 0) { + return -1; + } + + if (len) { + if (slirp_os_lseek(spt->fd, block_nr * spt->block_size, SEEK_SET) == (off_t)-1) { + return -1; + } + + bytes_read = slirp_os_read(spt->fd, buf, len); + } + + return bytes_read; +} + +static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt, + struct mbuf *m) +{ + struct tftp_t *tp; + + memset(m->m_data, 0, m->m_size); + + m->m_data += IF_MAXLINKHDR; + if (spt->client_addr.ss_family == AF_INET6) { + m->m_data += sizeof(struct ip6); + } else { + m->m_data += sizeof(struct ip); + } + tp = (void *)m->m_data; + m->m_data += sizeof(struct udphdr); + + return tp; +} + +static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m, + struct tftphdr *hdr) +{ + if (spt->client_addr.ss_family == AF_INET6) { + struct sockaddr_in6 sa6, da6; + + sa6.sin6_addr = spt->slirp->vhost_addr6; + sa6.sin6_port = hdr->udp.uh_dport; + da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr; + da6.sin6_port = spt->client_port; + + udp6_output(NULL, m, &sa6, &da6); + } else { + struct sockaddr_in sa4, da4; + + sa4.sin_addr = spt->slirp->vhost_addr; + sa4.sin_port = hdr->udp.uh_dport; + da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr; + da4.sin_port = spt->client_port; + + udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY); + } +} + +static int tftp_send_oack(struct tftp_session *spt, const char *keys[], + uint32_t values[], int nb, struct tftp_t *recv_tp) +{ + struct mbuf *m; + struct tftp_t *tp; + int i, n = 0; + + m = m_get(spt->slirp); + + if (!m) + return -1; + + tp = tftp_prep_mbuf_data(spt, m); + + tp->hdr.tp_op = htons(TFTP_OACK); + for (i = 0; i < nb; i++) { + n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", keys[i]); + n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", values[i]); + } + + m->m_len = G_SIZEOF_MEMBER(struct tftp_t, hdr.tp_op) + n; + tftp_udp_output(spt, m, &recv_tp->hdr); + + return 0; +} + +static void tftp_send_error(struct tftp_session *spt, uint16_t errorcode, + const char *msg, struct tftp_t *recv_tp) +{ + struct mbuf *m; + struct tftp_t *tp; + + DEBUG_TFTP("tftp error msg: %s", msg); + + m = m_get(spt->slirp); + + if (!m) { + goto out; + } + + tp = tftp_prep_mbuf_data(spt, m); + + tp->hdr.tp_op = htons(TFTP_ERROR); + tp->x.tp_error.tp_error_code = htons(errorcode); + slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), + msg); + + m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 + + strlen(msg) - sizeof(struct udphdr); + tftp_udp_output(spt, m, &recv_tp->hdr); + +out: + tftp_session_terminate(spt); +} + +static void tftp_send_next_block(struct tftp_session *spt, + struct tftphdr *hdr) +{ + struct mbuf *m; + struct tftp_t *tp; + int nobytes; + + m = m_get(spt->slirp); + + if (!m) { + return; + } + + tp = tftp_prep_mbuf_data(spt, m); + + tp->hdr.tp_op = htons(TFTP_DATA); + tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff); + + nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, + spt->block_size); + + if (nobytes < 0) { + /* send "file not found" error back */ + + tftp_send_error(spt, 1, "File not found", tp); + + m_free(m); + + return; + } + + m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes) - + sizeof(struct udphdr); + tftp_udp_output(spt, m, hdr); + + if (nobytes == spt->block_size) { + tftp_session_update(spt); + } else { + tftp_session_terminate(spt); + } + + spt->block_nr++; +} + +static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftp_t *tp, int pktlen) +{ + struct tftp_session *spt; + int s, k; + size_t prefix_len; + char *req_fname; + const char *option_name[2]; + uint32_t option_value[2]; + int nb_options = 0; + + /* check if a session already exists and if so terminate it */ + s = tftp_session_find(slirp, srcsas, &tp->hdr); + if (have_valid_socket(s)) { + tftp_session_terminate(&slirp->tftp_sessions[s]); + } + + s = tftp_session_allocate(slirp, srcsas, &tp->hdr); + + if (not_valid_socket(s)) { + return; + } + + spt = &slirp->tftp_sessions[s]; + + /* unspecified prefix means service disabled */ + if (!slirp->tftp_prefix) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + /* skip header fields */ + k = 0; + pktlen -= offsetof(struct tftp_t, x.tp_buf); + + /* prepend tftp_prefix */ + prefix_len = strlen(slirp->tftp_prefix); + spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2); + memcpy(spt->filename, slirp->tftp_prefix, prefix_len); + spt->filename[prefix_len] = '/'; + + /* get name */ + req_fname = spt->filename + prefix_len + 1; + + while (1) { + if (k >= TFTP_FILENAME_MAX || k >= pktlen) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + req_fname[k] = tp->x.tp_buf[k]; + if (req_fname[k++] == '\0') { + break; + } + } + + DEBUG_TFTP("tftp rrq file: %s", req_fname); + + /* check mode */ + if ((pktlen - k) < 6) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + if (g_ascii_strcasecmp(&tp->x.tp_buf[k], "octet") != 0) { + tftp_send_error(spt, 4, "Unsupported transfer mode", tp); + return; + } + + k += 6; /* skipping octet */ + + /* do sanity checks on the filename */ + if ( +#ifdef G_OS_WIN32 + strstr(req_fname, "..\\") || + req_fname[strlen(req_fname) - 1] == '\\' || +#endif + strstr(req_fname, "../") || + req_fname[strlen(req_fname) - 1] == '/') { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + /* check if the file exists */ + if (tftp_read_data(spt, 0, NULL, 0) < 0) { + tftp_send_error(spt, 1, "File not found", tp); + return; + } + + if (tp->x.tp_buf[pktlen - 1] != 0) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + while (k < pktlen && nb_options < G_N_ELEMENTS(option_name)) { + const char *key, *value; + + key = &tp->x.tp_buf[k]; + k += strlen(key) + 1; + + if (k >= pktlen) { + tftp_send_error(spt, 2, "Access violation", tp); + return; + } + + value = &tp->x.tp_buf[k]; + k += strlen(value) + 1; + + if (g_ascii_strcasecmp(key, "tsize") == 0) { + int tsize = atoi(value); + struct stat stat_p; + + if (tsize == 0) { + if (stat(spt->filename, &stat_p) == 0) + tsize = stat_p.st_size; + else { + tftp_send_error(spt, 1, "File not found", tp); + return; + } + } + + option_name[nb_options] = "tsize"; + option_value[nb_options] = tsize; + nb_options++; + } else if (g_ascii_strcasecmp(key, "blksize") == 0) { + int blksize = atoi(value); + + /* Accept blksize up to our maximum size */ + if (blksize > 0) { + spt->block_size = MIN(blksize, TFTP_BLOCKSIZE_MAX); + option_name[nb_options] = "blksize"; + option_value[nb_options] = spt->block_size; + nb_options++; + } + } + } + + if (nb_options > 0) { + assert(nb_options <= G_N_ELEMENTS(option_name)); + tftp_send_oack(spt, option_name, option_value, nb_options, tp); + return; + } + + spt->block_nr = 0; + tftp_send_next_block(spt, &tp->hdr); +} + +static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftphdr *hdr) +{ + int s; + + s = tftp_session_find(slirp, srcsas, hdr); + + if (not_valid_socket(s)) { + return; + } + + tftp_send_next_block(&slirp->tftp_sessions[s], hdr); +} + +static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas, + struct tftphdr *hdr) +{ + int s; + + s = tftp_session_find(slirp, srcsas, hdr); + + if (not_valid_socket(s)) { + return; + } + + tftp_session_terminate(&slirp->tftp_sessions[s]); +} + +void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m) +{ + struct tftphdr *hdr = mtod_check(m, sizeof(struct tftphdr)); + + if (hdr == NULL) { + return; + } + + switch (ntohs(hdr->tp_op)) { + case TFTP_RRQ: + tftp_handle_rrq(m->slirp, srcsas, + mtod(m, struct tftp_t *), + m->m_len); + break; + + case TFTP_ACK: + tftp_handle_ack(m->slirp, srcsas, hdr); + break; + + case TFTP_ERROR: + tftp_handle_error(m->slirp, srcsas, hdr); + break; + } +} diff --git a/libslirp/src/tftp.h b/libslirp/src/tftp.h new file mode 100644 index 000000000..263c540ab --- /dev/null +++ b/libslirp/src/tftp.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* tftp defines */ + +#ifndef SLIRP_TFTP_H +#define SLIRP_TFTP_H + +#include "util.h" + +#define TFTP_SESSIONS_MAX 20 + +#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_OACK 6 + +#define TFTP_FILENAME_MAX 512 +#define TFTP_BLOCKSIZE_MAX 1428 + +SLIRP_PACKED_BEGIN +struct tftphdr { + struct udphdr udp; + uint16_t tp_op; +} SLIRP_PACKED_END; + +SLIRP_PACKED_BEGIN +struct tftp_t { + struct tftphdr hdr; + union { + struct { + uint16_t tp_block_nr; + uint8_t tp_buf[TFTP_BLOCKSIZE_MAX]; + } tp_data; + struct { + uint16_t tp_error_code; + uint8_t tp_msg[TFTP_BLOCKSIZE_MAX]; + } tp_error; + char tp_buf[TFTP_BLOCKSIZE_MAX + 2]; + } x; +} SLIRP_PACKED_END; + +struct tftp_session { + Slirp *slirp; + char *filename; + int fd; + uint16_t block_size; + + struct sockaddr_storage client_addr; + uint16_t client_port; + uint32_t block_nr; + + int timestamp; +}; + +/* Process TFTP packet coming from the guest */ +void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m); + +/* Clear remaining sessions */ +void tftp_cleanup(Slirp *slirp); + +#endif diff --git a/libslirp/src/udp.c b/libslirp/src/udp.c new file mode 100644 index 000000000..eb20bbb9b --- /dev/null +++ b/libslirp/src/udp.c @@ -0,0 +1,428 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 "slirp.h" +#include "ip_icmp.h" + +static uint8_t udp_tos(struct socket *so); + +void udp_init(Slirp *slirp) +{ + slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb; + slirp->udp_last_so = &slirp->udb; +} + +void udp_cleanup(Slirp *slirp) +{ + struct socket *so, *so_next; + + for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { + so_next = so->so_next; + udp_detach(so); + } +} + +/* m->m_data points at ip packet header + * m->m_len length ip packet + * ip->ip_len length data (IPDU) + */ +void udp_input(register struct mbuf *m, int iphlen) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, 0); + + register struct ip *ip; + register struct udphdr *uh; + int len; + struct ip save_ip; + struct socket *so; + struct sockaddr_storage lhost; + struct sockaddr_in *lhost4; + int ttl; + + DEBUG_CALL("udp_input"); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("iphlen = %d", iphlen); + + /* + * 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); + iphlen = sizeof(struct ip); + } + + /* + * Get IP and UDP header together in first mbuf. + */ + ip = mtod_check(m, iphlen + sizeof(struct udphdr)); + if (ip == NULL) { + goto bad; + } + uh = (struct udphdr *)((char *)ip + iphlen); + + /* + * Make mbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + len = ntohs((uint16_t)uh->uh_ulen); + + if (ip->ip_len != len) { + if (len > ip->ip_len) { + 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 (uh->uh_sum) { + memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr)); + ((struct ipovly *)ip)->ih_x1 = 0; + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + if (cksum(m, len + sizeof(struct ip))) { + goto bad; + } + } + + lhost.ss_family = AF_INET; + lhost4 = (struct sockaddr_in *)&lhost; + lhost4->sin_addr = ip->ip_src; + lhost4->sin_port = uh->uh_sport; + + /* + * handle DHCP/BOOTP + */ + if (ntohs(uh->uh_dport) == BOOTP_SERVER && + (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || + ip->ip_dst.s_addr == 0xffffffff)) { + bootp_input(m); + goto bad; + } + + /* + * handle TFTP + */ + if (ntohs(uh->uh_dport) == TFTP_SERVER && + ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { + m->m_data += iphlen; + m->m_len -= iphlen; + tftp_input(&lhost, m); + m->m_data -= iphlen; + m->m_len += iphlen; + goto bad; + } + + if (slirp->restricted) { + goto bad; + } + + /* + * Locate pcb for datagram. + */ + so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL); + + if (so == NULL) { + /* + * If there's no socket for this packet, + * create one + */ + so = socreate(slirp, IPPROTO_UDP); + if (not_valid_socket(udp_attach(so, AF_INET))) { + DEBUG_MISC(" udp_attach errno = %d-%s", errno, g_strerror(errno)); + sofree(so); + goto bad; + } + + /* + * Setup fields + */ + so->so_lfamily = AF_INET; + 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_ffamily = AF_INET; + 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; + + /* + * Check for TTL + */ + ttl = save_ip.ip_ttl-1; + if (ttl <= 0) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC("udp ttl exceeded"); + icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, NULL); + goto bad; + } + setsockopt(so->s, IPPROTO_IP, IP_TTL, (const void *) &ttl, sizeof(ttl)); + + /* + * Now we sendto() the packet. + */ + if (sosendto(so, m) == -1) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC("udp tx errno = %d-%s", errno, g_strerror(errno)); + icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, g_strerror(errno)); + goto bad; + } + + m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ + + /* restore the orig mbuf packet */ + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + so->so_m = m; /* ICMP backup */ + + return; +bad: + m_free(m); +} + +int udp_output(struct socket *so, struct mbuf *m, const struct sockaddr_in *saddr, + const struct sockaddr_in *daddr, int iptos) +{ + Slirp *slirp = m->slirp; + char addr[INET_ADDRSTRLEN]; + + M_DUP_DEBUG(slirp, m, 0, sizeof(struct udpiphdr)); + + register struct udpiphdr *ui; + int error = 0; + + DEBUG_CALL("udp_output"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); + DEBUG_ARG("saddr = %s", inet_ntop(AF_INET, &saddr->sin_addr, addr, sizeof(addr))); + DEBUG_ARG("daddr = %s", inet_ntop(AF_INET, &daddr->sin_addr, addr, sizeof(addr))); + + /* + * Adjust for header + */ + m->m_data -= sizeof(struct udpiphdr); + m->m_len += sizeof(struct udpiphdr); + + /* + * Fill in mbuf with extended UDP header + * and addresses and length put into network format. + */ + ui = mtod(m, struct udpiphdr *); + memset(&ui->ui_i.ih_mbuf, 0, sizeof(struct mbuf_ptr)); + ui->ui_x1 = 0; + ui->ui_pr = IPPROTO_UDP; + ui->ui_len = htons(m->m_len - sizeof(struct ip)); + /* 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 ((ui->ui_sum = cksum(m, m->m_len)) == 0) + ui->ui_sum = 0xffff; + ((struct ip *)ui)->ip_len = m->m_len; + + ((struct ip *)ui)->ip_ttl = IPDEFTTL; + ((struct ip *)ui)->ip_tos = iptos; + + error = ip_output(so, m); + + return (error); +} + +slirp_os_socket udp_attach(struct socket *so, unsigned short af) +{ + so->s = slirp_socket(af, SOCK_DGRAM, 0); + if (have_valid_socket(so->s)) { + if (slirp_bind_outbound(so, af) != 0) { + // bind failed - close socket + closesocket(so->s); + so->s = SLIRP_INVALID_SOCKET; + return SLIRP_INVALID_SOCKET; + } + +#ifdef __linux__ + { + int opt = 1; + switch (af) { + case AF_INET: + setsockopt(so->s, IPPROTO_IP, IP_RECVERR, &opt, sizeof(opt)); + break; + case AF_INET6: + setsockopt(so->s, IPPROTO_IPV6, IPV6_RECVERR, &opt, sizeof(opt)); + break; + default: + g_assert_not_reached(); + } + } +#endif + + so->so_expire = curtime + SO_EXPIRE; + slirp_insque(so, &so->slirp->udb); + } + slirp_register_poll_socket(so); + return (so->s); +} + +void udp_detach(struct socket *so) +{ + slirp_unregister_poll_socket(so); + closesocket(so->s); + sofree(so); +} + +static const struct tos_t udptos[] = { { 0, 53, IPTOS_LOWDELAY, 0 }, /* DNS */ + { 0, 0, 0, 0 } }; + +static uint8_t udp_tos(struct socket *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)) { + if (so->slirp->enable_emu) + so->so_emu = udptos[i].emu; + return udptos[i].tos; + } + i++; + } + + return 0; +} + +struct socket *udpx_listen(Slirp *slirp, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *laddr, socklen_t laddrlen, + int flags) +{ + struct socket *so; + socklen_t addrlen; + int save_errno; + + so = socreate(slirp, IPPROTO_UDP); + so->s = slirp_socket(haddr->sa_family, SOCK_DGRAM, 0); + if (not_valid_socket(so->s)) { + save_errno = errno; + sofree(so); + errno = save_errno; + return NULL; + } + if (haddr->sa_family == AF_INET6) + slirp_socket_set_v6only(so->s, (flags & SS_HOSTFWD_V6ONLY) != 0); + so->so_expire = curtime + SO_EXPIRE; + slirp_insque(so, &slirp->udb); + + if (bind(so->s, haddr, haddrlen) < 0) { + save_errno = errno; + udp_detach(so); + errno = save_errno; + return NULL; + } + slirp_socket_set_fast_reuse(so->s); + + addrlen = sizeof(so->fhost); + getsockname(so->s, &so->fhost.sa, &addrlen); + sotranslate_accept(so); + + sockaddr_copy(&so->lhost.sa, sizeof(so->lhost), laddr, laddrlen); + + if (flags != SS_FACCEPTONCE) + so->so_expire = 0; + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_ISFCONNECTED | flags; + slirp_register_poll_socket(so); + + return so; +} + +struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, + uint32_t laddr, unsigned lport, int flags) +{ + struct sockaddr_in hsa, lsa; + + memset(&hsa, 0, sizeof(hsa)); + hsa.sin_family = AF_INET; + hsa.sin_addr.s_addr = haddr; + hsa.sin_port = hport; + + memset(&lsa, 0, sizeof(lsa)); + lsa.sin_family = AF_INET; + lsa.sin_addr.s_addr = laddr; + lsa.sin_port = lport; + + return udpx_listen(slirp, (const struct sockaddr *) &hsa, sizeof(hsa), (struct sockaddr *) &lsa, sizeof(lsa), flags); +} diff --git a/libslirp/src/udp.h b/libslirp/src/udp.h new file mode 100644 index 000000000..6604db5cf --- /dev/null +++ b/libslirp/src/udp.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * 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 + +#include "socket.h" + +#define UDP_TTL 0x60 + +/* + * Udp protocol header. + * Per RFC 768, September, 1981. + */ +struct udphdr { + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + int16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ +}; + +/* + * UDP kernel structures and variables. + */ +struct udpiphdr { + struct ipovly ui_i; /* overlaid ip structure */ + struct udphdr ui_u; /* udp header */ +}; +#define ui_mbuf ui_i.ih_mbuf.mptr +#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 + +/* + * Names for UDP sysctl objects + */ +#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ +#define UDPCTL_MAXID 2 + +struct mbuf; + +/* Called from slirp_init */ +void udp_init(Slirp *); +/* Called from slirp_cleanup */ +void udp_cleanup(Slirp *); +/* Process UDP datagram coming from the guest */ +void udp_input(register struct mbuf *, int); +/* Create a host UDP socket, bound to this socket */ +slirp_os_socket udp_attach(struct socket *, unsigned short af); +/* Destroy socket */ +void udp_detach(struct socket *); + +/* Listen for incoming UDP datagrams on this haddr+hport */ +struct socket *udp_listen(Slirp *, uint32_t haddr, unsigned hport, uint32_t laddr, unsigned lport, int flags); +/* Listen for incoming UDP datagrams on this haddr */ +struct socket *udpx_listen(Slirp *, + const struct sockaddr *haddr, socklen_t haddrlen, + const struct sockaddr *laddr, socklen_t laddrlen, + int flags); +/* Send UDP datagram to the guest */ +int udp_output(struct socket *so, struct mbuf *m, const struct sockaddr_in *saddr, + const struct sockaddr_in *daddr, int iptos); + +/* Process UDPv6 datagram coming from the guest */ +void udp6_input(register struct mbuf *); +/* Send UDPv6 datagram to the guest */ +int udp6_output(struct socket *so, struct mbuf *m, const struct sockaddr_in6 *saddr, + const struct sockaddr_in6 *daddr); + +#endif diff --git a/libslirp/src/udp6.c b/libslirp/src/udp6.c new file mode 100644 index 000000000..f38c9bdb1 --- /dev/null +++ b/libslirp/src/udp6.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2013 + * Guillaume Subiron + */ + +#include "slirp.h" +#include "udp.h" +#include "dhcpv6.h" + +void udp6_input(struct mbuf *m) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, 0); + + struct ip6 *ip, save_ip; + struct udphdr *uh; + int iphlen = sizeof(struct ip6); + int len; + struct socket *so; + struct sockaddr_in6 lhost; + int hop_limit; + + DEBUG_CALL("udp6_input"); + DEBUG_ARG("m = %p", m); + + if (slirp->restricted) { + goto bad; + } + + ip = mtod(m, struct ip6 *); + m->m_len -= iphlen; + m->m_data += iphlen; + uh = mtod_check(m, sizeof(struct udphdr)); + if (uh == NULL) { + goto bad; + } + m->m_len += iphlen; + m->m_data -= iphlen; + + if (ip6_cksum(m)) { + goto bad; + } + + len = ntohs((uint16_t)uh->uh_ulen); + + /* + * Make mbuf data length reflect UDP length. + * If not enough data to reflect UDP length, drop. + */ + if (ntohs(ip->ip_pl) != len) { + if (len > ntohs(ip->ip_pl)) { + goto bad; + } + m_adj(m, len - ntohs(ip->ip_pl)); + ip->ip_pl = htons(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; + + /* Locate pcb for datagram. */ + lhost.sin6_family = AF_INET6; + lhost.sin6_addr = ip->ip_src; + lhost.sin6_port = uh->uh_sport; + + /* handle DHCPv6 */ + if (ntohs(uh->uh_dport) == DHCPV6_SERVER_PORT && + (in6_equal(&ip->ip_dst, &slirp->vhost_addr6) || + in6_dhcp_multicast(&ip->ip_dst))) { + m->m_data += iphlen; + m->m_len -= iphlen; + dhcpv6_input(&lhost, m); + m->m_data -= iphlen; + m->m_len += iphlen; + goto bad; + } + + /* handle TFTP */ + if (ntohs(uh->uh_dport) == TFTP_SERVER && + !memcmp(ip->ip_dst.s6_addr, slirp->vhost_addr6.s6_addr, 16)) { + m->m_data += iphlen; + m->m_len -= iphlen; + tftp_input((struct sockaddr_storage *)&lhost, m); + m->m_data -= iphlen; + m->m_len += iphlen; + goto bad; + } + + so = solookup(&slirp->udp_last_so, &slirp->udb, + (struct sockaddr_storage *)&lhost, NULL); + + if (so == NULL) { + /* If there's no socket for this packet, create one. */ + so = socreate(slirp, IPPROTO_UDP); + if (not_valid_socket(udp_attach(so, AF_INET6))) { + DEBUG_MISC(" udp6_attach errno = %d-%s", errno, g_strerror(errno)); + sofree(so); + goto bad; + } + + /* Setup fields */ + so->so_lfamily = AF_INET6; + so->so_laddr6 = ip->ip_src; + so->so_lport6 = uh->uh_sport; + } + + so->so_ffamily = AF_INET6; + so->so_faddr6 = ip->ip_dst; /* XXX */ + so->so_fport6 = uh->uh_dport; /* XXX */ + + iphlen += sizeof(struct udphdr); + m->m_len -= iphlen; + m->m_data += iphlen; + + /* + * Check for TTL + */ + hop_limit = save_ip.ip_hl-1; + if (hop_limit <= 0) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC("udp ttl exceeded"); + icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS); + goto bad; + } + setsockopt(so->s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const void *) &hop_limit, sizeof(hop_limit)); + + /* + * Now we sendto() the packet. + */ + if (sosendto(so, m) == -1) { + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + DEBUG_MISC("udp tx errno = %d-%s", errno, g_strerror(errno)); + icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); + goto bad; + } + + m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ + + /* restore the orig mbuf packet */ + m->m_len += iphlen; + m->m_data -= iphlen; + *ip = save_ip; + so->so_m = m; + + return; +bad: + m_free(m); +} + +int udp6_output(struct socket *so, struct mbuf *m, const struct sockaddr_in6 *saddr, + const struct sockaddr_in6 *daddr) +{ + Slirp *slirp = m->slirp; + M_DUP_DEBUG(slirp, m, 0, sizeof(struct ip6) + sizeof(struct udphdr)); + + struct ip6 *ip; + struct udphdr *uh; + + DEBUG_CALL("udp6_output"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); + + /* adjust for header */ + m->m_data -= sizeof(struct udphdr); + m->m_len += sizeof(struct udphdr); + uh = mtod(m, struct udphdr *); + m->m_data -= sizeof(struct ip6); + m->m_len += sizeof(struct ip6); + ip = mtod(m, struct ip6 *); + + /* Build IP header */ + ip->ip_pl = htons(m->m_len - sizeof(struct ip6)); + ip->ip_nh = IPPROTO_UDP; + ip->ip_src = saddr->sin6_addr; + ip->ip_dst = daddr->sin6_addr; + + /* Build UDP header */ + uh->uh_sport = saddr->sin6_port; + uh->uh_dport = daddr->sin6_port; + uh->uh_ulen = ip->ip_pl; + uh->uh_sum = 0; + uh->uh_sum = ip6_cksum(m); + if (uh->uh_sum == 0) { + uh->uh_sum = 0xffff; + } + + return ip6_output(so, m, 0); +} diff --git a/libslirp/src/util.c b/libslirp/src/util.c new file mode 100644 index 000000000..4e3f52a1d --- /dev/null +++ b/libslirp/src/util.c @@ -0,0 +1,558 @@ +/* SPDX-License-Identifier: MIT */ +/* + * util.c (mostly based on QEMU os-win32.c) + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010-2016 Red Hat, Inc. + * + * QEMU library functions for win32 which are shared between QEMU and + * the QEMU tools. + * + * 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. + */ + +#if defined(_WIN32) +/* slirp.h is not included here. However, if TARGET_WINVER is set, + * ensure that WINVER and _WIN32_WINNT are also properly set. + */ +# if defined(TARGET_WINVER) + /* TARGET_WINVER defined on the compiler command line? */ + +# undef WINVER +# undef _WIN32_WINNT +# define WINVER TARGET_WINVER +# define _WIN32_WINNT TARGET_WINVER + +# elif !defined(WINVER) + /* Default WINVER to Windows 7 API, same as glib. */ +# undef _WIN32_WINNT + +# define WINVER 0x0601 +# define _WIN32_WINNT WINVER + +# endif +#endif + +/* Ensure that _WIN32_WINNT matches WINVER */ +#if defined(WINVER) && !defined(_WIN32_WINNT) +#define _WIN32_WINNT WINVER +#endif + + +#include +#include +#include +/* Windows: If errno.h is not included, then errno is a preprocessor define + * for WSAGetLastError(). errno.h redefines errno as a thread safe function, + * i.e. "*errno()", which allows us to assign errno a value. */ +#include + +#include "util.h" +#include "debug.h" + +#if defined(_WIN32) + +int slirp_inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr; + int valid_addr; + +#if WINVER >= 0x0601 + valid_addr = inet_pton(AF_INET, cp, &addr) > 0; +#else + addr = inet_addr(cp); + valid_addr = (addr != 0xffffffff); +#endif + + if (valid_addr) { + ia->s_addr = addr; + return 1; + } + + /* Invalid address. */ + return 0; +} + +#if WINVER < 0x0601 +/* Something older than Windows 7 and TARGET_WINVER obviously was set. There + * are more than a few calls to inet_pton() and inet_ntop(), so provide suitable + * stubs with renames. */ + +int slirp_inet_pton(int af, const char *src, void *dst) +{ + struct sockaddr_storage ss; + int size = (int) sizeof(ss); + char src_copy[INET6_ADDRSTRLEN + 1]; + + ZeroMemory(&ss, sizeof(ss)); + strncpy (src_copy, src, INET6_ADDRSTRLEN); + src_copy[INET6_ADDRSTRLEN] = '\0'; + + if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { + switch(af) { + case AF_INET: + *((struct in_addr *) dst) = ((const struct sockaddr_in *) &ss)->sin_addr; + return 1; + case AF_INET6: + *((struct in6_addr *) dst) = ((const struct sockaddr_in6 *) &ss)->sin6_addr; + return 1; + } + } + + return 0; +} + +const char *slirp_inet_ntop(int af, const void *src, char *dst, socklen_t size) +{ + struct sockaddr_storage ss; + DWORD s = (DWORD) size; + + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = af; + + switch(af) { + case AF_INET: + ((struct sockaddr_in *) &ss)->sin_addr = *((const struct in_addr *) src); + break; + case AF_INET6: + ((struct sockaddr_in6 *) &ss)->sin6_addr = *((const struct in6_addr *) src); + break; + default: + return NULL; + } + + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL; +} +#endif +#endif + + +void slirp_set_nonblock(slirp_os_socket fd) +{ +#if !defined(_WIN32) + int f; + f = fcntl(fd, F_GETFL); + assert(f != -1); + f = fcntl(fd, F_SETFL, f | O_NONBLOCK); + assert(f != -1); +#else + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +#endif +} + +static void slirp_set_cloexec(slirp_os_socket fd) +{ +#if !defined(_WIN32) + int f; + f = fcntl(fd, F_GETFD); + assert(f != -1); + f = fcntl(fd, F_SETFD, f | FD_CLOEXEC); + assert(f != -1); +#else +#ifdef GLIB_UNUSED_PARAM + GLIB_UNUSED_PARAM(fd); +#endif +#endif +} + +/* + * Opens a socket with FD_CLOEXEC set + * On failure errno contains the reason. + */ +slirp_os_socket slirp_socket(int domain, int type, int protocol) +{ + slirp_os_socket ret; + +#ifdef SOCK_CLOEXEC + ret = socket(domain, type | SOCK_CLOEXEC, protocol); + if (ret != -1 || errno != EINVAL) { + return ret; + } +#endif + ret = socket(domain, type, protocol); + if (have_valid_socket(ret)) { + slirp_set_cloexec(ret); + } + + return ret; +} + +#if defined(_WIN32) +static int win32_socket_error(void) +{ + switch (WSAGetLastError()) { + case 0: + return 0; + case WSAEINTR: + return EINTR; + case WSAEINVAL: + return EINVAL; + case WSA_INVALID_HANDLE: + return EBADF; + case WSA_NOT_ENOUGH_MEMORY: + return ENOMEM; + case WSA_INVALID_PARAMETER: + return EINVAL; + case WSAENAMETOOLONG: + return ENAMETOOLONG; + case WSAENOTEMPTY: + return ENOTEMPTY; + case WSAEWOULDBLOCK: + /* not using EWOULDBLOCK as we don't want code to have + * to check both EWOULDBLOCK and EAGAIN */ + return EAGAIN; + case WSAEINPROGRESS: + return EINPROGRESS; + case WSAEALREADY: + return EALREADY; + case WSAENOTSOCK: + return ENOTSOCK; + case WSAEDESTADDRREQ: + return EDESTADDRREQ; + case WSAEMSGSIZE: + return EMSGSIZE; + case WSAEPROTOTYPE: + return EPROTOTYPE; + case WSAENOPROTOOPT: + return ENOPROTOOPT; + case WSAEPROTONOSUPPORT: + return EPROTONOSUPPORT; + case WSAEOPNOTSUPP: + return EOPNOTSUPP; + case WSAEAFNOSUPPORT: + return EAFNOSUPPORT; + case WSAEADDRINUSE: + return EADDRINUSE; + case WSAEADDRNOTAVAIL: + return EADDRNOTAVAIL; + case WSAENETDOWN: + return ENETDOWN; + case WSAENETUNREACH: + return ENETUNREACH; + case WSAENETRESET: + return ENETRESET; + case WSAECONNABORTED: + return ECONNABORTED; + case WSAECONNRESET: + return ECONNRESET; + case WSAENOBUFS: + return ENOBUFS; + case WSAEISCONN: + return EISCONN; + case WSAENOTCONN: + return ENOTCONN; + case WSAETIMEDOUT: + return ETIMEDOUT; + case WSAECONNREFUSED: + return ECONNREFUSED; + case WSAELOOP: + return ELOOP; + case WSAEHOSTUNREACH: + return EHOSTUNREACH; + default: + return EIO; + } +} + +#undef ioctlsocket +int slirp_ioctlsocket_wrap(slirp_os_socket fd, int req, void *val) +{ + int ret; + ret = ioctlsocket(fd, req, val); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef closesocket +int slirp_closesocket_wrap(slirp_os_socket fd) +{ + int ret; + ret = closesocket(fd); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef connect +int slirp_connect_wrap(slirp_os_socket sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = connect(sockfd, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef listen +int slirp_listen_wrap(slirp_os_socket sockfd, int backlog) +{ + int ret; + ret = listen(sockfd, backlog); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef bind +int slirp_bind_wrap(slirp_os_socket sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = bind(sockfd, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef socket +slirp_os_socket slirp_socket_wrap(int domain, int type, int protocol) +{ + slirp_os_socket ret; + ret = socket(domain, type, protocol); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef accept +slirp_os_socket slirp_accept_wrap(slirp_os_socket sockfd, struct sockaddr *addr, int *addrlen) +{ + slirp_os_socket ret; + ret = accept(sockfd, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef shutdown +int slirp_shutdown_wrap(slirp_os_socket sockfd, int how) +{ + int ret; + ret = shutdown(sockfd, how); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef getsockopt +int slirp_getsockopt_wrap(slirp_os_socket sockfd, int level, int optname, void *optval, + int *optlen) +{ + int ret; + ret = getsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef setsockopt +int slirp_setsockopt_wrap(slirp_os_socket sockfd, int level, int optname, + const void *optval, int optlen) +{ + int ret; + ret = setsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef getpeername +int slirp_getpeername_wrap(slirp_os_socket sockfd, struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = getpeername(sockfd, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef getsockname +int slirp_getsockname_wrap(slirp_os_socket sockfd, struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = getsockname(sockfd, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef send +slirp_ssize_t slirp_send_wrap(slirp_os_socket sockfd, const void *buf, size_t len, int flags) +{ + int ret; + + ret = send(sockfd, buf, len, flags); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef sendto +slirp_ssize_t slirp_sendto_wrap(slirp_os_socket sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = sendto(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef recv +slirp_ssize_t slirp_recv_wrap(slirp_os_socket sockfd, void *buf, size_t len, int flags) +{ + int ret; + ret = recv(sockfd, buf, len, flags); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} + +#undef recvfrom +slirp_ssize_t slirp_recvfrom_wrap(slirp_os_socket sockfd, void *buf, size_t len, int flags, + struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = win32_socket_error(); + } + return ret; +} +#endif /* WIN32 */ + +void slirp_pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for (;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = (char) c; + } + *q = '\0'; +} + +G_GNUC_PRINTF(3, 0) +static int slirp_vsnprintf(char *str, size_t size, + const char *format, va_list args) +{ + int rv = g_vsnprintf(str, size, format, args); + + if (rv < 0) { + g_error("g_vsnprintf() failed: %s", g_strerror(errno)); + } + + return rv; +} + +/* + * A snprintf()-like function that: + * - returns the number of bytes written (excluding optional \0-ending) + * - dies on error + * - warn on truncation + */ +int slirp_fmt(char *str, size_t size, const char *format, ...) +{ + va_list args; + int rv; + + va_start(args, format); + rv = slirp_vsnprintf(str, size, format, args); + va_end(args); + + if (rv >= size) { + g_critical("slirp_fmt() truncation"); + } + + return MIN(rv, size); +} + +/* + * A snprintf()-like function that: + * - always \0-end (unless size == 0) + * - returns the number of bytes actually written, including \0 ending + * - dies on error + * - warn on truncation + */ +int slirp_fmt0(char *str, size_t size, const char *format, ...) +{ + va_list args; + int rv; + + va_start(args, format); + rv = slirp_vsnprintf(str, size, format, args); + va_end(args); + + if (rv >= size) { + g_critical("slirp_fmt0() truncation"); + if (size > 0) + str[size - 1] = '\0'; + rv = size; + } else { + rv += 1; /* include \0 */ + } + + return rv; +} + +const char *slirp_ether_ntoa(const uint8_t *addr, char *out_str, + size_t out_str_size) +{ + assert(out_str_size >= ETH_ADDRSTRLEN); + + slirp_fmt0(out_str, out_str_size, "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + return out_str; +} + +/* Programatically set and reset debugging flags in slirp_debug vice + * setting them via the SLIRP_DEBUG environment variable. */ + +void slirp_set_debug(unsigned int flags) +{ + slirp_debug |= flags; +} + +void slirp_reset_debug(unsigned int flags) +{ + slirp_debug &= ~flags; +} diff --git a/libslirp/src/util.h b/libslirp/src/util.h new file mode 100644 index 000000000..3922731e3 --- /dev/null +++ b/libslirp/src/util.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010-2019 Red Hat, Inc. + * + * 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. + */ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "libslirp.h" + +#ifdef __GNUC__ +#define SLIRP_PACKED_BEGIN +#if !defined(__clang__) && (defined(_WIN32) && (defined(__x86_64__) || defined(__i386__))) +#define SLIRP_PACKED_END __attribute__((gcc_struct, packed)) +#else +#define SLIRP_PACKED_END __attribute__((packed)) +#endif +#elif defined(_MSC_VER) +#define SLIRP_PACKED_BEGIN __pragma(pack(push, 1)) +#define SLIRP_PACKED_END __pragma(pack(pop)) +#endif + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) \ + ((type *) (((char *)(ptr)) - offsetof(type, member))) +#endif + +#ifndef G_SIZEOF_MEMBER +#define G_SIZEOF_MEMBER(type, member) sizeof(((type *)0)->member) +#endif + +/* size_t, ssize_t format specifier. Windows, naturally, has to be different + * and, despite implementing "%z", MinGW hasn't caught up. */ +#if defined(__MINGW64__) || defined(_WIN64) +# if defined(PRIu64) +# define SLIRP_PRIsize_t PRIu64 +# else +# define SLIRP_PRIsize_t "llu" +# endif +# if defined(PRId64) +# define SLIRP_PRIssize_t PRId64 +# else +# define SLIRP_PRIssize_t "lld" +# endif +#elif defined(__MINGW32__) || defined(_WIN32) +# if defined(PRIu32) +# define SLIRP_PRIsize_t PRIu32 +# else +# define SLIRP_PRIsize_t "lu" +# endif +# if defined(PRId32) +# define SLIRP_PRIssize_t PRId32 +# else +# define SLIRP_PRIssize_t "ld" +# endif +#else +#define SLIRP_PRIsize_t "zu" +#define SLIRP_PRIssize_t "zd" +#endif + +#if defined(_WIN32) /* CONFIG_IOVEC */ +#if !defined(IOV_MAX) /* XXX: to avoid duplicate with QEMU osdep.h */ +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif +#else +#include +#endif + +#define stringify(s) tostring(s) +#define tostring(s) #s + +#define SCALE_MS 1000000 + +#define ETH_ALEN 6 +#define ETH_ADDRSTRLEN 18 /* "xx:xx:xx:xx:xx:xx", with trailing NUL */ +#define ETH_HLEN 14 +#define ETH_MINLEN 60 +#define ETH_P_IP (0x0800) /* Internet Protocol packet */ +#define ETH_P_ARP (0x0806) /* Address Resolution packet */ +#define ETH_P_IPV6 (0x86dd) +#define ETH_P_VLAN (0x8100) +#define ETH_P_DVLAN (0x88a8) +#define ETH_P_NCSI (0x88f8) +#define ETH_P_UNKNOWN (0xffff) + + +/* Windows: BLUF -- these functions have to have wrappers because Windows, just to + * be Windows, has error constants that are not the same as . Consequently, + * if one of the functions returns an error, the winsock2 error from WSAGetLastError() + * needs to be translated. + * + * To make the problem more complex, we have to ensure to include , which + * defines errno in a thread safe way. If we do not include , errno gets + * defined as WSAGetLastError(), which makes assigning a translated value impossible. + * Or ends up making the errno checking much more interesting than it has to be. + */ +#if defined(_WIN32) +#undef accept +#undef bind +#undef closesocket +#undef connect +#undef getpeername +#undef getsockname +#undef getsockopt +#undef ioctlsocket +#undef listen +#undef recv +#undef recvfrom +#undef send +#undef sendto +#undef setsockopt +#undef shutdown +#undef socket + +#define connect slirp_connect_wrap +int slirp_connect_wrap(slirp_os_socket fd, const struct sockaddr *addr, int addrlen); +#define listen slirp_listen_wrap +int slirp_listen_wrap(slirp_os_socket fd, int backlog); +#define bind slirp_bind_wrap +int slirp_bind_wrap(slirp_os_socket fd, const struct sockaddr *addr, int addrlen); +#define socket slirp_socket_wrap +slirp_os_socket slirp_socket_wrap(int domain, int type, int protocol); +#define accept slirp_accept_wrap +slirp_os_socket slirp_accept_wrap(slirp_os_socket fd, struct sockaddr *addr, int *addrlen); +#define shutdown slirp_shutdown_wrap +int slirp_shutdown_wrap(slirp_os_socket fd, int how); +#define getpeername slirp_getpeername_wrap +int slirp_getpeername_wrap(slirp_os_socket fd, struct sockaddr *addr, int *addrlen); +#define getsockname slirp_getsockname_wrap +int slirp_getsockname_wrap(slirp_os_socket fd, struct sockaddr *addr, int *addrlen); +#define send slirp_send_wrap +slirp_ssize_t slirp_send_wrap(slirp_os_socket fd, const void *buf, size_t len, int flags); +#define sendto slirp_sendto_wrap +slirp_ssize_t slirp_sendto_wrap(slirp_os_socket fd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, int addrlen); +#define recv slirp_recv_wrap +slirp_ssize_t slirp_recv_wrap(slirp_os_socket fd, void *buf, size_t len, int flags); +#define recvfrom slirp_recvfrom_wrap +slirp_ssize_t slirp_recvfrom_wrap(slirp_os_socket fd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, int *addrlen); +#define closesocket slirp_closesocket_wrap +int slirp_closesocket_wrap(slirp_os_socket fd); +#define ioctlsocket slirp_ioctlsocket_wrap +int slirp_ioctlsocket_wrap(slirp_os_socket fd, int req, void *val); +#define getsockopt slirp_getsockopt_wrap +int slirp_getsockopt_wrap(slirp_os_socket sockfd, int level, int optname, void *optval, + int *optlen); +#define setsockopt slirp_setsockopt_wrap +int slirp_setsockopt_wrap(slirp_os_socket sockfd, int level, int optname, + const void *optval, int optlen); +#define inet_aton slirp_inet_aton + +#if WINVER < 0x0601 +/* Windows versions older than Windows 7: */ + +#undef inet_pton +#undef inet_ntop + +#define inet_pton slirp_inet_pton +#define inet_ntop slirp_inet_ntop + +int slirp_inet_pton(int af, const char *src, void *dst); +const char *slirp_inet_ntop(int af, const void *src, char *dst, socklen_t size); +#endif +#else +#define closesocket(s) close(s) +#define ioctlsocket(s, r, v) ioctl(s, r, v) +#endif + +slirp_os_socket slirp_socket(int domain, int type, int protocol); +void slirp_set_nonblock(slirp_os_socket fd); + +static inline int slirp_socket_set_v6only(slirp_os_socket fd, int v) +{ + return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &v, sizeof(v)); +} + +static inline int slirp_socket_set_nodelay(slirp_os_socket fd) +{ + int v = 1; + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &v, sizeof(v)); +} + +static inline int slirp_socket_set_fast_reuse(slirp_os_socket fd) +{ +#ifndef _WIN32 + int v = 1; + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); +#else +#ifdef GLIB_UNUSED_PARAM + GLIB_UNUSED_PARAM(fd); +#endif + + /* Enabling the reuse of an endpoint that was used by a socket still in + * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows + * fast reuse is the default and SO_REUSEADDR does strange things. So we + * don't have to do anything here. More info can be found at: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */ + return 0; +#endif +} + +/* Socket error check */ +static inline int have_valid_socket(slirp_os_socket s) +{ +#if !defined(_WIN32) + return (s >= 0); +#else + return (s != SLIRP_INVALID_SOCKET); +#endif +} + +/* And the inverse -- the code reads more smoothly vs "!have_valid_socket" */ +static inline int not_valid_socket(slirp_os_socket s) +{ + return !have_valid_socket(s); +} + +void slirp_pstrcpy(char *buf, int buf_size, const char *str); + +int slirp_fmt(char *str, size_t size, const char *format, ...) G_GNUC_PRINTF(3, 4); +int slirp_fmt0(char *str, size_t size, const char *format, ...) G_GNUC_PRINTF(3, 4); + +/* + * Pretty print a MAC address into out_str. + * As a convenience returns out_str. + */ +const char *slirp_ether_ntoa(const uint8_t *addr, char *out_str, + size_t out_str_len); + +#endif diff --git a/libslirp/src/version.c b/libslirp/src/version.c new file mode 100644 index 000000000..93e0be9c2 --- /dev/null +++ b/libslirp/src/version.c @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#include "libslirp.h" + +const char * +slirp_version_string(void) +{ + return SLIRP_VERSION_STRING; +} diff --git a/libslirp/src/vmstate.c b/libslirp/src/vmstate.c new file mode 100644 index 000000000..2c3a727f8 --- /dev/null +++ b/libslirp/src/vmstate.c @@ -0,0 +1,445 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * VMState interpreter + * + * Copyright (c) 2009-2018 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +#include +#include +#include +#include + +#include "stream.h" +#include "vmstate.h" + +static int get_nullptr(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) { + return 0; + } + g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER"); + return -EINVAL; +} + +static int put_nullptr(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) + +{ + if (pv == NULL) { + slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER); + return 0; + } + g_warning("vmstate: put_nullptr must be called with pv == NULL"); + return -EINVAL; +} + +const VMStateInfo slirp_vmstate_info_nullptr = { + .name = "uint64", + .get = get_nullptr, + .put = put_nullptr, +}; + +/* 8 bit unsigned int */ + +static int get_uint8(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint8_t *v = pv; + *v = slirp_istream_read_u8(f); + return 0; +} + +static int put_uint8(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint8_t *v = pv; + slirp_ostream_write_u8(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint8 = { + .name = "uint8", + .get = get_uint8, + .put = put_uint8, +}; + +/* 16 bit unsigned int */ + +static int get_uint16(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint16_t *v = pv; + *v = slirp_istream_read_u16(f); + return 0; +} + +static int put_uint16(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint16_t *v = pv; + slirp_ostream_write_u16(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint16 = { + .name = "uint16", + .get = get_uint16, + .put = put_uint16, +}; + +/* 32 bit unsigned int */ + +static int get_uint32(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint32_t *v = pv; + *v = slirp_istream_read_u32(f); + return 0; +} + +static int put_uint32(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint32_t *v = pv; + slirp_ostream_write_u32(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint32 = { + .name = "uint32", + .get = get_uint32, + .put = put_uint32, +}; + +/* 16 bit int */ + +static int get_int16(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + int16_t *v = pv; + *v = slirp_istream_read_i16(f); + return 0; +} + +static int put_int16(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + int16_t *v = pv; + slirp_ostream_write_i16(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_int16 = { + .name = "int16", + .get = get_int16, + .put = put_int16, +}; + +/* 32 bit int */ + +static int get_int32(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + int32_t *v = pv; + *v = slirp_istream_read_i32(f); + return 0; +} + +static int put_int32(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + int32_t *v = pv; + slirp_ostream_write_i32(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_int32 = { + .name = "int32", + .get = get_int32, + .put = put_int32, +}; + +/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate + * a temporary buffer and the pre_load/pre_save methods in the child vmsd + * copy stuff from the parent into the child and do calculations to fill + * in fields that don't really exist in the parent but need to be in the + * stream. + */ +static int get_tmp(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + int ret; + const VMStateDescription *vmsd = field->vmsd; + int version_id = field->version_id; + void *tmp = g_malloc(size); + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id); + g_free(tmp); + return ret; +} + +static int put_tmp(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + const VMStateDescription *vmsd = field->vmsd; + void *tmp = g_malloc(size); + int ret; + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + ret = slirp_vmstate_save_state(f, vmsd, tmp); + g_free(tmp); + + return ret; +} + +const VMStateInfo slirp_vmstate_info_tmp = { + .name = "tmp", + .get = get_tmp, + .put = put_tmp, +}; + +/* uint8_t buffers */ + +static int get_buffer(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + slirp_istream_read(f, pv, size); + return 0; +} + +static int put_buffer(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + slirp_ostream_write(f, pv, size); + return 0; +} + +const VMStateInfo slirp_vmstate_info_buffer = { + .name = "buffer", + .get = get_buffer, + .put = put_buffer, +}; + +static int vmstate_n_elems(char *opaque, const VMStateField *field) +{ + int n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY_INT32) { + n_elems = *(int32_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT16) { + n_elems = *(uint16_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque + field->num_offset); + } + + if (field->flags & VMS_MULTIPLY_ELEMENTS) { + n_elems *= field->num; + } + + return n_elems; +} + +static int vmstate_size(char *opaque, const VMStateField *field) +{ + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque + field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + + return size; +} + +static int vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd, + char *opaque, int version_id) +{ + int ret = 0; + const VMStateField *field = vmsd->fields; + + if (vmsd->pre_save) { + ret = vmsd->pre_save(opaque); + if (ret) { + g_warning("pre-save failed: %s", vmsd->name); + return ret; + } + } + + while (field->name) { + if ((field->field_exists && field->field_exists(opaque, version_id)) || + (!field->field_exists && field->version_id <= version_id)) { + char *first_elem = opaque + field->offset; + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + + if (field->flags & VMS_POINTER) { + first_elem = *(void **)first_elem; + assert(first_elem || !n_elems || !size); + } + for (i = 0; i < n_elems; i++) { + void *curr_elem = first_elem + size * i; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + assert(curr_elem); + curr_elem = *(void **)curr_elem; + } + if (!curr_elem && size) { + /* if null pointer write placeholder and do not follow */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size, + NULL); + } else if (field->flags & VMS_STRUCT) { + ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, field->vmsd, curr_elem, + field->struct_version_id); + } else { + ret = field->info->put(f, curr_elem, size, field); + } + if (ret) { + g_warning("Save of field %s/%s failed", vmsd->name, + field->name); + return ret; + } + } + } else { + if (field->flags & VMS_MUST_EXIST) { + g_warning("Output state validation failed: %s/%s", vmsd->name, + field->name); + assert(!(field->flags & VMS_MUST_EXIST)); + } + } + field++; + } + + return 0; +} + +int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id); +} + +static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) +{ + if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) { + size_t size = vmstate_size(opaque, field); + size *= vmstate_n_elems(opaque, field); + if (size) { + *(void **)ptr = g_malloc(size); + } + } +} + +int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, + void *opaque_, int version_id) +{ + VMStateField *field = vmsd->fields; + int ret = 0; + char *opaque = opaque_; + + if (version_id > vmsd->version_id) { + g_warning("%s: incoming version_id %d is too new " + "for local version_id %d", + vmsd->name, version_id, vmsd->version_id); + return -EINVAL; + } + if (vmsd->pre_load) { + int ret = vmsd->pre_load(opaque); + if (ret) { + return ret; + } + } + while (field->name) { + if ((field->field_exists && field->field_exists(opaque, version_id)) || + (!field->field_exists && field->version_id <= version_id)) { + char *first_elem = opaque + field->offset; + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + + vmstate_handle_alloc(first_elem, field, opaque); + if (field->flags & VMS_POINTER) { + first_elem = *(void **)first_elem; + assert(first_elem || !n_elems || !size); + } + for (i = 0; i < n_elems; i++) { + void *curr_elem = first_elem + size * i; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + curr_elem = *(void **)curr_elem; + } + if (!curr_elem && size) { + /* if null pointer check placeholder and do not follow */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size, + NULL); + } else if (field->flags & VMS_STRUCT) { + ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, + field->vmsd->version_id); + } else if (field->flags & VMS_VSTRUCT) { + ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, + field->struct_version_id); + } else { + ret = field->info->get(f, curr_elem, size, field); + } + if (ret < 0) { + g_warning("Failed to load %s:%s", vmsd->name, field->name); + return ret; + } + } + } else if (field->flags & VMS_MUST_EXIST) { + g_warning("Input validation failed: %s/%s", vmsd->name, + field->name); + return -1; + } + field++; + } + if (vmsd->post_load) { + ret = vmsd->post_load(opaque, version_id); + } + return ret; +} diff --git a/libslirp/src/vmstate.h b/libslirp/src/vmstate.h new file mode 100644 index 000000000..82dcec79d --- /dev/null +++ b/libslirp/src/vmstate.h @@ -0,0 +1,407 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * QEMU migration/snapshot declarations + * + * Copyright (c) 2009-2011 Red Hat, Inc. + * + * Original author: Juan Quintela + * + * 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 copyright holder 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT HOLDER 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. + */ +#ifndef VMSTATE_H_ +#define VMSTATE_H_ + +#include +#include +#include "slirp.h" +#include "stream.h" + +#define stringify(s) tostring(s) +#define tostring(s) #s + +typedef struct VMStateInfo VMStateInfo; +typedef struct VMStateDescription VMStateDescription; +typedef struct VMStateField VMStateField; + +int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, + void *opaque); +int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, + void *opaque, int version_id); + +/* VMStateInfo allows customized migration of objects that don't fit in + * any category in VMStateFlags. Additional information is always passed + * into get and put in terms of field and vmdesc parameters. However + * these two parameters should only be used in cases when customized + * handling is needed, such as QTAILQ. For primitive data types such as + * integer, field and vmdesc parameters should be ignored inside get/put. + */ +struct VMStateInfo { + const char *name; + int (*get)(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field); + int (*put)(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field); +}; + +enum VMStateFlags { + /* Ignored */ + VMS_SINGLE = 0x001, + + /* The struct member at opaque + VMStateField.offset is a pointer + * to the actual field (e.g. struct a { uint8_t *b; + * }). Dereference the pointer before using it as basis for + * further pointer arithmetic (see e.g. VMS_ARRAY). Does not + * affect the meaning of VMStateField.num_offset or + * VMStateField.size_offset; see VMS_VARRAY* and VMS_VBUFFER for + * those. */ + VMS_POINTER = 0x002, + + /* The field is an array of fixed size. VMStateField.num contains + * the number of entries in the array. The size of each entry is + * given by VMStateField.size and / or opaque + + * VMStateField.size_offset; see VMS_VBUFFER and + * VMS_MULTIPLY. Each array entry will be processed individually + * (VMStateField.info.get()/put() if VMS_STRUCT is not set, + * recursion into VMStateField.vmsd if VMS_STRUCT is set). May not + * be combined with VMS_VARRAY*. */ + VMS_ARRAY = 0x004, + + /* The field is itself a struct, containing one or more + * fields. Recurse into VMStateField.vmsd. Most useful in + * combination with VMS_ARRAY / VMS_VARRAY*, recursing into each + * array entry. */ + VMS_STRUCT = 0x008, + + /* The field is an array of variable size. The int32_t at opaque + + * VMStateField.num_offset contains the number of entries in the + * array. See the VMS_ARRAY description regarding array handling + * in general. May not be combined with VMS_ARRAY or any other + * VMS_VARRAY*. */ + VMS_VARRAY_INT32 = 0x010, + + /* Ignored */ + VMS_BUFFER = 0x020, + + /* The field is a (fixed-size or variable-size) array of pointers + * (e.g. struct a { uint8_t *b[]; }). Dereference each array entry + * before using it. Note: Does not imply any one of VMS_ARRAY / + * VMS_VARRAY*; these need to be set explicitly. */ + VMS_ARRAY_OF_POINTER = 0x040, + + /* The field is an array of variable size. The uint16_t at opaque + * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT16 = 0x080, + + /* The size of the individual entries (a single array entry if + * VMS_ARRAY or any of VMS_VARRAY* are set, or the field itself if + * neither is set) is variable (i.e. not known at compile-time), + * but the same for all entries. Use the int32_t at opaque + + * VMStateField.size_offset (subject to VMS_MULTIPLY) to determine + * the size of each (and every) entry. */ + VMS_VBUFFER = 0x100, + + /* Multiply the entry size given by the int32_t at opaque + + * VMStateField.size_offset (see VMS_VBUFFER description) with + * VMStateField.size to determine the number of bytes to be + * allocated. Only valid in combination with VMS_VBUFFER. */ + VMS_MULTIPLY = 0x200, + + /* The field is an array of variable size. The uint8_t at opaque + + * VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT8 = 0x400, + + /* The field is an array of variable size. The uint32_t at opaque + * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) + * contains the number of entries in the array. See the VMS_ARRAY + * description regarding array handling in general. May not be + * combined with VMS_ARRAY or any other VMS_VARRAY*. */ + VMS_VARRAY_UINT32 = 0x800, + + /* Fail loading the serialised VM state if this field is missing + * from the input. */ + VMS_MUST_EXIST = 0x1000, + + /* When loading serialised VM state, allocate memory for the + * (entire) field. Only valid in combination with + * VMS_POINTER. Note: Not all combinations with other flags are + * currently supported, e.g. VMS_ALLOC|VMS_ARRAY_OF_POINTER won't + * cause the individual entries to be allocated. */ + VMS_ALLOC = 0x2000, + + /* Multiply the number of entries given by the integer at opaque + + * VMStateField.num_offset (see VMS_VARRAY*) with VMStateField.num + * to determine the number of entries in the array. Only valid in + * combination with one of VMS_VARRAY*. */ + VMS_MULTIPLY_ELEMENTS = 0x4000, + + /* A structure field that is like VMS_STRUCT, but uses + * VMStateField.struct_version_id to tell which version of the + * structure we are referencing to use. */ + VMS_VSTRUCT = 0x8000, + + /* Marker for end of list */ + VMS_END = 0x10000 +}; + +struct VMStateField { + const char *name; + size_t offset; + size_t size; + size_t start; + int num; + size_t num_offset; + size_t size_offset; + const VMStateInfo *info; + enum VMStateFlags flags; + const VMStateDescription *vmsd; + int version_id; + int struct_version_id; + bool (*field_exists)(void *opaque, int version_id); +}; + +struct VMStateDescription { + const char *name; + int version_id; + int (*pre_load)(void *opaque); + int (*post_load)(void *opaque, int version_id); + int (*pre_save)(void *opaque); + VMStateField *fields; +}; + + +extern const VMStateInfo slirp_vmstate_info_int16; +extern const VMStateInfo slirp_vmstate_info_int32; +extern const VMStateInfo slirp_vmstate_info_uint8; +extern const VMStateInfo slirp_vmstate_info_uint16; +extern const VMStateInfo slirp_vmstate_info_uint32; + +/** Put this in the stream when migrating a null pointer.*/ +#define VMS_NULLPTR_MARKER (0x30U) /* '0' */ +extern const VMStateInfo slirp_vmstate_info_nullptr; + +extern const VMStateInfo slirp_vmstate_info_buffer; +extern const VMStateInfo slirp_vmstate_info_tmp; + +/* __typeof__ is recommended for better portability over typeof. + * + * __typeof__ is available in GCC, Clang, Clang masquerading as gcc on macOS, + * as well as MSVC version 19.39.33428+. It's also part of C23. + */ +#if defined(__GNUC__) || defined(__clang__) || (_MSC_FULL_VER >= 193933428) || \ + (__STDC_VERSION__ >= 202301L) +#define type_check_array(t1, t2, n) ((t1(*)[n])0 - (t2 *)0) +#define type_check_pointer(t1, t2) ((t1 **)0 - (t2 *)0) +#define typeof_field(type, field) __typeof__(((type *)0)->field) +#define type_check(t1, t2) ((t1 *)0 - (t2 *)0) +#else +#define type_check_array(t1, t2, n) 0 +#define type_check_pointer(t1, t2) 0 +#define typeof_field(type, field) (((type *)0)->field) +#define type_check(t1, t2) 0 +#endif + +#define vmstate_offset_value(_state, _field, _type) \ + (offsetof(_state, _field) + type_check(_type, typeof_field(_state, _field))) + +#define vmstate_offset_pointer(_state, _field, _type) \ + (offsetof(_state, _field) + \ + type_check_pointer(_type, typeof_field(_state, _field))) + +#define vmstate_offset_array(_state, _field, _type, _num) \ + (offsetof(_state, _field) + \ + type_check_array(_type, typeof_field(_state, _field), _num)) + +#define vmstate_offset_buffer(_state, _field) \ + vmstate_offset_array(_state, _field, uint8_t, \ + sizeof(typeof_field(_state, _field))) + +/* In the macros below, if there is a _version, that means the macro's + * field will be processed only if the version being received is >= + * the _version specified. In general, if you add a new field, you + * would increment the structure's version and put that version + * number into the new field so it would only be processed with the + * new version. + * + * In particular, for VMSTATE_STRUCT() and friends the _version does + * *NOT* pick the version of the sub-structure. It works just as + * specified above. The version of the top-level structure received + * is passed down to all sub-structures. This means that the + * sub-structures must have version that are compatible with all the + * structures that use them. + * + * If you want to specify the version of the sub-structure, use + * VMSTATE_VSTRUCT(), which allows the specific sub-structure version + * to be directly specified. + */ + +#define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) \ + { \ + .name = (stringify(_field)), .version_id = (_version), \ + .field_exists = (_test), .size = sizeof(_type), .info = &(_info), \ + .flags = VMS_SINGLE, \ + .offset = vmstate_offset_value(_state, _field, _type), \ + } + +#define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) \ + { \ + .name = (stringify(_field)), .version_id = (_version), .num = (_num), \ + .info = &(_info), .size = sizeof(_type), .flags = VMS_ARRAY, \ + .offset = vmstate_offset_array(_state, _field, _type, _num), \ + } + +#define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) \ + { \ + .name = (stringify(_field)), .version_id = (_version), \ + .field_exists = (_test), .vmsd = &(_vmsd), .size = sizeof(_type), \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, _type), \ + } + +#define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) \ + { \ + .name = (stringify(_field)), .version_id = (_version), \ + .vmsd = &(_vmsd), .size = sizeof(_type *), \ + .flags = VMS_STRUCT | VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ + } + +#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, \ + _vmsd, _type) \ + { \ + .name = (stringify(_field)), .num = (_num), .field_exists = (_test), \ + .version_id = (_version), .vmsd = &(_vmsd), .size = sizeof(_type), \ + .flags = VMS_STRUCT | VMS_ARRAY, \ + .offset = vmstate_offset_array(_state, _field, _type, _num), \ + } + +#define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) \ + { \ + .name = (stringify(_field)), .version_id = (_version), \ + .field_exists = (_test), .size = (_size - _start), \ + .info = &slirp_vmstate_info_buffer, .flags = VMS_BUFFER, \ + .offset = vmstate_offset_buffer(_state, _field) + _start, \ + } + +#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _field_size) \ + { \ + .name = (stringify(_field)), .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, uint32_t), \ + .info = &slirp_vmstate_info_buffer, \ + .flags = VMS_VBUFFER | VMS_POINTER, \ + .offset = offsetof(_state, _field), \ + } + +#define QEMU_BUILD_BUG_ON_STRUCT(x) \ + struct { \ + int : (x) ? -1 : 1; \ + } + +#define QEMU_BUILD_BUG_ON_ZERO(x) \ + (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) + +/* Allocate a temporary of type 'tmp_type', set tmp->parent to _state + * and execute the vmsd on the temporary. Note that we're working with + * the whole of _state here, not a field within it. + * We compile time check that: + * That _tmp_type contains a 'parent' member that's a pointer to the + * '_state' type + * That the pointer is right at the start of _tmp_type. + */ +#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) \ + { \ + .name = "tmp", \ + .size = sizeof(_tmp_type) + \ + QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \ + type_check_pointer(_state, typeof_field(_tmp_type, parent)), \ + .vmsd = &(_vmsd), .info = &slirp_vmstate_info_tmp, \ + } + +#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ + VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) + +#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ + VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) + +#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ + VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type) + +#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ + VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, _vmsd, \ + _type) + +#define VMSTATE_INT16_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int16, int16_t) +#define VMSTATE_INT32_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int32, int32_t) + +#define VMSTATE_UINT8_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint8, uint8_t) +#define VMSTATE_UINT16_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint16, uint16_t) +#define VMSTATE_UINT32_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint32, uint32_t) + +#define VMSTATE_INT16(_f, _s) VMSTATE_INT16_V(_f, _s, 0) +#define VMSTATE_INT32(_f, _s) VMSTATE_INT32_V(_f, _s, 0) + +#define VMSTATE_UINT8(_f, _s) VMSTATE_UINT8_V(_f, _s, 0) +#define VMSTATE_UINT16(_f, _s) VMSTATE_UINT16_V(_f, _s, 0) +#define VMSTATE_UINT32(_f, _s) VMSTATE_UINT32_V(_f, _s, 0) + +#define VMSTATE_UINT16_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint16, uint16_t) + +#define VMSTATE_UINT32_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint32, uint32_t) + +#define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, slirp_vmstate_info_int16, int16_t) + +#define VMSTATE_INT16_ARRAY(_f, _s, _n) VMSTATE_INT16_ARRAY_V(_f, _s, _n, 0) + +#define VMSTATE_BUFFER_V(_f, _s, _v) \ + VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f))) + +#define VMSTATE_BUFFER(_f, _s) VMSTATE_BUFFER_V(_f, _s, 0) + +#define VMSTATE_END_OF_LIST() \ + { \ + .flags = VMS_END, \ + } + +#endif /* VMSTATE_H_ */ diff --git a/libslirp/test/ncsitest.c b/libslirp/test/ncsitest.c new file mode 100644 index 000000000..f5ee0b5a6 --- /dev/null +++ b/libslirp/test/ncsitest.c @@ -0,0 +1,176 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + */ + +/* + * This test verifies slirp responses to NC-SI commands. + */ + +#include +#include +#include + +#include "slirp.h" +#include "ncsi-pkt.h" + +#define NCSI_RESPONSE_CAPACITY 1024 + +static void test_ncsi_get_version_id(Slirp *slirp) +{ + slirp->mfr_id = 0xabcdef01; + + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x15, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x00, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + }; + slirp_input(slirp, command, sizeof(command)); + + const struct ncsi_rsp_gvi_pkt *gvi = (const struct ncsi_rsp_gvi_pkt *) ((const char*) slirp->opaque + ETH_HLEN); + + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(gvi->rsp.code) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(gvi->mf_id) == slirp->mfr_id); + + slirp->mfr_id = 0; +} + +static void test_ncsi_oem_mlx_unsupported_command(Slirp *slirp) +{ + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0xff, /* Command Revision */ + 0xff, /* Command ID */ + 0x00, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = (const struct ncsi_rsp_oem_pkt *) ((const char*) slirp->opaque + ETH_HLEN); + + slirp->mfr_id = 0x00000000; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); + + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_UNSUPPORTED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_UNKNOWN); + assert(ntohl(oem->mfr_id) == 0x8119); +} + +static void test_ncsi_oem_mlx_gma(Slirp *slirp) +{ + uint8_t oob_eth_addr[ETH_ALEN] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}; + uint8_t command[] = { + /* Destination MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Source MAC */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* Ethertype */ + 0x88, 0xf8, + /* NC-SI Control packet header */ + 0x00, /* MC ID */ + 0x01, /* Header revision */ + 0x00, /* Reserved */ + 0x01, /* Instance ID */ + 0x50, /* Control Packet Type */ + 0x00, /* Channel ID */ + 0x00, /* Reserved */ + 0x08, /* Payload length */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + 0x00, 0x00, 0x00, 0x00, /* Reserved */ + /* NC-SI OEM packet header */ + 0x00, 0x00, 0x81, 0x19, /* Manufacturer ID: Mellanox */ + /* Vendor Data */ + 0x00, /* Command Revision */ + 0x00, /* Command ID */ + 0x1b, /* Parameter */ + 0x00, /* Optional data */ + }; + const struct ncsi_rsp_oem_pkt *oem = (const struct ncsi_rsp_oem_pkt *) ((const char*) slirp->opaque + ETH_HLEN); + + memset(slirp->oob_eth_addr, 0, ETH_ALEN); + slirp->mfr_id = 0x8119; + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(slirp->oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 0); + + memcpy(slirp->oob_eth_addr, oob_eth_addr, ETH_ALEN); + slirp_input(slirp, command, sizeof(command)); + + assert(ntohs(oem->rsp.code) == NCSI_PKT_RSP_C_COMPLETED); + assert(ntohs(oem->rsp.reason) == NCSI_PKT_RSP_R_NO_ERROR); + assert(ntohl(oem->mfr_id) == slirp->mfr_id); + assert(ntohs(oem->rsp.common.length) == MLX_GMA_PAYLOAD_LEN); + assert(memcmp(oob_eth_addr, &oem->data[MLX_MAC_ADDR_OFFSET], ETH_ALEN) == 0); + assert(oem->data[MLX_GMA_STATUS_OFFSET] == 1); +} + +static slirp_ssize_t send_packet(const void *buf, size_t len, void *opaque) +{ + assert(len <= NCSI_RESPONSE_CAPACITY); + memcpy(opaque, buf, len); + return len; +} + +int main(int argc, char *argv[]) +{ + SlirpConfig config = { + .version = SLIRP_CONFIG_VERSION_MAX, + }; + SlirpCb callbacks = { + .send_packet = send_packet, + }; + Slirp *slirp = NULL; + uint8_t ncsi_response[NCSI_RESPONSE_CAPACITY]; + + slirp = slirp_new(&config, &callbacks, ncsi_response); + + test_ncsi_get_version_id(slirp); + test_ncsi_oem_mlx_unsupported_command(slirp); + test_ncsi_oem_mlx_gma(slirp); + + slirp_cleanup(slirp); +} diff --git a/libslirp/test/pingtest.c b/libslirp/test/pingtest.c new file mode 100644 index 000000000..063628f71 --- /dev/null +++ b/libslirp/test/pingtest.c @@ -0,0 +1,479 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2021-2022, 2024 Samuel Thibault + */ + +/* + * This simple test configures slirp and tries to ping it + * + * Note: to make this example actually be able to use the outside world, you + * need to either + * - run as root + * - set /proc/sys/net/ipv4/ping_group_range to allow sending ICMP echo requests + * - run a UDP echo server on the target + */ + +#include +#include +#include +#include + +#include "libslirp.h" + +//#define _WIN32 +#ifdef _WIN32 +#define inet_aton slirp_inet_aton +#else +#include +#include +#include +#endif + +/* Dumb simulation tick: 100ms */ +#define TICK 100 + +static Slirp *slirp; +static bool done; +static int64_t mytime; + +/* Print a frame for debugging */ +static void print_frame(const uint8_t *data, size_t len) { + int i; + + printf("\ngot packet size %zu:\n", len); + for (i = 0; i < len; i++) { + if (i && i % 16 == 0) + printf("\n"); + printf("%s%02x", i % 16 ? " " : "", data[i]); + } + if (len % 16 != 0) + printf("\n"); + printf("\n"); +} + +/* Classical 16bit checksum */ +static void checksum(uint8_t *data, size_t size, uint8_t *cksum) { + uint32_t sum = 0; + int i; + + cksum[0] = 0; + cksum[1] = 0; + + for (i = 0; i+1 < size; i += 2) + sum += (((uint16_t) data[i]) << 8) + data[i+1]; + if (i < size) /* Odd number of bytes */ + sum += ((uint16_t) data[i]) << 8; + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + sum = ~sum; + + cksum[0] = sum >> 8; + cksum[1] = sum; +} + +/* This is called when receiving a packet from the virtual network, for the + * guest */ +static slirp_ssize_t send_packet(const void *buf, size_t len, void *opaque) { + const uint8_t *data = buf; + + assert(len >= 14); + + if (data[12] == 0x86 && + data[13] == 0xdd) { + /* Ignore IPv6 */ + return len; + } + + print_frame(data, len); + + if (data[12] == 0x08 && + data[13] == 0x06) { + /* ARP */ + /* We expect receiving an ARP request for our address */ + + /* Ethernet address type */ + assert(data[14] == 0x00); + assert(data[15] == 0x01); + + /* IPv4 address type */ + assert(data[16] == 0x08); + assert(data[17] == 0x00); + + /* Ethernet addresses are 6 bytes long */ + assert(data[18] == 0x06); + + /* IPv4 addresses are 4 bytes long */ + assert(data[19] == 0x04); + + /* Opcode: ARP request */ + assert(data[20] == 0x00); + assert(data[21] == 0x01); + + /* Ok, reply! */ + uint8_t myframe[] = { + /*** Ethernet ***/ + /* dst */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* src */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Type: ARP */ + 0x08, 0x06, + + /* ether, IPv4, */ + 0x00, 0x01, 0x08, 0x00, + /* elen, IPlen */ + 0x06, 0x04, + /* ARP reply */ + 0x00, 0x02, + + /* Our ethernet address */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Our IP address */ + 0x0a, 0x00, 0x02, 0x0e, + + /* Host ethernet address */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* Host IP address */ + 0x0a, 0x00, 0x02, 0x02, + }; + + slirp_input(slirp, myframe, sizeof(myframe)); + } + + if (data[12] == 0x08 && + data[13] == 0x00) { + /* IPv4 */ + assert(len >= 14 + 20); + + /* We expect receiving the ICMP echo reply for our echo request */ + + /* IPv + hlen */ + assert(data[14] == 0x45); + + /* proto: ICMP */ + assert(data[23] == 0x01); + + /* ICMP */ + assert(len >= 14 + 20 + 8 + 4); + + /* ICMP type: reply */ + assert(data[34] == 0x00); + + /* Check the data */ + assert(data[42] == 0xde); + assert(data[43] == 0xad); + assert(data[44] == 0xbe); + assert(data[45] == 0xef); + + /* Got the answer! */ + printf("got it!\n"); + done = 1; + } + + return len; +} + +static void guest_error(const char *msg, void *opaque) { + printf("guest error %s\n", msg); +} + + +/* + * Dumb timer implementation + */ +static int64_t clock_get_ns(void *opaque) { + return mytime; +} + +struct timer { + SlirpTimerId id; + void *cb_opaque; + int64_t expire; + struct timer *next; +}; + +static struct timer *timer_queue; + +static void *timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque) { + struct timer *new_timer = malloc(sizeof(*new_timer)); + new_timer->id = id; + new_timer->cb_opaque = cb_opaque; + new_timer->next = NULL; + return new_timer; +} + +static void timer_free(void *_timer, void *opaque) { + struct timer *timer = _timer; + struct timer **t; + + for (t = &timer_queue; *t != NULL; *t = (*t)->next) { + if (*t == timer) { + /* Not expired yet, drop it */ + *t = timer->next; + break; + } + } + + free(timer); +} + +static void timer_mod(void *_timer, int64_t expire_time, void *opaque) { + struct timer *timer = _timer; + struct timer **t; + + timer->expire = expire_time * 1000 * 1000; + + for (t = &timer_queue; *t != NULL; *t = (*t)->next) { + if (expire_time < (*t)->expire) + break; + } + + timer->next = *t; + *t = timer; +} + +static void timer_check(Slirp *slirp) { + while (timer_queue && timer_queue->expire <= mytime) + { + struct timer *t = timer_queue; + printf("handling %p at time %lu\n", + t, (unsigned long) timer_queue->expire); + timer_queue = t->next; + slirp_handle_timer(slirp, t->id, t->cb_opaque); + } +} + +static uint32_t timer_timeout(void) { + if (timer_queue) + { + uint32_t timeout = (timer_queue->expire - mytime) / (1000 * 1000); + if (timeout < TICK) + return timeout; + } + + return TICK; +} + + +/* + * Dumb polling implementation + */ +static int npoll; +static void register_poll_socket(slirp_os_socket fd, void *opaque) { + /* We might want to prepare for polling on fd */ + npoll++; +} + +static void unregister_poll_socket(slirp_os_socket fd, void *opaque) { + /* We might want to clear polling on fd */ + npoll--; +} + +static void notify(void *opaque) { + /* No need for this in single-thread case */ +} + +#ifdef _WIN32 +/* select() variant */ +static fd_set readfds, writefds, exceptfds; +static unsigned int maxfd; +static int add_poll_cb(slirp_os_socket fd, int events, void *opaque) +{ + if (events & SLIRP_POLL_IN) + FD_SET(fd, &readfds); + if (events & SLIRP_POLL_OUT) + FD_SET(fd, &writefds); + if (events & SLIRP_POLL_PRI) + FD_SET(fd, &exceptfds); + if (maxfd < fd) + maxfd = fd; + return fd; +} + +static int get_revents_cb(int idx, void *opaque) +{ + int event = 0; + if (FD_ISSET(idx, &readfds)) + event |= SLIRP_POLL_IN; + if (FD_ISSET(idx, &writefds)) + event |= SLIRP_POLL_OUT; + if (FD_ISSET(idx, &exceptfds)) + event |= SLIRP_POLL_PRI; + return event; +} + +static void dopoll(uint32_t timeout) { + int err; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + maxfd = 0; + + slirp_pollfds_fill_socket(slirp, &timeout, add_poll_cb, NULL); + printf("we will use timeout %u\n", (unsigned) timeout); + + struct timeval tv = { + .tv_sec = timeout / 1000, + .tv_usec = (timeout % 1000) * 1000, + }; + err = select(maxfd+1, &readfds, &writefds, &exceptfds, &tv); + + slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL); +} +#else +/* poll() variant */ +static struct pollfd *fds; +static int cur_poll; +static int add_poll_cb(slirp_os_socket fd, int events, void *opaque) +{ + short poll_events = 0; + + assert(cur_poll < npoll); + fds[cur_poll].fd = fd; + + if (events & SLIRP_POLL_IN) + poll_events |= POLLIN; + if (events & SLIRP_POLL_OUT) + poll_events |= POLLOUT; + if (events & SLIRP_POLL_PRI) + poll_events |= POLLPRI; + fds[cur_poll].events = poll_events; + + return cur_poll++; +} + +static int get_revents_cb(int idx, void *opaque) +{ + return fds[idx].revents; +} + +static void dopoll(uint32_t timeout) { + int err; + fds = malloc(sizeof(*fds) * npoll); + cur_poll = 0; + + slirp_pollfds_fill_socket(slirp, &timeout, add_poll_cb, NULL); + printf("we will use timeout %u\n", (unsigned) timeout); + + err = poll(fds, cur_poll, timeout); + + slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL); + + free(fds); +} +#endif + + +static struct SlirpCb callbacks = { + .send_packet = send_packet, + .guest_error = guest_error, + .clock_get_ns = clock_get_ns, + .timer_new_opaque = timer_new_opaque, + .timer_free = timer_free, + .timer_mod = timer_mod, + .register_poll_socket = register_poll_socket, + .unregister_poll_socket = unregister_poll_socket, + .notify = notify, +}; + + +int main(int argc, char *argv[]) { + SlirpConfig config = { + .version = 4, + .restricted = false, + .in_enabled = true, + .vnetwork.s_addr = htonl(0x0a000200), + .vnetmask.s_addr = htonl(0xffffff00), + .vhost.s_addr = htonl(0x0a000202), + .vdhcp_start.s_addr = htonl(0x0a00020f), + .vnameserver.s_addr = htonl(0x0a000203), + .disable_host_loopback = false, + .enable_emu = false, + .disable_dns = false, + }; + uint32_t timeout = 0; + + printf("Slirp version %s\n", slirp_version_string()); + +#if !defined(_WIN32) + inet_pton(AF_INET6, "fec0::", &config.vprefix_addr6); + config.vprefix_len = 64; + config.vhost6 = config.vprefix_addr6; + config.vhost6.s6_addr[15] = 2; + config.vnameserver6 = config.vprefix_addr6; + config.vnameserver6.s6_addr[15] = 2; + config.in6_enabled = true, +#endif + + slirp = slirp_new(&config, &callbacks, NULL); + + /* Send echo request */ + uint8_t myframe[] = { + /*** Ethernet ***/ + /* dst */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* src */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Type: IPv4 */ + 0x08, 0x00, + + /*** IPv4 ***/ + /* vhl,tos, len */ + 0x45, 0x00, 0x00, 0x20, + /* id, off (DF) */ + 0x68, 0xd7, 0x40, 0x00, + /* ttl,pro, cksum */ + 0x40, 0x01, 0x00, 0x00, + /* src */ + 0x0a, 0x00, 0x02, 0x0e, + /* dst */ + 0x00, 0x00, 0x00, 0x00, + + /*** ICMPv4 ***/ + /* type, code, cksum */ + 0x08, 0x00, 0x00, 0x00, + /* id, seq */ + 0x01, 0xec, 0x00, 0x01, + /* data */ + 0xde, 0xad, 0xbe, 0xef, + }; + + struct in_addr in_addr = { .s_addr = htonl(0x0a000202) }; + if (argc > 1) { + if (inet_aton(argv[1], &in_addr) == 0) { + printf("usage: %s [destination IPv4 address]\n", argv[0]); + exit(EXIT_FAILURE); + } + } + uint32_t addr = ntohl(in_addr.s_addr); + myframe[30] = addr >> 24; + myframe[31] = addr >> 16; + myframe[32] = addr >> 8; + myframe[33] = addr >> 0; + + /* IPv4 header checksum */ + checksum(&myframe[14], 20, &myframe[24]); + /* ICMP header checksum */ + checksum(&myframe[34], 12, &myframe[36]); + + slirp_input(slirp, myframe, sizeof(myframe)); + + /* Wait for echo reply */ + while (!done) { + printf("time %lu\n", (unsigned long) mytime); + + timer_check(slirp); + /* Here we make the virtual time wait like the real time, but we could + * make it wait differently */ + timeout = timer_timeout(); + printf("we wish timeout %u\n", (unsigned) timeout); + + dopoll(timeout); + + /* Fake that the tick elapsed */ + mytime += TICK * 1000 * 1000; + } + + slirp_cleanup(slirp); +} diff --git a/makefile b/makefile index 79755eb24..a349eb332 100644 --- a/makefile +++ b/makefile @@ -102,6 +102,18 @@ OS_CCDEFS= AIO_CCDEFS= +# Need to set these early: +BIN = BIN/ +SIMHD = . + +# libslirp locations: +LIBSLIRP_DIR=${SIMHD}/libslirp +LIBSLIRP_SRC=${LIBSLIRP_DIR}/src + +# SYS_LDFLAGS: System libraries, e.g., -lm and -lpthread, that should appear +# at the end of the compiler's command line. +SYS_LDFLAGS = + ifneq (,${GREP_OPTIONS}) $(info GREP_OPTIONS is defined in your environment.) $(info ) @@ -367,7 +379,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) ifeq (agcc,$(findstring agcc,${GCC})) # Android target build? OS_CCDEFS += -D_GNU_SOURCE AIO_CCDEFS += -DSIM_ASYNCH_IO - OS_LDFLAGS = -lm + SYS_LDFLAGS = -lm else # Non-Android (or Native Android) Builds ifeq (,$(INCLUDES)$(LIBRARIES)) INCPATH:=$(shell LANG=C; ${GCC} -x c -v -E /dev/null 2>&1 | grep -A 10 '> search starts here' | grep '^ ' | tr -d '\n') @@ -452,7 +464,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) LIBPATH := $(shell LANG=C; crle | grep 'Default Library Path' | awk '{ print $$5 }' | sed 's/:/ /g') endif LIBEXT = so - OS_LDFLAGS += -lsocket -lnsl + SYS_LDFLAGS += -lsocket -lnsl ifeq (incsfw,$(shell if ${TEST} -d /opt/sfw/include; then echo incsfw; fi)) INCPATH += /opt/sfw/include OS_CCDEFS += -I/opt/sfw/include @@ -471,7 +483,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) LIBEXT = a else ifneq (,$(findstring AIX,$(OSTYPE))) - OS_LDFLAGS += -lm -lrt + SYS_LDFLAGS += -lm -lrt ifeq (incopt,$(shell if ${TEST} -d /opt/freeware/include; then echo incopt; fi)) INCPATH += /opt/freeware/include OS_CCDEFS += -I/opt/freeware/include @@ -492,7 +504,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) INCPATH := $(shell findpaths -e -a $(HAIKU_ARCH) B_FIND_PATH_HEADERS_DIRECTORY) INCPATH += $(shell findpaths -e B_FIND_PATH_HEADERS_DIRECTORY posix) LIBPATH := $(shell findpaths -e -a $(HAIKU_ARCH) B_FIND_PATH_DEVELOP_LIB_DIRECTORY) - OS_LDFLAGS += -lnetwork + SYS_LDFLAGS += -lnetwork else ifeq (,$(findstring NetBSD,$(OSTYPE))) ifneq (no ldconfig,$(findstring no ldconfig,$(shell which ldconfig 2>&1))) @@ -569,24 +581,24 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) need_search = $(strip $(shell ld -l$(1) /dev/null 2>&1 | grep $(1) | sed s/$(1)//)) LD_SEARCH_NEEDED := $(call need_search,ZzzzzzzZ) ifneq (,$(call find_lib,m)) - OS_LDFLAGS += -lm + SYS_LDFLAGS += -lm $(info using libm: $(call find_lib,m)) endif ifneq (,$(call find_lib,rt)) - OS_LDFLAGS += -lrt + SYS_LDFLAGS += -lrt $(info using librt: $(call find_lib,rt)) endif ifneq (,$(call find_include,pthread)) ifneq (,$(call find_lib,pthread)) AIO_CCDEFS += -DUSE_READER_THREAD -DSIM_ASYNCH_IO - OS_LDFLAGS += -lpthread + SYS_LDFLAGS += -lpthread $(info using libpthread: $(call find_lib,pthread) $(call find_include,pthread)) else LIBEXTSAVE := ${LIBEXT} LIBEXT = a ifneq (,$(call find_lib,pthread)) AIO_CCDEFS += -DUSE_READER_THREAD -DSIM_ASYNCH_IO - OS_LDFLAGS += -lpthread + SYS_LDFLAGS += -lpthread $(info using libpthread: $(call find_lib,pthread) $(call find_include,pthread)) else ifneq (,$(findstring Haiku,$(OSTYPE))) @@ -595,7 +607,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) else ifeq (Darwin,$(OSTYPE)) AIO_CCDEFS += -DUSE_READER_THREAD -DSIM_ASYNCH_IO - OS_LDFLAGS += -lpthread + SYS_LDFLAGS += -lpthread $(info using macOS libpthread: $(call find_include,pthread)) endif endif @@ -635,7 +647,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) ifneq (,$(call find_include,dlfcn)) ifneq (,$(call find_lib,dl)) OS_CCDEFS += -DSIM_HAVE_DLOPEN=$(LIBSOEXT) - OS_LDFLAGS += -ldl + SYS_LDFLAGS += -ldl $(info using libdl: $(call find_lib,dl) $(call find_include,dlfcn)) else ifneq (,$(findstring BSD,$(OSTYPE))$(findstring AIX,$(OSTYPE))$(findstring Haiku,$(OSTYPE))) @@ -644,7 +656,7 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) else ifneq (,$(call find_lib,dld)) OS_CCDEFS += -DSIM_HAVE_DLOPEN=$(LIBSOEXT) - OS_LDFLAGS += -ldld + SYS_LDFLAGS += -ldld $(info using libdld: $(call find_lib,dld) $(call find_include,dlfcn)) else ifeq (Darwin,$(OSTYPE)) @@ -713,76 +725,88 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) SDLX_CONFIG = sdl2-config endif ifneq (,$(SDLX_CONFIG)) - VIDEO_CCDEFS += -DHAVE_LIBSDL -DUSE_SIM_VIDEO `$(SDLX_CONFIG) --cflags` - VIDEO_LDFLAGS += `$(SDLX_CONFIG) --libs` + VIDEO_CCDEFS += -DHAVE_LIBSDL -DUSE_SIM_VIDEO $(shell $(SDLX_CONFIG) --cflags) + VIDEO_LDFLAGS += $(shell $(SDLX_CONFIG) --libs) VIDEO_FEATURES = - video capabilities provided by libSDL2 (Simple Directmedia Layer) DISPLAYL = ${DISPLAYD}/display.c $(DISPLAYD)/sim_ws.c DISPLAYVT = ${DISPLAYD}/vt11.c DISPLAY340 = ${DISPLAYD}/type340.c DISPLAYNG = ${DISPLAYD}/ng.c DISPLAYIII = ${DISPLAYD}/iii.c - DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(VIDEO_LDFLAGS) + DISPLAY_OPT += -DUSE_DISPLAY $(VIDEO_CCDEFS) $(info using libSDL2: $(call find_include,SDL2/SDL)) ifeq (Darwin,$(OSTYPE)) VIDEO_CCDEFS += -DSDL_MAIN_AVAILABLE endif - ifneq (,$(and $(BESM6_BUILD), $(or $(and $(call find_include,SDL2/SDL_ttf),$(call find_lib,SDL2_ttf)), $(and $(call find_include,SDL/SDL_ttf),$(call find_lib,SDL_ttf))))) - FONTPATH += /usr/share/fonts /Library/Fonts /usr/lib/jvm /System/Library/Frameworks/JavaVM.framework/Versions /System/Library/Fonts C:/Windows/Fonts - FONTPATH := $(dir $(foreach dir,$(strip $(FONTPATH)),$(wildcard $(dir)/.))) - FONTNAME += DejaVuSans.ttf LucidaSansRegular.ttf FreeSans.ttf AppleGothic.ttf tahoma.ttf - $(info font paths are: $(FONTPATH)) - $(info font names are: $(FONTNAME)) - find_fontfile = $(strip $(firstword $(foreach dir,$(strip $(FONTPATH)),$(wildcard $(dir)/$(1))$(wildcard $(dir)/*/$(1))$(wildcard $(dir)/*/*/$(1))$(wildcard $(dir)/*/*/*/$(1))))) - find_font = $(abspath $(strip $(firstword $(foreach font,$(strip $(FONTNAME)),$(call find_fontfile,$(font)))))) - ifneq (,$(call find_font)) - FONTFILE=$(call find_font) - else - $(info ***) - $(info *** No font file available, BESM-6 video panel disabled.) - $(info ***) - $(info *** To enable the panel display please specify one of:) - $(info *** a font path with FONTPATH=path) - $(info *** a font name with FONTNAME=fontname.ttf) - $(info *** a font file with FONTFILE=path/fontname.ttf) - $(info ***) + ## Don't search for SDL2_ttf unless BESM6 is actually being built! + ifneq (,$(BESM6_BUILD)) + BESM6_PANEL_OPT = $(filter-out -DSDL_MAIN_AVAILABLE,${VIDEO_CCDEFS}) + SDL2_TTF_CFLAGS = $(call find_include,SDL2/SDL_ttf) + SDL2_TTF_LDFLAGS = -lSDL2_ttf + ifeq (,$(and ${SDL2_TTF_CFLAGS},${SDL2_TTF_LDFLAGS})) + SDL2_TTF_CFLAGS = $(call find_include,SDL/SDL_ttf) + SDL2_TTF_LDFLAGS = -lSDL2_ttf endif - endif - ifeq (,$(and ${VIDEO_LDFLAGS}, ${FONTFILE}, $(BESM6_BUILD))) - $(info *** No SDL ttf support available. BESM-6 video panel disabled.) - $(info ***) - ifeq (Darwin,$(OSTYPE)) - ifeq (/opt/local/bin/port,$(shell which port)) - $(info *** Info *** Install the MacPorts libSDL2-ttf development package to provide this) - $(info *** Info *** functionality for your OS X system:) - $(info *** Info *** # port install libsdl2-ttf-dev) + ifneq (,$(and ${SDL2_TTF_CFLAGS},${SDL2_TTF_LDFLAGS})) + $(info using libSDL2_ttf: ${SDL2_TTF_LDFLAGS}, ${SDL2_TTF_CFLAGS}) + FONTPATH += /usr/share/fonts /Library/Fonts /usr/lib/jvm /System/Library/Frameworks/JavaVM.framework/Versions \ + /System/Library/Fonts C:/Windows/Fonts + FONTPATH := $(dir $(foreach dir,$(strip $(FONTPATH)),$(wildcard $(dir)/.))) + FONTNAME += DejaVuSans.ttf LucidaSansRegular.ttf FreeSans.ttf AppleGothic.ttf tahoma.ttf + $(info font paths are: $(FONTPATH)) + $(info font names are: $(FONTNAME)) + find_fontfile = $(strip $(firstword $(foreach dir,$(strip $(FONTPATH)),$(wildcard $(dir)/$(1))$(wildcard $(dir)/*/$(1))$(wildcard $(dir)/*/*/$(1))$(wildcard $(dir)/*/*/*/$(1))))) + find_font = $(abspath $(strip $(firstword $(foreach font,$(strip $(FONTNAME)),$(call find_fontfile,$(font)))))) + ifneq (,$(call find_font)) + FONTFILE=$(call find_font) + $(info Font file: ${FONTFILE}) + ## Presumably, searching for SDL2 already added the correct include path, + ## this is just insurance. + BESM6_PANEL_OPT += -DFONTFILE=${FONTFILE} -I $(subst SDL_ttf.h,,${SDL2_TTF_CFLAGS}) + BESM6_PANEL_LDFLAGS = ${SDL2_TTF_LDFLAGS} + else + $(info ***) + $(info *** No font file available, BESM-6 video panel disabled.) + $(info ***) + $(info *** To enable the panel display please specify one of:) + $(info *** a font path with FONTPATH=path) + $(info *** a font name with FONTNAME=fontname.ttf) + $(info *** a font file with FONTFILE=path/fontname.ttf) + $(info ***) endif - ifeq (/usr/local/bin/brew,$(shell which brew)) + endif + ifeq (,$(and ${SDL2_TTF_LDFLAGS}, ${FONTFILE})) + $(info ***) + $(info *** No SDL ttf support available. BESM-6 video panel disabled.) + $(info ***) + ifeq (Darwin,$(OSTYPE)) ifeq (/opt/local/bin/port,$(shell which port)) - $(info *** Info ***) - $(info *** Info *** OR) - $(info *** Info ***) + $(info *** Info *** Install the MacPorts libSDL2-ttf development package to provide this) + $(info *** Info *** functionality for your OS X system:) + $(info *** Info *** # port install libsdl2-ttf-dev) + endif + ifeq (/usr/local/bin/brew,$(shell which brew)) + ifeq (/opt/local/bin/port,$(shell which port)) + $(info *** Info ***) + $(info *** Info *** OR) + $(info *** Info ***) + endif + $(info *** Info *** Install the HomeBrew sdl2_ttf package to provide this) + $(info *** Info *** functionality for your OS X system:) + $(info *** Info *** $$ brew install sdl2_ttf) endif - $(info *** Info *** Install the HomeBrew sdl2_ttf package to provide this) - $(info *** Info *** functionality for your OS X system:) - $(info *** Info *** $$ brew install sdl2_ttf) - endif - else - ifneq (,$(and $(findstring Linux,$(OSTYPE)),$(call find_exe,apt-get))) - $(info *** Info *** Install the development components of libSDL2-ttf) - $(info *** Info *** packaged for your Linux operating system distribution:) - $(info *** Info *** $$ sudo apt-get install libsdl2-ttf-dev) else - $(info *** Info *** Install the development components of libSDL2-ttf packaged by your) - $(info *** Info *** operating system distribution and rebuild your simulator to) - $(info *** Info *** enable this extra functionality.) + ifneq (,$(and $(findstring Linux,$(OSTYPE)),$(call find_exe,apt-get))) + $(info *** Info *** Install the development components of libSDL2-ttf) + $(info *** Info *** packaged for your Linux operating system distribution:) + $(info *** Info *** $$ sudo apt-get install libsdl2-ttf-dev) + else + $(info *** Info *** Install the development components of libSDL2-ttf packaged by your) + $(info *** Info *** operating system distribution and rebuild your simulator to) + $(info *** Info *** enable this extra functionality.) + endif endif endif - else - ifneq (,$(call find_include,SDL2/SDL_ttf),$(call find_lib,SDL2_ttf)) - $(info using libSDL2_ttf: $(call find_lib,SDL2_ttf) $(call find_include,SDL2/SDL_ttf)) - $(info ***) - BESM6_PANEL_OPT = -DFONTFILE=${FONTFILE} $(filter-out -DSDL_MAIN_AVAILABLE,${VIDEO_CCDEFS}) ${VIDEO_LDFLAGS} -lSDL2_ttf - endif endif endif endif @@ -970,6 +994,17 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) $(info *** Warning ***) endif endif + # poll() vs. select(): + sim_use_select=0 + sim_use_poll=0 + ifneq (,$(call find_include,poll)) + $(info poll()-ing for sockets) + sim_use_poll=1 + else + $(info select()-ing for sockets) + sim_use_select=1 + endif + OS_CCDEFS += -DSIM_USE_SELECT=${sim_use_select} -DSIM_USE_POLL=${sim_use_poll} # Consider other network connections ifneq (,$(call find_lib,vdeplug)) # libvdeplug requires the use of the OS provided libpcap @@ -1059,9 +1094,16 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) NETWORK_CCDEFS += -DUSE_NETWORK endif endif - ifeq (slirp,$(shell if ${TEST} -e slirp_glue/sim_slirp.c; then echo slirp; fi)) - NETWORK_CCDEFS += -Islirp -Islirp_glue -Islirp_glue/qemu -DHAVE_SLIRP_NETWORK -DUSE_SIMH_SLIRP_DEBUG slirp/*.c slirp_glue/*.c + ifeq (libslirp,$(shell if ${TEST} -e ${LIBSLIRP_DIR}; then echo libslirp; fi)) + SLIRP_DEP=${BIN}libslirp.dir/libslirp.a + NETWORK_CCDEFS += -I${LIBSLIRP_SRC} -I${SIMHD}/libslirp/minimal -I${SIMHD}/sim_slirp -I${SIMHD}/sim_slirp/config \ + -DHAVE_SLIRP_NETWORK + NETWORK_CCDEFS += sim_slirp/sim_slirp.c sim_slirp/slirp_poll.c + NETWORK_LDFLAGS += ${BIN}libslirp.dir/libslirp.a NETWORK_LAN_FEATURES += NAT(SLiRP) + ifeq (Darwin,$(OSTYPE)) + OS_LDFLAGS += -lresolv + endif endif ifeq (,$(findstring USE_NETWORK,$(NETWORK_CCDEFS))$(findstring USE_SHARED,$(NETWORK_CCDEFS))$(findstring HAVE_VDE_NETWORK,$(NETWORK_CCDEFS))) NETWORK_CCDEFS += -DUSE_NETWORK @@ -1189,7 +1231,7 @@ else endif endif OS_CCDEFS += -fms-extensions $(PTHREADS_CCDEFS) - OS_LDFLAGS += -lm -lwsock32 -lwinmm $(PTHREADS_LDFLAGS) + SYS_LDFLAGS += -lm -lwsock32 -lwinmm $(PTHREADS_LDFLAGS) EXE = .exe ifneq (clean,${MAKECMDGOALS}) ifneq (buildtoolsexists,$(shell if exist BIN\buildtools (echo buildtoolsexists) else (mkdir BIN\buildtools))) @@ -1301,8 +1343,12 @@ else OS_LDFLAGS += -lpcre -L../windows-build/PCRE/lib/ $(info using libpcre: $(abspath ../windows-build/PCRE/lib/pcre.a) $(abspath ../windows-build/PCRE/include/pcre.h)) endif - ifeq (slirp,slirp) - NETWORK_OPT += -Islirp -Islirp_glue -Islirp_glue/qemu -DHAVE_SLIRP_NETWORK -DUSE_SIMH_SLIRP_DEBUG slirp/*.c slirp_glue/*.c -lIphlpapi + ifeq (libslirp,$(shell if ${TEST} -e ${LIBSLIRP_DIR}; then echo libslirp; fi)) + SLIRP_DEP=libslirp-dep + NETWORK_CCDEFS += -I${LIBSLIRP_SRC} -I${SIMHD}/libslirp/minimal -I${SIMHD}/sim_slirp -I${SIMHD}/sim_slirp/config \ + -DHAVE_SLIRP_NETWORK + NETWORK_CCDEFS += sim_slirp/sim_slirp.c sim_slirp/slirp_poll.c + NETWORK_LDFLAGS += ${BIN}libslirp.dir/libslirp.a NETWORK_LAN_FEATURES += NAT(SLiRP) endif endif @@ -1418,23 +1464,37 @@ CC := ${GCC} ${CC_STD} -U__STRICT_ANSI__ ${CFLAGS_G} ${CFLAGS_O} ${CFLAGS_GIT} $ ifneq (,${SIM_VERSION_MODE}) CC += -DSIM_VERSION_MODE="${SIM_VERSION_MODE}" endif -LDFLAGS := ${OS_LDFLAGS} ${NETWORK_LDFLAGS} ${LDFLAGS_O} +LDFLAGS := $(strip ${OS_LDFLAGS} ${LDFLAGS_O} ${SYS_LDFLAGS}) # # Common Libraries # -BIN = BIN/ -SIMHD = . SIM = ${SIMHD}/scp.c ${SIMHD}/sim_console.c ${SIMHD}/sim_fio.c \ ${SIMHD}/sim_timer.c ${SIMHD}/sim_sock.c ${SIMHD}/sim_tmxr.c \ ${SIMHD}/sim_ether.c ${SIMHD}/sim_tape.c ${SIMHD}/sim_disk.c \ ${SIMHD}/sim_serial.c ${SIMHD}/sim_video.c ${SIMHD}/sim_imd.c \ - ${SIMHD}/sim_card.c + ${SIMHD}/sim_card.c ${SIMHD}/sim_debtab.c DISPLAYD = ${SIMHD}/display SCSI = ${SIMHD}/sim_scsi.c +## libslirp sources for libslirp.a +LIBSLIRP_SOURCES=${LIBSLIRP_SRC}/arp_table.c ${LIBSLIRP_SRC}/bootp.c ${LIBSLIRP_SRC}/cksum.c \ + ${LIBSLIRP_SRC}/dhcpv6.c ${LIBSLIRP_SRC}/dnssearch.c ${LIBSLIRP_SRC}/if.c \ + ${LIBSLIRP_SRC}/ip6_icmp.c ${LIBSLIRP_SRC}/ip6_input.c ${LIBSLIRP_SRC}/ip6_output.c \ + ${LIBSLIRP_SRC}/ip_icmp.c ${LIBSLIRP_SRC}/ip_input.c ${LIBSLIRP_SRC}/ip_output.c \ + ${LIBSLIRP_SRC}/mbuf.c ${LIBSLIRP_SRC}/misc.c ${LIBSLIRP_SRC}/ncsi.c ${LIBSLIRP_SRC}/ndp_table.c \ + ${LIBSLIRP_SRC}/sbuf.c ${LIBSLIRP_SRC}/slirp.c ${LIBSLIRP_SRC}/socket.c ${LIBSLIRP_SRC}/state.c \ + ${LIBSLIRP_SRC}/stream.c ${LIBSLIRP_SRC}/tcp_input.c ${LIBSLIRP_SRC}/tcp_output.c \ + ${LIBSLIRP_SRC}/tcp_subr.c ${LIBSLIRP_SRC}/tcp_timer.c ${LIBSLIRP_SRC}/tftp.c ${LIBSLIRP_SRC}/udp.c \ + ${LIBSLIRP_SRC}/udp6.c ${LIBSLIRP_SRC}/util.c ${LIBSLIRP_SRC}/version.c ${LIBSLIRP_SRC}/vmstate.c \ + +LIBSLIRP_STUB_SRC=${LIBSLIRP_DIR}/minimal/glib-stubs.c + +LIBSLIRP_OBJS=${subst ${LIBSLIRP_SRC},${BIN}libslirp.dir,${LIBSLIRP_SOURCES:.c=.o}} \ + ${subst ${LIBSLIRP_DIR}/minimal,${BIN}libslirp.dir,${LIBSLIRP_STUB_SRC:.c=.o}} + # # Emulator source files and compile time options # @@ -1444,6 +1504,7 @@ PDP1 = ${PDP1D}/pdp1_lp.c ${PDP1D}/pdp1_cpu.c ${PDP1D}/pdp1_stddev.c \ ${PDP1D}/pdp1_sys.c ${PDP1D}/pdp1_dt.c ${PDP1D}/pdp1_drm.c \ ${PDP1D}/pdp1_clk.c ${PDP1D}/pdp1_dcs.c ${PDP1D}/pdp1_dpy.c ${DISPLAYL} PDP1_OPT = -I ${PDP1D} ${DISPLAY_OPT} $(PDP1_DISPLAY_OPT) +PDP1_LDFLAGS = ${VIDEO_LDFLAGS} ND100D = ${SIMHD}/ND100 @@ -1475,10 +1536,12 @@ PDP18B = ${PDP18BD}/pdp18b_dt.c ${PDP18BD}/pdp18b_drm.c ${PDP18BD}/pdp18b_cpu.c ifneq (,${DISPLAY_OPT}) PDP7_DISPLAY_OPT = -DDISPLAY_TYPE=DIS_TYPE30 -DPIX_SCALE=RES_HALF + PDP7_DISPLAY_LDFLAGS = ${VIDEO_LDFLAGS} endif PDP4_OPT = -DPDP4 -I ${PDP18BD} PDP7_OPT = -DPDP7 -I ${PDP18BD} ${DISPLAY_OPT} $(PDP7_DISPLAY_OPT) +PDP7_LDFLAGS = ${PDP7_DISPLAY_LDFLAGS} PDP9_OPT = -DPDP9 -I ${PDP18BD} PDP15_OPT = -DPDP15 -I ${PDP18BD} @@ -1503,6 +1566,7 @@ PDP11 = ${PDP11D}/pdp11_fp.c ${PDP11D}/pdp11_cpu.c ${PDP11D}/pdp11_dz.c \ ${PDP11D}/pdp11_mb.c ${PDP11D}/pdp11_rr.c \ ${DISPLAYL} ${DISPLAYNG} ${DISPLAYVT} PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} ${DISPLAY_OPT} ${AIO_CCDEFS} +PDP11_LDFLAGS = ${VIDEO_LDFLAGS} ${NETWORK_LDFLAGS} UC15D = ${SIMHD}/PDP11 @@ -1527,6 +1591,7 @@ VAX = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c ${VAXD}/vax_io.c \ ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_dup.c VAX_OPT = -DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} ${AIO_CCDEFS} +VAX_LDFLAGS = ${NETWORK_LDFLAGS} VAX410 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1538,6 +1603,7 @@ VAX410 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax4xx_va.c ${VAXD}/vax4xx_vc.c ${VAXD}/vax_lk.c \ ${VAXD}/vax_vs.c ${VAXD}/vax_gpx.c VAX410_OPT = -DVM_VAX -DVAX_410 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} ${AIO_CCDEFS} +VAX410_LDFLAGS = ${NETWORK_LDFLAGS} VAX420 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1548,8 +1614,9 @@ VAX420 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax4xx_rd.c ${VAXD}/vax4xx_rz80.c ${VAXD}/vax_xs.c \ ${VAXD}/vax4xx_va.c ${VAXD}/vax4xx_vc.c ${VAXD}/vax4xx_ve.c \ ${VAXD}/vax_lk.c ${VAXD}/vax_vs.c ${VAXD}/vax_gpx.c -VAX420_OPT = -DVM_VAX -DVAX_420 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} \ - ${AIO_CCDEFS} +VAX420_OPT = -DVM_VAX -DVAX_420 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} \ + ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} ${AIO_CCDEFS} +VAX420_LDFLAGS = ${NETWORK_LDFLAGS} VAX411_OPT = ${VAX420_OPT} -DVAX_411 VAX412_OPT = ${VAX420_OPT} -DVAX_412 VAX41A_OPT = ${VAX420_OPT} -DVAX_41A @@ -1566,6 +1633,7 @@ VAX43 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax4xx_rz80.c ${VAXD}/vax_xs.c ${VAXD}/vax4xx_vc.c \ ${VAXD}/vax4xx_ve.c ${VAXD}/vax_lk.c ${VAXD}/vax_vs.c VAX43_OPT = -DVM_VAX -DVAX_43 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} ${AIO_CCDEFS} +VAX43_LDFLAGS = ${NETWORK_LDFLAGS} VAX440 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1575,6 +1643,7 @@ VAX440 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax440_sysdev.c ${VAXD}/vax440_syslist.c ${VAXD}/vax4xx_dz.c \ ${VAXD}/vax_xs.c ${VAXD}/vax_lk.c ${VAXD}/vax_vs.c ${VAXD}/vax4xx_rz94.c VAX440_OPT = -DVM_VAX -DVAX_440 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX440_LDFLAGS = ${NETWORK_LDFLAGS} VAX46_OPT = ${VAX440_OPT} -DVAX_46 VAX47_OPT = ${VAX440_OPT} -DVAX_47 VAX48_OPT = ${VAX440_OPT} -DVAX_48 @@ -1587,6 +1656,7 @@ IS1000 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${VAXD}/vax4xx_rz94.c ${VAXD}/vax4nn_stddev.c \ ${VAXD}/is1000_sysdev.c ${VAXD}/is1000_syslist.c IS1000_OPT = -DVM_VAX -DIS_1000 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} ${NETWORK_OPT} ${AIO_CCDEFS} +IS1000_LDFLAGS = ${NETWORK_LDFLAGS} VAX610 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1599,8 +1669,9 @@ VAX610 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_io_lib.c -VAX610_OPT = -DVM_VAX -DVAX_610 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} \ - ${AIO_CCDEFS} +VAX610_OPT = -DVM_VAX -DVAX_610 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} \ + ${NETWORK_OPT} ${VIDEO_CCDEFS} ${AIO_CCDEFS} +VAX610_LDFLAGS = ${VIDEO_LDFLAGS} ${NETWORK_LDFLAGS} VAX630 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1615,8 +1686,10 @@ VAX630 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_dup.c VAX620_OPT = -DVM_VAX -DVAX_620 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} -VAX630_OPT = -DVM_VAX -DVAX_630 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${VIDEO_CCDEFS} ${VIDEO_LDFLAGS} \ - ${AIO_CCDEFS} +VAX620_LDFLAGS = ${NETWORK_LDFLAGS} +VAX630_OPT = -DVM_VAX -DVAX_630 -DUSE_INT64 -DUSE_ADDR64 -DUSE_SIM_VIDEO -I ${VAXD} -I ${PDP11D} \ + ${NETWORK_OPT} ${VIDEO_CCDEFS} ${AIO_CCDEFS} +VAX630_LDFLAGS = ${VIDEO_LDFLAGS} ${NETWORK_LDFLAGS} VAX730 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1632,6 +1705,7 @@ VAX730 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_tc.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_ch.c ${PDP11D}/pdp11_dup.c VAX730_OPT = -DVM_VAX -DVAX_730 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX730_LDFLAGS = ${NETWORK_LDFLAGS} VAX750 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1648,6 +1722,7 @@ VAX750 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_tc.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_ch.c VAX750_OPT = -DVM_VAX -DVAX_750 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX750_LDFLAGS = ${NETWORK_LDFLAGS} VAX780 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1664,6 +1739,7 @@ VAX780 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_tc.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_ch.c VAX780_OPT = -DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX780_LDFLAGS = ${NETWORK_LDFLAGS} VAX8200 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1679,6 +1755,7 @@ VAX8200 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_tc.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_ch.c ${PDP11D}/pdp11_dup.c VAX8200_OPT = -DVM_VAX -DVAX_820 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX8200_LDFLAGS = ${NETWORK_LDFLAGS} VAX8600 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ @@ -1695,6 +1772,7 @@ VAX8600 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ ${PDP11D}/pdp11_td.c ${PDP11D}/pdp11_tc.c ${PDP11D}/pdp11_rk.c \ ${PDP11D}/pdp11_io_lib.c ${PDP11D}/pdp11_ch.c VAX8600_OPT = -DVM_VAX -DVAX_860 -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +VAX8600_LDFLAGS = ${NETWORK_LDFLAGS} PDP10D = ${SIMHD}/PDP10 @@ -1705,7 +1783,8 @@ PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ ${PDP11D}/pdp11_dup.c ${PDP11D}/pdp11_dmc.c ${PDP11D}/pdp11_kmc.c \ ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ch.c -PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} +PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} ${AIO_CCDEFS} +PDP10_LDFLAGS = ${NETWORK_LDFLAGS} IMLACD = ${SIMHD}/imlac @@ -1714,6 +1793,7 @@ IMLAC = ${IMLACD}/imlac_sys.c ${IMLACD}/imlac_cpu.c \ ${IMLACD}/imlac_tty.c ${IMLACD}/imlac_pt.c ${IMLACD}/imlac_bel.c \ ${DISPLAYL} IMLAC_OPT = -I ${IMLACD} ${DISPLAY_OPT} ${AIO_CCDEFS} +IMLAC_LDFLAGS = ${VIDEO_LDFLAGS} STUBD = ${SIMHD}/stub @@ -1727,6 +1807,7 @@ TT2500 = ${TT2500D}/tt2500_sys.c ${TT2500D}/tt2500_cpu.c \ ${TT2500D}/tt2500_key.c ${TT2500D}/tt2500_uart.c ${TT2500D}/tt2500_rom.c \ ${DISPLAYL} TT2500_OPT = -I ${TT2500D} ${DISPLAY_OPT} ${AIO_CCDEFS} +TT2500_LDFLAGS = ${VIDEO_LDFLAGS} PDP8D = ${SIMHD}/PDP8 @@ -2022,6 +2103,7 @@ TX0D = ${SIMHD}/TX-0 TX0 = ${TX0D}/tx0_cpu.c ${TX0D}/tx0_dpy.c ${TX0D}/tx0_stddev.c \ ${TX0D}/tx0_sys.c ${TX0D}/tx0_sys_orig.c ${DISPLAYL} TX0_OPT = -I ${TX0D} ${DISPLAY_OPT} +TX0_LDFLAGS = ${VIDEO_LDFLAGS} SSEMD = ${SIMHD}/SSEM @@ -2041,6 +2123,7 @@ BESM6 = ${BESM6D}/besm6_cpu.c ${BESM6D}/besm6_sys.c ${BESM6D}/besm6_mmu.c \ ${BESM6D}/besm6_pl.c ${BESM6D}/besm6_mg.c \ ${BESM6D}/besm6_punch.c ${BESM6D}/besm6_punchcard.c ${BESM6D}/besm6_vu.c BESM6_OPT = -I ${BESM6D} -DUSE_INT64 $(BESM6_PANEL_OPT) +BESM6_LDFLAGS = ${BESM6_PANEL_LDFLAGS} ${VIDEO_LDFLAGS} PDP6D = ${SIMHD}/PDP10 ifneq (,${DISPLAY_OPT}) @@ -2054,6 +2137,7 @@ PDP6 = ${PDP6D}/kx10_cpu.c ${PDP6D}/kx10_sys.c ${PDP6D}/kx10_cty.c \ ${DISPLAYL} ${DISPLAY340} PDP6_OPT = -DPDP6=1 -DUSE_INT64 -I ${PDP6D} -DUSE_SIM_CARD ${DISPLAY_OPT} ${PDP6_DISPLAY_OPT} \ ${AIO_CCDEFS} +PDP6_LDFLAGS = ${VIDEO_LDFLAGS} KA10D = ${SIMHD}/PDP10 ifneq (,${DISPLAY_OPT}) @@ -2075,6 +2159,7 @@ KA10 = ${KA10D}/kx10_cpu.c ${KA10D}/kx10_sys.c ${KA10D}/kx10_df.c \ ${KA10D}/ka10_pclk.c ${KA10D}/ka10_tv.c ${KA10D}/ka10_dd.c \ ${KA10D}/kx10_ddc.c ${DISPLAYL} ${DISPLAY340} ${DISPLAYIII} KA10_OPT = -DKA=1 -DUSE_INT64 -I ${KA10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KA10_DISPLAY_OPT} ${AIO_CCDEFS} +KA10_LDFLAGS = ${VIDEO_LDFLAGS} ${NETWORK_LDFLAGS} ifneq (${PANDA_LIGHTS},) # ONLY for Panda display. KA10_OPT += -DPANDA_LIGHTS @@ -2095,6 +2180,7 @@ KI10 = ${KI10D}/kx10_cpu.c ${KI10D}/kx10_sys.c ${KI10D}/kx10_df.c \ ${KI10D}/kx10_imp.c ${KI10D}/kx10_dpy.c ${KI10D}/kx10_disk.c \ ${KI10D}/kx10_ddc.c ${KI10D}/kx10_tym.c ${DISPLAYL} ${DISPLAY340} KI10_OPT = -DKI=1 -DUSE_INT64 -I ${KI10D} -DUSE_SIM_CARD ${NETWORK_OPT} ${DISPLAY_OPT} ${KI10_DISPLAY_OPT} ${AIO_CCDEFS} +KI10_LDFLAGS = ${VIDEO_LDFLAGS} ${NETWORK_LDFLAGS} ifneq (${PANDA_LIGHTS},) # ONLY for Panda display. KI10_OPT += -DPANDA_LIGHTS @@ -2110,7 +2196,8 @@ KL10 = ${KL10D}/kx10_cpu.c ${KL10D}/kx10_sys.c ${KL10D}/kx10_df.c \ ${KL10D}/kx10_rp.c ${KL10D}/kx10_tu.c ${KL10D}/kx10_rs.c \ ${KL10D}/kx10_imp.c ${KL10D}/kl10_fe.c ${KL10D}/ka10_pd.c \ ${KL10D}/ka10_ch10.c ${KL10D}/kl10_nia.c ${KL10D}/kx10_disk.c -KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) -DUSE_SIM_CARD ${NETWORK_OPT} ${AIO_CCDEFS} +KL10_OPT = -DKL=1 -DUSE_INT64 -I $(KL10D) -DUSE_SIM_CARD ${NETWORK_OPT} ${AIO_CCDEFS} +KL10_LDFLAGS = ${NETWORK_LDFLAGS} KS10D = ${SIMHD}/PDP10 KS10 = ${KS10D}/kx10_cpu.c ${KS10D}/kx10_sys.c ${KS10D}/kx10_disk.c \ @@ -2119,6 +2206,7 @@ KS10 = ${KS10D}/kx10_cpu.c ${KS10D}/kx10_sys.c ${KS10D}/kx10_disk.c \ ${KS10D}/ks10_tcu.c ${KS10D}/ks10_lp.c ${KS10D}/ks10_ch11.c \ ${KS10D}/ks10_kmc.c ${KS10D}/ks10_dup.c ${KS10D}/kx10_imp.c KS10_OPT = -DKS=1 -DUSE_INT64 -I $(KS10D) -I $(PDP11D) ${NETWORK_OPT} ${AIO_CCDEFS} +KS10_LDFLAGS = ${NETWORK_LDFLAGS} ATT3B2D = ${SIMHD}/3B2 ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ @@ -2131,6 +2219,7 @@ ATT3B2M400 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ ${ATT3B2D}/3b2_ports.c ${ATT3B2D}/3b2_ctc.c \ ${ATT3B2D}/3b2_ni.c ATT3B2M400_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV2 -I ${ATT3B2D} ${NETWORK_OPT} ${AIO_CCDEFS} +ATT3B2M400_LDFLAGS = ${NETWORK_LDFLAGS} ATT3B2M700 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ ${ATT3B2D}/3b2_rev3_sys.c ${ATT3B2D}/3b2_rev3_mmu.c \ @@ -2141,6 +2230,7 @@ ATT3B2M700 = ${ATT3B2D}/3b2_cpu.c ${ATT3B2D}/3b2_sys.c \ ${ATT3B2D}/3b2_io.c ${ATT3B2D}/3b2_ports.c \ ${ATT3B2D}/3b2_scsi.c ${ATT3B2D}/3b2_ni.c ATT3B2M700_OPT = -DUSE_INT64 -DUSE_ADDR64 -DREV3 -I ${ATT3B2D} ${NETWORK_OPT} ${AIO_CCDEFS} +ATT3B2M700_LDFLAGS = ${NETWORK_LDFLAGS} SIGMAD = ${SIMHD}/sigma SIGMA = ${SIGMAD}/sigma_cpu.c ${SIGMAD}/sigma_sys.c ${SIGMAD}/sigma_cis.c \ @@ -2159,6 +2249,7 @@ SEL32 = ${SEL32D}/sel32_cpu.c ${SEL32D}/sel32_sys.c ${SEL32D}/sel32_chan.c \ ${SEL32D}/sel32_hsdp.c ${SEL32D}/sel32_mfp.c ${SEL32D}/sel32_scsi.c \ ${SEL32D}/sel32_ec.c ${SEL32D}/sel32_ipu.c SEL32_OPT = -I $(SEL32D) -DUSE_INT32 -DSEL32 ${NETWORK_OPT} +SEL32_LDFLAGS = ${NETWORK_LDFLAGS} ### ### Experimental simulators @@ -2222,9 +2313,10 @@ EXPERIMENTAL = alpha pdq3 sage experimental : ${EXPERIMENTAL} -clean : +clean :: ifeq (${WIN32},) - ${RM} -rf ${BIN} + ${RM} -rf ${SIMHD}/sim_slirp/config/glib-endian.h ${SIMHD}/sim_slirp/config/libslirp-version.h \ + ${SIMHD}/sim_slirp/config/mk-glib-endian ${BIN} else if exist BIN rmdir /s /q BIN endif @@ -2245,7 +2337,7 @@ pdp1 : ${BIN}pdp1${EXE} ${BIN}pdp1${EXE} : ${PDP1} ${SIM} ${MKDIRBIN} - ${CC} ${PDP1} ${SIM} ${PDP1_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${PDP1} ${SIM} ${PDP1_OPT} ${CC_OUTSPEC} ${PDP1_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP1D},pdp1)) $@ $(call find_test,${PDP1D},pdp1) ${TEST_ARG} endif @@ -2263,7 +2355,7 @@ pdp7 : ${BIN}pdp7${EXE} ${BIN}pdp7${EXE} : ${PDP18B} ${PDP18BD}/pdp18b_dpy.c ${DISPLAYL} ${DISPLAY340} ${SIM} ${MKDIRBIN} - ${CC} ${PDP18B} ${PDP18BD}/pdp18b_dpy.c ${DISPLAYL} ${DISPLAY340} ${SIM} ${PDP7_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${PDP18B} ${PDP18BD}/pdp18b_dpy.c ${DISPLAYL} ${DISPLAY340} ${SIM} ${PDP7_OPT} ${CC_OUTSPEC} ${PDP7_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP18BD},pdp7)) $@ $(call find_test,${PDP18BD},pdp7) ${TEST_ARG} endif @@ -2297,9 +2389,9 @@ endif pdp10 : ${BIN}pdp10${EXE} -${BIN}pdp10${EXE} : ${PDP10} ${SIM} +${BIN}pdp10${EXE} : ${PDP10} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${PDP10} ${SIM} ${PDP10_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${PDP10} ${SIM} ${PDP10_OPT} ${CC_OUTSPEC} ${PDP10_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},pdp10)) $@ $(call find_test,${PDP10D},pdp10) ${TEST_ARG} endif @@ -2308,7 +2400,7 @@ imlac : ${BIN}imlac${EXE} ${BIN}imlac${EXE} : ${IMLAC} ${SIM} ${MKDIRBIN} - ${CC} ${IMLAC} ${SIM} ${IMLAC_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${IMLAC} ${SIM} ${IMLAC_OPT} ${CC_OUTSPEC} ${IMLAC_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${IMLAC},imlac)) $@ $(call find_test,${IMLACD},imlac) ${TEST_ARG} endif @@ -2323,16 +2415,16 @@ tt2500 : ${BIN}tt2500${EXE} ${BIN}tt2500${EXE} : ${TT2500} ${SIM} ${MKDIRBIN} - ${CC} ${TT2500} ${SIM} ${TT2500_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${TT2500} ${SIM} ${TT2500_OPT} ${CC_OUTSPEC} ${TT2500_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${TT2500},tt2500)) $@ $(call find_test,${TT2500D},tt2500) ${TEST_ARG} endif pdp11 : ${BIN}pdp11${EXE} -${BIN}pdp11${EXE} : ${PDP11} ${SIM} ${BUILD_ROMS} +${BIN}pdp11${EXE} : ${PDP11} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${PDP11} ${SIM} ${PDP11_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${PDP11} ${SIM} ${PDP11_OPT} ${CC_OUTSPEC} ${PDP11_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP11D},pdp11)) $@ $(call find_test,${PDP11D},pdp11) ${TEST_ARG} endif @@ -2350,9 +2442,9 @@ microvax3900 : vax vax : ${BIN}vax${EXE} -${BIN}vax${EXE} : ${VAX} ${SIM} ${BUILD_ROMS} +${BIN}vax${EXE} : ${VAX} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX} ${SIM} ${VAX_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${VAX} ${SIM} ${VAX_OPT} ${CC_OUTSPEC} ${VAX_LDFLAGS} ${LDFLAGS} ifeq (${WIN32},) cp ${BIN}vax${EXE} ${BIN}microvax3900${EXE} else @@ -2364,180 +2456,180 @@ endif microvax2000 : ${BIN}microvax2000${EXE} -${BIN}microvax2000${EXE} : ${VAX410} ${SIM} ${BUILD_ROMS} +${BIN}microvax2000${EXE} : ${VAX410} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX410} ${SCSI} ${SIM} ${VAX410_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX410} ${SCSI} ${SIM} ${VAX410_OPT} -o $@ ${VAX410_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif infoserver100 : ${BIN}infoserver100${EXE} -${BIN}infoserver100${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}infoserver100${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX411_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX411_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif infoserver150vxt : ${BIN}infoserver150vxt${EXE} -${BIN}infoserver150vxt${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}infoserver150vxt${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX412_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX412_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif microvax3100 : ${BIN}microvax3100${EXE} -${BIN}microvax3100${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}microvax3100${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX41A_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX41A_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif microvax3100e : ${BIN}microvax3100e${EXE} -${BIN}microvax3100e${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}microvax3100e${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX41D_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX41D_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vaxstation3100m30 : ${BIN}vaxstation3100m30${EXE} -${BIN}vaxstation3100m30${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}vaxstation3100m30${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX42A_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX42A_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vaxstation3100m38 : ${BIN}vaxstation3100m38${EXE} -${BIN}vaxstation3100m38${EXE} : ${VAX420} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}vaxstation3100m38${EXE} : ${VAX420} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX42B_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX420} ${SCSI} ${SIM} ${VAX42B_OPT} -o $@ ${VAX420_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vaxstation3100m76 : ${BIN}vaxstation3100m76${EXE} -${BIN}vaxstation3100m76${EXE} : ${VAX43} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}vaxstation3100m76${EXE} : ${VAX43} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX43} ${SCSI} ${SIM} ${VAX43_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX43} ${SCSI} ${SIM} ${VAX43_OPT} -o $@ ${VAX43_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vaxstation4000m60 : ${BIN}vaxstation4000m60${EXE} -${BIN}vaxstation4000m60${EXE} : ${VAX440} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}vaxstation4000m60${EXE} : ${VAX440} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX46_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX46_OPT} -o $@ ${VAX440_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif microvax3100m80 : ${BIN}microvax3100m80${EXE} -${BIN}microvax3100m80${EXE} : ${VAX440} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}microvax3100m80${EXE} : ${VAX440} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX47_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX47_OPT} -o $@ ${VAX440_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vaxstation4000vlc : ${BIN}vaxstation4000vlc${EXE} -${BIN}vaxstation4000vlc${EXE} : ${VAX440} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}vaxstation4000vlc${EXE} : ${VAX440} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX48_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX440} ${SCSI} ${SIM} ${VAX48_OPT} -o $@ ${VAX440_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif infoserver1000 : ${BIN}infoserver1000${EXE} -${BIN}infoserver1000${EXE} : ${IS1000} ${SCSI} ${SIM} ${BUILD_ROMS} +${BIN}infoserver1000${EXE} : ${IS1000} ${SCSI} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${IS1000} ${SCSI} ${SIM} ${IS1000_OPT} -o $@ ${LDFLAGS} + ${CC} ${IS1000} ${SCSI} ${SIM} ${IS1000_OPT} -o $@ ${IS1000_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif microvax1 : ${BIN}microvax1${EXE} -${BIN}microvax1${EXE} : ${VAX610} ${SIM} ${BUILD_ROMS} +${BIN}microvax1${EXE} : ${VAX610} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX610} ${SIM} ${VAX610_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX610} ${SIM} ${VAX610_OPT} -o $@ ${VAX610_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif rtvax1000 : ${BIN}rtvax1000${EXE} -${BIN}rtvax1000${EXE} : ${VAX630} ${SIM} ${BUILD_ROMS} +${BIN}rtvax1000${EXE} : ${VAX630} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX630} ${SIM} ${VAX620_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX630} ${SIM} ${VAX620_OPT} -o $@ ${VAX620_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif microvax2 : ${BIN}microvax2${EXE} -${BIN}microvax2${EXE} : ${VAX630} ${SIM} ${BUILD_ROMS} +${BIN}microvax2${EXE} : ${VAX630} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX630} ${SIM} ${VAX630_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX630} ${SIM} ${VAX630_OPT} -o $@ ${VAX630_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vax730 : ${BIN}vax730${EXE} -${BIN}vax730${EXE} : ${VAX730} ${SIM} ${BUILD_ROMS} +${BIN}vax730${EXE} : ${VAX730} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX730} ${SIM} ${VAX730_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX730} ${SIM} ${VAX730_OPT} -o $@ ${VAX730_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vax750 : ${BIN}vax750${EXE} -${BIN}vax750${EXE} : ${VAX750} ${SIM} ${BUILD_ROMS} +${BIN}vax750${EXE} : ${VAX750} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX750} ${SIM} ${VAX750_OPT} -o $@ ${LDFLAGS} + ${CC} ${VAX750} ${SIM} ${VAX750_OPT} -o $@ ${VAX750_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vax780 : ${BIN}vax780${EXE} -${BIN}vax780${EXE} : ${VAX780} ${SIM} ${BUILD_ROMS} +${BIN}vax780${EXE} : ${VAX780} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX780} ${SIM} ${VAX780_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${VAX780} ${SIM} ${VAX780_OPT} ${CC_OUTSPEC} ${VAX780_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vax8200 : ${BIN}vax8200${EXE} -${BIN}vax8200${EXE} : ${VAX8200} ${SIM} ${BUILD_ROMS} +${BIN}vax8200${EXE} : ${VAX8200} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX8200} ${SIM} ${VAX8200_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${VAX8200} ${SIM} ${VAX8200_OPT} ${CC_OUTSPEC} ${VAX780_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif vax8600 : ${BIN}vax8600${EXE} -${BIN}vax8600${EXE} : ${VAX8600} ${SIM} ${BUILD_ROMS} +${BIN}vax8600${EXE} : ${VAX8600} ${SIM} ${SLIRP_DEP} ${BUILD_ROMS} ${MKDIRBIN} - ${CC} ${VAX8600} ${SIM} ${VAX8600_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${VAX8600} ${SIM} ${VAX8600_OPT} ${CC_OUTSPEC} ${VAX8600_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${VAXD},vax-diag)) $@ $(call find_test,${VAXD},vax-diag) ${TEST_ARG} endif @@ -2661,9 +2753,9 @@ endif sel32: ${BIN}sel32${EXE} -${BIN}sel32${EXE}: ${SEL32} ${SIM} +${BIN}sel32${EXE}: ${SEL32} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${SEL32} ${SIM} ${SEL32_OPT} $(CC_OUTSPEC) ${LDFLAGS} + ${CC} ${SEL32} ${SIM} ${SEL32_OPT} $(CC_OUTSPEC) ${SEL32_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${SEL32D},sel32)) $@ $(call find_test,${SEL32D},sel32) $(TEST_ARG) endif @@ -2791,7 +2883,7 @@ tx-0 : ${BIN}tx-0${EXE} ${BIN}tx-0${EXE} : ${TX0} ${SIM} ${MKDIRBIN} - ${CC} ${TX0} ${SIM} ${TX0_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${TX0} ${SIM} ${TX0_OPT} ${CC_OUTSPEC} ${TX0_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${TX0D},tx-0)) $@ $(call find_test,${TX0D},tx-0) ${TEST_ARG} endif @@ -2819,7 +2911,7 @@ besm6 : ${BIN}besm6${EXE} ${BIN}besm6${EXE} : ${BESM6} ${SIM} ifneq (1,${CPP_BUILD}${CPP_FORCE}) ${MKDIRBIN} - ${CC} ${BESM6} ${SIM} ${BESM6_OPT} ${BESM6_PANEL_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${BESM6} ${SIM} ${BESM6_OPT} ${BESM6_PANEL_OPT} ${CC_OUTSPEC} ${BESM6_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${BESM6D},besm6)) $@ $(call find_test,${BESM6D},besm6) ${TEST_ARG} endif @@ -2876,9 +2968,9 @@ endif 3b2 : ${BIN}3b2${EXE} -${BIN}3b2${EXE} : ${ATT3B2M400} ${SIM} +${BIN}3b2${EXE} : ${ATT3B2M400} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${ATT3B2M400} ${SIM} ${ATT3B2M400_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${ATT3B2M400} ${SIM} ${ATT3B2M400_OPT} ${CC_OUTSPEC} ${ATT3B2M400_LDFLAGS} ${LDFLAGS} ifeq (${WIN32},) cp ${BIN}3b2${EXE} ${BIN}3b2-400${EXE} else @@ -2890,9 +2982,9 @@ endif 3b2-700 : ${BIN}3b2-700${EXE} -${BIN}3b2-700${EXE} : ${ATT3B2M700} ${SIM} +${BIN}3b2-700${EXE} : ${ATT3B2M700} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${ATT3B2M700} ${SCSI} ${SIM} ${ATT3B2M700_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${ATT3B2M700} ${SCSI} ${SIM} ${ATT3B2M700_OPT} ${CC_OUTSPEC} ${ATT3B2M700_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${ATT3B2D},3b2-700)) $@ $(call find_test,${ATT3B2D},3b2-700) ${TEST_ARG} endif @@ -2964,43 +3056,43 @@ pdp6 : ${BIN}pdp6${EXE} ${BIN}pdp6${EXE} : ${PDP6} ${SIM} ${MKDIRBIN} - ${CC} ${PDP6} ${PDP6_DPY} ${SIM} ${PDP6_OPT} ${CC_OUTSPEC} ${LDFLAGS} ${PDP6_LDFLAGS} + ${CC} ${PDP6} ${PDP6_DPY} ${SIM} ${PDP6_OPT} ${CC_OUTSPEC} ${PDP6_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},pdp6)) $@ $(call find_test,${PDP10D},pdp6) ${TEST_ARG} endif pdp10-ka : ${BIN}pdp10-ka${EXE} -${BIN}pdp10-ka${EXE} : ${KA10} ${SIM} +${BIN}pdp10-ka${EXE} : ${KA10} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${KA10} ${KA10_DPY} ${SIM} ${KA10_OPT} ${CC_OUTSPEC} ${LDFLAGS} ${KA10_LDFLAGS} + ${CC} ${KA10} ${KA10_DPY} ${SIM} ${KA10_OPT} ${CC_OUTSPEC} ${KA10_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},ka10)) $@ $(call find_test,${PDP10D},ka10) ${TEST_ARG} endif pdp10-ki : ${BIN}pdp10-ki${EXE} -${BIN}pdp10-ki${EXE} : ${KI10} ${SIM} +${BIN}pdp10-ki${EXE} : ${KI10} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${KI10} ${KI10_DPY} ${SIM} ${KI10_OPT} ${CC_OUTSPEC} ${LDFLAGS} ${KI10_LDFLAGS} + ${CC} ${KI10} ${KI10_DPY} ${SIM} ${KI10_OPT} ${CC_OUTSPEC} ${KI10_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},ki10)) $@ $(call find_test,${PDP10D},ki10) ${TEST_ARG} endif pdp10-kl : ${BIN}pdp10-kl${EXE} -${BIN}pdp10-kl${EXE} : ${KL10} ${SIM} +${BIN}pdp10-kl${EXE} : ${KL10} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${KL10} ${SIM} ${KL10_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${KL10} ${SIM} ${KL10_OPT} ${CC_OUTSPEC} ${KL10_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},kl10)) $@ $(call find_test,${PDP10D},kl10) ${TEST_ARG} endif pdp10-ks : ${BIN}pdp10-ks${EXE} -${BIN}pdp10-ks${EXE} : ${KS10} ${SIM} +${BIN}pdp10-ks${EXE} : ${KS10} ${SIM} ${SLIRP_DEP} ${MKDIRBIN} - ${CC} ${KS10} ${SIM} ${KS10_OPT} ${CC_OUTSPEC} ${LDFLAGS} + ${CC} ${KS10} ${SIM} ${KS10_OPT} ${CC_OUTSPEC} ${NETWORK_LDFLAGS} ${LDFLAGS} ifneq (,$(call find_test,${PDP10D},ks10)) $@ $(call find_test,${PDP10D},ks10) ${TEST_ARG} endif @@ -3014,3 +3106,69 @@ ${BIN}frontpaneltest${EXE} : frontpanel/FrontPanelTest.c sim_sock.c sim_frontpan ${MKDIRBIN} ${CC} frontpanel/FrontPanelTest.c sim_sock.c sim_frontpanel.c ${CC_OUTSPEC} ${LDFLAGS} ${OS_CURSES_DEFS} +## libslirp library and configuration: + +RANLIB=$(shell which ranlib) +ifeq (,${RANLIB}) +RANLIB=: +endif + +LIBSLIRP_CONFIG_SRC=${SIMHD}/sim_slirp/config/libslirp_config.c +LIBSLIRP_CONFIG_TEST=${BIN}libslirp.dir/libslirp_config + +test_feature = \ + $(strip $(shell mkdir -p ${BIN}libslirp.dir > /dev/null 2>&1 && \ + ${GCC} ${CC_STD} -o ${LIBSLIRP_CONFIG_TEST} -D$(1) ${LIBSLIRP_CONFIG_SRC} > /dev/null 2>&1 && \ + ${LIBSLIRP_CONFIG_TEST} > /dev/null 2>&1 && \ + echo "Detected feature: $(2)" 1>&2 && \ + echo "feature" )) + +LIBSLIRP_FEATURES = -DBUILDING_LIBSLIRP -DLIBSLIRP_STATIC + +ifneq ($(call test_feature,TEST_VASPRINTF,vasprintf),) + LIBSLIRP_FEATURES += -DHAVE_VASPRINTF +else + ifneq ($(call test_feature,TEST_VASPRINTF_GNU_SOURCE,vasprintf with _GNU_SOURCE),) + LIBSLIRP_FEATURES += -DHAVE_VASPRINTF -D_GNU_SOURCE + endif +endif + +ifneq ($(call test_feature,TEST_CLOCK_GETTIME,clock_gettime),) + LIBSLIRP_FEATURES += -DHAVE_CLOCK_GETTIME -DHAVE_TIME_H +else + ifneq ($(call test_feature,TEST_GETTIMEOFDAY,gettimeofday),) + LIBSLIRP_FEATURES += -DHAVE_GETTIMEOFDAY -DHAVE_SYS_TIME_H + endif +endif + +ifneq ($(call test_feature,TEST_INET_PTON,inet_pton),) + LIBSLIRP_FEATURES += -DHAVE_INET_PTON +endif + +BUILD_LIBSLIRP_INCS=-I ${LIBSLIRP_SRC} -I ${LIBSLIRP_DIR}/minimal -I ${SIMHD}/sim_slirp/config + +${BIN}libslirp.dir/%.o: ${LIBSLIRP_SRC}/%.c + @mkdir -p ${BIN}libslirp.dir + ${GCC} ${CC_STD} ${CFLAGS_G} ${CFLAGS_O} ${CFLAGS_I} $(strip ${LIBSLIRP_FEATURES}) ${BUILD_LIBSLIRP_INCS} -o $@ -c $< + +${BIN}libslirp.dir/%.o: ${LIBSLIRP_DIR}/minimal/%.c + @mkdir -p ${BIN}libslirp.dir + ${GCC} ${CC_STD} ${CFLAGS_G} ${CFLAGS_O} ${CFLAGS_I} $(strip ${LIBSLIRP_FEATURES}) ${BUILD_LIBSLIRP_INCS} -o $@ -c $< + +${BIN}libslirp.dir/libslirp.a: \ + ${SIMHD}/sim_slirp/config/libslirp-version.h \ + ${SIMHD}/sim_slirp/config/glib-endian.h \ + ${LIBSLIRP_OBJS} + ${AR} rv $@ ${LIBSLIRP_OBJS} + ${RANLIB} $@ + +${SIMHD}/sim_slirp/config/libslirp-version.h: ${SIMHD}/sim_slirp/config/libslirp-version.awk + @ echo "Updating ${SIMHD}/sim_slirp/config/libslirp-version.h" + @ awk -f ${SIMHD}/sim_slirp/config/libslirp-version.awk ${SIMHD}/libslirp/meson.build > ${SIMHD}/sim_slirp/config/libslirp-version.h + +${SIMHD}/sim_slirp/config/glib-endian.h: ${SIMHD}/sim_slirp/config/mk-glib-endian + @ echo "Updating ${SIMHD}/sim_slirp/config/glib-endian.h" + @ ${SIMHD}/sim_slirp/config/mk-glib-endian > ${SIMHD}/sim_slirp/config/glib-endian.h + +${SIMHD}/sim_slirp/config/mk-glib-endian: ${SIMHD}/sim_slirp/config/mk-glib-endian.c + ${GCC} ${CC_STD} ${CFLAGS_G} ${CFLAGS_O} ${CFLAGS_I} -o $@ $< diff --git a/scp.c b/scp.c index 7d9ea9cf1..0fffb76be 100644 --- a/scp.c +++ b/scp.c @@ -376,6 +376,9 @@ pthread_mutex_t sim_timer_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t sim_timer_wake = PTHREAD_COND_INITIALIZER; pthread_mutex_t sim_tmxr_poll_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t sim_tmxr_poll_cond = PTHREAD_COND_INITIALIZER; + +pthread_mutex_t sim_debug_lock = PTHREAD_MUTEX_INITIALIZER; + int32 sim_tmxr_poll_count; pthread_t sim_asynch_main_threadid; UNIT * volatile sim_asynch_queue; @@ -400,7 +403,7 @@ if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */ ++migrated; uptr = q; q = q->a_next; - uptr->a_next = NULL; /* hygiene */ + uptr->a_next = NULL; /* hygiene */ if (uptr->a_activate_call != &sim_activate_notbefore) { a_event_time = uptr->a_event_time-((sim_asynch_inst_latency+1)/2); if (a_event_time < 0) @@ -434,7 +437,7 @@ else { uptr->a_activate_call = caller; do { q = AIO_QUEUE_VAL; - uptr->a_next = q; /* Mark as on list */ + uptr->a_next = q; /* Mark as on list */ } while (q != AIO_QUEUE_SET(uptr, q)); } AIO_IUNLOCK; @@ -448,6 +451,13 @@ if (sim_idle_wait) { t_bool sim_asynch_enabled = FALSE; #endif +/* sim_unit_aio_pending(): Test if the UNIT has pending asynch I/O. Does not + * depend on being in the main simulator thread, unlike sim_is_active(). */ +t_bool sim_unit_aio_pending(UNIT *uptr) +{ + return (uptr->next != NULL) || AIO_IS_ACTIVE(uptr); +} + /* The per-simulator init routine is a weak global that defaults to NULL The other per-simulator pointers can be overridden by the init routine @@ -12227,7 +12237,7 @@ t_bool sim_is_active (UNIT *uptr) { AIO_VALIDATE(uptr); AIO_UPDATE_QUEUE; -return (((uptr->next) || AIO_IS_ACTIVE(uptr) || ((uptr->dynflags & UNIT_TMR_UNIT) ? sim_timer_is_active (uptr) : FALSE)) ? TRUE : FALSE); +return ((sim_unit_aio_pending(uptr) || ((uptr->dynflags & UNIT_TMR_UNIT) ? sim_timer_is_active (uptr) : FALSE)) ? TRUE : FALSE); } /* sim_activate_time - return activation time @@ -13660,7 +13670,7 @@ if (sim_deb_switches & SWMASK ('F')) { /* filtering disabled? */ _debug_fwrite (buf, len); /* output now. */ return; /* done */ } -AIO_LOCK; +AIO_DEBUG_LOCK; if (debug_line_offset + len + 1 > debug_line_bufsize) { /* realloc(NULL, size) == malloc(size). Initialize the malloc()-ed space. Only need to test debug_line_buf since SIMH allocates both buffers at the same @@ -13745,7 +13755,7 @@ while (NULL != (eol = strchr (debug_line_buf, '\n')) || flush) { memmove (debug_line_buf, eol + 1, debug_line_offset); debug_line_buf[debug_line_offset] = '\0'; } -AIO_UNLOCK; +AIO_DEBUG_UNLOCK; } static void _sim_debug_write (const char *buf, size_t len) @@ -13814,14 +13824,15 @@ return some_match ? some_match : debtab_nomatch; /* Prints standard debug prefix unless previous call unterminated */ -static const char *sim_debug_prefix (uint32 dbits, DEVICE* dptr, UNIT* uptr) +static const char *sim_debug_prefix (const char *dev_name, const char *debug_type) { -const char* debug_type = _get_dbg_verb (dbits, dptr, uptr); char tim_t[32] = ""; char tim_a[32] = ""; char pc_s[MAX_WIDTH + 1] = ""; struct timespec time_now; +AIO_DEBUG_LOCK; + if (sim_deb_switches & (SWMASK ('T') | SWMASK ('R') | SWMASK ('A'))) { sim_rtcn_get_time(&time_now, 0); if (sim_deb_switches & SWMASK ('R')) @@ -13852,10 +13863,56 @@ if (sim_deb_switches & SWMASK ('P')) { sprintf(pc_s, "-%s:", sim_PC->name); sprint_val (&pc_s[strlen(pc_s)], val, sim_PC->radix, sim_PC->width, sim_PC->flags & REG_FMT); } -sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s, AIO_MAIN_THREAD ? "" : "+", dptr->name, debug_type); + +sprintf(debug_line_prefix, "DBG(%s%s%.0f%s)%s> %s %s: ", tim_t, tim_a, sim_gtime(), pc_s, AIO_MAIN_THREAD ? "" : "+", dev_name, debug_type); +AIO_DEBUG_UNLOCK; + return debug_line_prefix; } +/* Expand the newlines in a debug message: */ + +void sim_debug_expand_newlines(char *buf, size_t len, const char *debug_prefix) +{ +size_t i, j; + +/* Output the formatted data expanding newlines where they exist */ +AIO_DEBUG_LOCK; +for (i = j = 0; i < len; ++i) { + if ('\n' == buf[i]) { + if (i >= j) { + if ((i != j) || (i == 0)) { + if (!debug_unterm) /* print prefix when required */ + _sim_debug_write (debug_prefix, strlen (debug_prefix)); + _sim_debug_write (&buf[j], i-j); + _sim_debug_write ("\r\n", 2); + } + debug_unterm = 0; + } + j = i + 1; + } + else { + if (buf[i] == 0) { /* Imbedded \0 character in formatted result? */ + fprintf (stderr, "sim_debug() formatted result: '%s'\r\n" + " has an imbedded \\0 character.\r\n" + "DON'T DO THAT!\r\n", buf); + abort(); + } + } + } +if (i > j) { + if (!debug_unterm) /* print prefix when required */ + _sim_debug_write (debug_prefix, strlen (debug_prefix)); + _sim_debug_write (&buf[j], i-j); + } + +/* Set unterminated flag for next time */ + +debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm; +AIO_DEBUG_UNLOCK; +} + + void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs) { int32 i, fields, offset; @@ -13907,11 +13964,13 @@ void sim_debug_bits_hdr(uint32 dbits, DEVICE* dptr, const char *header, BITFIELD* bitdefs, uint32 before, uint32 after, int terminate) { if (sim_deb && dptr && (dptr->dctrl & dbits)) { - TMLN *saved_oline = sim_oline; + TMLN *saved_oline; + AIO_DEBUG_LOCK; + saved_oline = sim_oline; sim_oline = NULL; /* avoid potential debug to active socket */ if (!debug_unterm) - fprintf(sim_deb, "%s", sim_debug_prefix(dbits, dptr, NULL)); /* print prefix if required */ + fprintf(sim_deb, "%s", sim_debug_prefix(dptr->name, NULL)); /* print prefix if required */ if (header) fprintf(sim_deb, "%s: ", header); fprint_fields (sim_deb, (t_value)before, (t_value)after, bitdefs); /* print xlation, transition */ @@ -13919,6 +13978,7 @@ if (sim_deb && dptr && (dptr->dctrl & dbits)) { fprintf(sim_deb, "\r\n"); debug_unterm = terminate ? 0 : 1; /* set unterm for next */ sim_oline = saved_oline; /* restore original socket */ + AIO_DEBUG_UNLOCK; } } @@ -13932,9 +13992,9 @@ sim_debug_bits_hdr(dbits, dptr, NULL, bitdefs, before, after, terminate); void sim_printf (const char* fmt, ...) { char stackbuf[STACKBUFSIZE]; -int32 bufsize = sizeof(stackbuf); +size_t bufsize = sizeof(stackbuf); char *buf = stackbuf; -int32 len; +int len; va_list arglist; while (1) { /* format passed string, args */ @@ -13948,18 +14008,23 @@ while (1) { /* format passed string, arg /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ - if ((len < 0) || (len >= bufsize-1)) { + if ((len > 0) && ((size_t) len >= bufsize - 1)) { if (buf != stackbuf) free (buf); bufsize = bufsize * 2; - if (bufsize < len + 2) - bufsize = len + 2; + if (bufsize < (size_t) (len + 2)) + bufsize = (size_t) (len + 2); buf = (char *) malloc (bufsize); if (buf == NULL) /* out of memory */ return; buf[bufsize-1] = '\0'; continue; } + else if (len < 0) { + /* Output error. Dubious as to whether this really happens with + * vsprintf() or vsprintf(). */ + return; + } break; } @@ -13977,10 +14042,11 @@ if (sim_is_running) { } else fprintf (stdout, "%s", buf); -if ((!sim_oline) && (sim_log && (sim_log != stdout))) +if ((sim_oline == NULL) && (sim_log != NULL && sim_log != stdout)) fprintf (sim_log, "%s", buf); -if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log)) +if (sim_deb != NULL && (sim_deb != stdout) && (sim_deb != sim_log)) { _sim_debug_write (buf, strlen (buf)); +} if (buf != stackbuf) free (buf); @@ -14098,13 +14164,18 @@ return stat | ((stat != SCPE_OK) ? SCPE_NOMESSAGE : 0); void _sim_vdebug (uint32 dbits, DEVICE* dptr, UNIT *uptr, const char* fmt, va_list arglist) { if (sim_deb && dptr && ((dptr->dctrl | (uptr ? uptr->dctrl : 0)) & dbits)) { - TMLN *saved_oline = sim_oline; + TMLN *saved_oline; char stackbuf[STACKBUFSIZE]; int32 bufsize = sizeof(stackbuf); char *buf = stackbuf; - int32 i, j, len; - const char* debug_prefix = sim_debug_prefix(dbits, dptr, uptr); /* prefix to print if required */ + int32 len; + /* prefix to print when required */ + const char* debug_prefix; + AIO_DEBUG_LOCK; + + debug_prefix = sim_debug_prefix(dptr->name, _get_dbg_verb (dbits, dptr, uptr)); + saved_oline = sim_oline; sim_oline = NULL; /* avoid potential debug to active socket */ buf[bufsize-1] = '\0'; @@ -14124,50 +14195,34 @@ if (sim_deb && dptr && ((dptr->dctrl | (uptr ? uptr->dctrl : 0)) & dbits)) { if (bufsize < len + 2) bufsize = len + 2; buf = (char *) malloc (bufsize); - if (buf == NULL) /* out of memory */ + if (buf == NULL) { /* out of memory */ + AIO_DEBUG_UNLOCK; return; + } buf[bufsize-1] = '\0'; continue; } break; } -/* Output the formatted data expanding newlines where they exist */ + sim_debug_expand_newlines(buf, len, debug_prefix); - for (i = j = 0; i < len; ++i) { - if ('\n' == buf[i]) { - if (i >= j) { - if ((i != j) || (i == 0)) { - if (!debug_unterm) /* print prefix when required */ - _sim_debug_write (debug_prefix, strlen (debug_prefix)); - _sim_debug_write (&buf[j], i-j); - _sim_debug_write ("\r\n", 2); - } - debug_unterm = 0; - } - j = i + 1; - } - else { - if (buf[i] == 0) { /* Imbedded \0 character in formatted result? */ - fprintf (stderr, "sim_debug() formatted result: '%s'\r\n" - " has an imbedded \\0 character.\r\n" - "DON'T DO THAT!\r\n", buf); - abort(); - } - } - } - if (i > j) { - if (!debug_unterm) /* print prefix when required */ - _sim_debug_write (debug_prefix, strlen (debug_prefix)); - _sim_debug_write (&buf[j], i-j); - } - -/* Set unterminated flag for next time */ - - debug_unterm = len ? (((buf[len-1]=='\n')) ? 0 : 1) : debug_unterm; if (buf != stackbuf) free (buf); sim_oline = saved_oline; /* restore original socket */ + AIO_DEBUG_UNLOCK; + } +} + +void sim_misc_debug (const char *debug_thing, const char *debug_type, const char* msg) +{ +if (sim_deb != NULL) { + char *buf = strdup(msg); + + AIO_DEBUG_LOCK; + sim_debug_expand_newlines(buf, strlen(buf), sim_debug_prefix(debug_thing, debug_type)); + AIO_DEBUG_UNLOCK; + free(buf); } } diff --git a/scp.h b/scp.h index 04199d65b..af503ae1d 100644 --- a/scp.h +++ b/scp.h @@ -261,6 +261,7 @@ void sim_debug_bits (uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs, #define CANT_USE_MACRO_VA_ARGS 1 #endif void _sim_vdebug (uint32 dbits, DEVICE* dptr, UNIT *uptr, const char* fmt, va_list arglist); +void sim_misc_debug (const char *debug_thing, const char *debug_type, const char* msg); #ifdef CANT_USE_MACRO_VA_ARGS #define _sim_debug_device sim_debug void sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4); @@ -335,6 +336,7 @@ extern BRKTYPTAB *sim_brk_type_desc; /* type descriptions */ extern const char *sim_prog_name; /* executable program name */ extern FILE *stdnul; extern t_bool sim_asynch_enabled; +extern t_bool sim_unit_aio_pending(UNIT *uptr); #if defined(SIM_ASYNCH_IO) int sim_aio_update_queue (void); void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time); @@ -382,6 +384,10 @@ extern int32 sim_vm_initial_ips; /* base estimate of simu extern const char *sim_vm_interval_units; /* Simulator can change this - default "instructions" */ extern const char *sim_vm_step_unit; /* Simulator can change this - default "instruction" */ +/* Debug table manipulation: */ +extern DEBTAB *sim_combine_debtabs(const DEBTAB *tab1, const DEBTAB *tab2); +extern size_t sim_debtab_nelems(const DEBTAB *debtab); +extern void sim_fill_debtab_flags(DEBTAB *debtab); /* Core SCP libraries can potentially have unit test routines. These defines help implement consistent unit test functionality */ diff --git a/sigma/CMakeLists.txt b/sigma/CMakeLists.txt index 3e17a9474..927224a47 100644 --- a/sigma/CMakeLists.txt +++ b/sigma/CMakeLists.txt @@ -31,8 +31,8 @@ add_simulator(sigma sigma_rad.c sigma_rtc.c sigma_tt.c - sigma_cp.c sigma_cr.c + sigma_cp.c INCLUDES ${CMAKE_CURRENT_SOURCE_DIR} LABEL sigma diff --git a/sim_console.c b/sim_console.c index 1c0e82d4b..d159862a0 100644 --- a/sim_console.c +++ b/sim_console.c @@ -161,7 +161,6 @@ /* Forward declarations of platform specific routines */ static t_stat sim_os_poll_kbd (void); -static t_bool sim_os_poll_kbd_ready (int ms_timeout); static t_stat sim_os_putchar (int32 out); static t_stat sim_os_ttinit (void); static t_stat sim_os_ttrun (void); @@ -658,7 +657,7 @@ for (i=connections=0; ismp_reg_count) { uint32 reg; - DEVICE *dptr = NULL; + DEVICE *samp_dptr = NULL; if (rem->smp_sample_dither_pct) fprintf (st, "Register Bit Sampling is occurring every %d %s (dithered %d percent)\n", rem->smp_sample_interval, sim_vm_interval_units, rem->smp_sample_dither_pct); @@ -668,13 +667,13 @@ for (i=connections=0; ismp_reg_count; reg++) { if (rem->smp_regs[reg].indirect) fprintf (st, " indirect "); - if (dptr != rem->smp_regs[reg].dptr) + if (samp_dptr != rem->smp_regs[reg].dptr) fprintf (st, "%s ", rem->smp_regs[reg].dptr->name); if (rem->smp_regs[reg].reg->depth > 1) fprintf (st, "%s[%d]%s", rem->smp_regs[reg].reg->name, rem->smp_regs[reg].idx, ((reg + 1) < rem->smp_reg_count) ? ", " : ""); else fprintf (st, "%s%s", rem->smp_regs[reg].reg->name, ((reg + 1) < rem->smp_reg_count) ? ", " : ""); - dptr = rem->smp_regs[reg].dptr; + samp_dptr = rem->smp_regs[reg].dptr; } fprintf (st, "\n"); if (sim_switches & SWMASK ('D')) @@ -3483,9 +3482,12 @@ return SCPE_OK; #include #include #define RAW_MODE 0 +typedef BOOL (WINAPI *std_output_writer_fn)(HANDLE, const void *, DWORD, LPDWORD, LPVOID); + static HANDLE std_input; static HANDLE std_output; static HANDLE std_error; +static std_output_writer_fn std_output_writer = NULL; static DWORD saved_input_mode; static DWORD saved_output_mode; static DWORD saved_error_mode; @@ -3531,11 +3533,14 @@ ControlHandler(DWORD dwCtrlType) static t_stat sim_os_ttinit (void) { +DWORD mode; + sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_ttinit()\n"); SetConsoleCtrlHandler( ControlHandler, TRUE ); std_input = GetStdHandle (STD_INPUT_HANDLE); std_output = GetStdHandle (STD_OUTPUT_HANDLE); +std_output_writer = GetConsoleMode(std_output, &mode) ? WriteConsoleA : (std_output_writer_fn) WriteFile; std_error = GetStdHandle (STD_ERROR_HANDLE); if ((std_input) && /* Not Background process? */ (std_input != INVALID_HANDLE_VALUE)) @@ -3667,17 +3672,6 @@ if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK)) return c | SCPE_KFLAG; } -static t_bool sim_os_poll_kbd_ready (int ms_timeout) -{ -sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd_ready()\n"); -if ((std_input == NULL) || /* No keyboard for */ - (std_input == INVALID_HANDLE_VALUE)) { /* background processes */ - Sleep (ms_timeout); - return FALSE; - } -return (WAIT_OBJECT_0 == WaitForSingleObject (std_input, ms_timeout)); -} - #define BELL_CHAR 7 /* Bell Character */ #define BELL_INTERVAL_MS 500 /* No more than 2 Bell Characters Per Second */ @@ -3690,16 +3684,15 @@ return (WAIT_OBJECT_0 == WaitForSingleObject (std_input, ms_timeout)); static uint8 out_buf[ESC_HOLD_MAX]; /* Buffered characters pending output */ static int32 out_ptr = 0; -static void sim_console_write(uint8 *outbuf, int32 outsz) +static inline void sim_console_write(uint8 *outbuf, int32 outsz) { DWORD unused; - DWORD mode; - - if (GetConsoleMode(std_output, &mode)) { - WriteConsoleA(std_output, outbuf, outsz, &unused, NULL); - } else { - BOOL result = WriteFile(std_output, outbuf, outsz, &unused, NULL); - } + BOOL result; + + /* Useful to see the return value from std_output_writer. */ + result = std_output_writer(std_output, outbuf, outsz, &unused, NULL); + /* But squelch the set-but-not-used warnings. */ + (void) result; } static t_stat sim_out_hold_svc (UNIT *uptr) @@ -4055,22 +4048,6 @@ if (sim_int_char && (buf[0] == sim_int_char)) return (buf[0] | SCPE_KFLAG); } -static t_bool sim_os_poll_kbd_ready (int ms_timeout) -{ -fd_set readfds; -struct timeval timeout; - -if (!sim_ttisatty()) { /* skip if !tty */ - sim_os_ms_sleep (ms_timeout); - return FALSE; - } -FD_ZERO (&readfds); -FD_SET (0, &readfds); -timeout.tv_sec = (ms_timeout*1000)/1000000; -timeout.tv_usec = (ms_timeout*1000)%1000000; -return (1 == select (1, &readfds, NULL, NULL, &timeout)); -} - static t_stat sim_os_putchar (int32 out) { char c; diff --git a/sim_debtab.c b/sim_debtab.c new file mode 100644 index 000000000..3890d3544 --- /dev/null +++ b/sim_debtab.c @@ -0,0 +1,98 @@ +/* sim_debtab.c: + ------------------------------------------------------------------------------ + + 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 AUTHOR 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. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This module provides DEBTAB debug table manipulation functions. + +*/ + +#include "sim_defs.h" +#include "scp.h" + +/* Count the elements in the debugging table. */ +size_t sim_debtab_nelems(const DEBTAB *debtab) +{ + const DEBTAB *p; + + for (p = debtab; p->name != NULL; ++p) + /* NOP */ ; + + return (p - debtab); +} + +/* Combine two DEBTAB debug tables, returning the resulting combined + * table. Both tables have NULL as the last element's name. */ +DEBTAB *sim_combine_debtabs(const DEBTAB *tab1, const DEBTAB *tab2) +{ + size_t n_combined = 0; + const DEBTAB *p; + DEBTAB *combined; + + n_combined = sim_debtab_nelems(tab1) + sim_debtab_nelems(tab2) + 1; + combined = (DEBTAB *) malloc(n_combined * sizeof(DEBTAB)); + n_combined = 0; + + for (p = tab1; p->name != NULL; ++p, n_combined++) + combined[n_combined] = *p; + for (p = tab2; p->name != NULL; ++p, n_combined++) + combined[n_combined] = *p; + + combined[n_combined].name = NULL; + return combined; +} + +/* Fill debug mask holes in the debugging table: If an entry's mask == 0, + * find an unused bit in the existing debugging table's mask and assign + * that unused bit to the entry's mask. */ +void sim_fill_debtab_flags(DEBTAB *debtab) +{ + uint32_t dbg_bitmask = 0, new_flag; + DEBTAB *p; + int i; + + /* Augment the device's debugging table with the poll and socket flags, + * so that debugging looks correct in the output (see _get_dbg_verb() in + * scp.c) */ + dbg_bitmask = 0; + for (p = debtab; p->name != NULL; ++p) + dbg_bitmask |= p->mask; + + for (p = debtab, new_flag = 1, i = sizeof(dbg_bitmask) * 8; p->name != NULL && i > 0; /* empty */) { + while (i > 0 && (new_flag & dbg_bitmask) != 0) { + new_flag <<= 1; + --i; + } + + /* Found a hole in the existing debugging flags */ + if (i > 0) { + /* Find a need for the new flag */ + while (p->mask != 0 && p->name != NULL) + ++p; + if (p->name != NULL) { + p->mask = new_flag; + } + } + } +} diff --git a/sim_defs.h b/sim_defs.h index fbb32ecd1..01a106eb2 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -153,10 +153,16 @@ extern int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...); #include #include #include +#include #undef PACKED /* avoid macro name collision */ #undef ERROR /* avoid macro name collision */ #undef MEM_MAPPED /* avoid macro name collision */ #include + +#if defined(_MSC_VER) +/* Disable unreferenced formal parameter warnings (4100) */ +#pragma warning(disable: 4100) +#endif #endif #ifdef USE_REGEX @@ -338,7 +344,11 @@ typedef uint32 t_addr; #else # define PACKED_BEGIN #if defined(_WIN32) -# define PACKED_END __attribute__((gcc_struct, packed)) +# if !defined(__clang__) +# define PACKED_END __attribute__((gcc_struct, packed)) +# else +# define PACKED_END __attribute__((packed)) +# endif #else # define PACKED_END __attribute__((packed)) #endif @@ -1166,6 +1176,7 @@ extern int32 sim_tmxr_poll_count; extern pthread_cond_t sim_tmxr_poll_cond; extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_t sim_asynch_main_threadid; +extern pthread_mutex_t sim_debug_lock; extern UNIT * volatile sim_asynch_queue; extern volatile t_bool sim_idle_wait; extern int32 sim_asynch_check; @@ -1209,8 +1220,9 @@ extern int32 sim_asynch_inst_latency; #define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid )) #define AIO_LOCK \ pthread_mutex_lock(&sim_asynch_lock) -#define AIO_UNLOCK \ - pthread_mutex_unlock(&sim_asynch_lock) +#define AIO_UNLOCK pthread_mutex_unlock(&sim_asynch_lock) +#define AIO_DEBUG_LOCK pthread_mutex_lock(&sim_debug_lock) +#define AIO_DEBUG_UNLOCK pthread_mutex_unlock(&sim_debug_lock) #define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next)) #if defined(SIM_ASYNCH_MUX) #define AIO_CANCEL(uptr) \ @@ -1263,6 +1275,13 @@ extern int32 sim_asynch_inst_latency; #define AIO_QUEUE_MODE "Lock free asynchronous event queue" #define AIO_INIT \ do { \ + pthread_mutexattr_t attr; \ + \ + pthread_mutexattr_init (&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init (&sim_debug_lock, &attr); \ + pthread_mutexattr_destroy (&attr); \ + \ sim_asynch_main_threadid = pthread_self(); \ /* Empty list/list end uses the point value (void *)1. \ This allows NULL in an entry's a_next pointer to \ @@ -1277,6 +1296,7 @@ extern int32 sim_asynch_inst_latency; pthread_cond_destroy(&sim_timer_wake); \ pthread_mutex_destroy(&sim_tmxr_poll_lock); \ pthread_cond_destroy(&sim_tmxr_poll_cond); \ + pthread_mutex_destroy(&sim_debug_lock); \ } while (0) #ifdef _WIN32 #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) @@ -1308,6 +1328,7 @@ extern int32 sim_asynch_inst_latency; pthread_mutexattr_init (&attr); \ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ pthread_mutex_init (&sim_asynch_lock, &attr); \ + pthread_mutex_init (&sim_debug_lock, &attr); \ pthread_mutexattr_destroy (&attr); \ sim_asynch_main_threadid = pthread_self(); \ /* Empty list/list end uses the point value (void *)1. \ @@ -1323,6 +1344,7 @@ extern int32 sim_asynch_inst_latency; pthread_cond_destroy(&sim_timer_wake); \ pthread_mutex_destroy(&sim_tmxr_poll_lock); \ pthread_cond_destroy(&sim_tmxr_poll_cond); \ + pthread_mutex_destroy(&sim_debug_lock); \ } while (0) #define AIO_ILOCK AIO_LOCK #define AIO_IUNLOCK AIO_UNLOCK @@ -1381,6 +1403,8 @@ extern int32 sim_asynch_inst_latency; #define AIO_MAIN_THREAD TRUE #define AIO_LOCK #define AIO_UNLOCK +#define AIO_DEBUG_LOCK +#define AIO_DEBUG_UNLOCK #define AIO_CLEANUP #define AIO_EVENT_BEGIN(uptr) #define AIO_EVENT_COMPLETE(uptr, reason) @@ -1390,6 +1414,9 @@ extern int32 sim_asynch_inst_latency; #define AIO_TLS #endif /* SIM_ASYNCH_IO */ +/* Unused argument macro */ +#define SIM_UNUSED_ARG(arg) (void ) (arg); + #ifdef __cplusplus } #endif diff --git a/sim_ether.c b/sim_ether.c index 7faabbd3f..b530b5849 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -454,7 +454,9 @@ t_stat eth_mac_scan_ex (ETH_MAC* mac, const char* strmac, UNIT *uptr) (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) && (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))) return sim_messagef (SCPE_ARG, "Invalid MAC address format: '%s'\n", strmac); - for (i=0; i<6; i++) + + memset(newmac, 0, sizeof(newmac) / sizeof(newmac[0])); + for (i=0; i<6; i++) { if (a[i] > 0xFF) return sim_messagef (SCPE_ARG, "Invalid MAC address byte value: %02X\n", a[i]); else { @@ -468,6 +470,7 @@ t_stat eth_mac_scan_ex (ETH_MAC* mac, const char* strmac, UNIT *uptr) mask = 0xFF << shift; newmac[i] = (unsigned char)((a[i] & mask) | (g[i] & ~mask)); } + } /* final check - mac cannot be broadcast or multicast address */ if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */ @@ -676,7 +679,7 @@ t_stat ethq_init(ETH_QUE* que, int max) /* create dynamic queue if it does not exist */ if (!que->item) { que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item)); - if (!que->item) { + if (NULL == que->item) { /* failed to allocate memory */ sim_printf("EthQ: failed to allocate dynamic queue[%d]\n", max); return SCPE_MEM; @@ -779,6 +782,9 @@ ethq_insert_data(que, type, pack->oversize ? pack->oversize : pack->msg, pack->u t_stat eth_show_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char *desc) { +SIM_UNUSED_ARG(dptr); +SIM_UNUSED_ARG(desc); + return eth_show (st, uptr, val, NULL); } @@ -895,6 +901,11 @@ t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc) ETH_LIST list[ETH_MAX_DEVICE]; int number; + SIM_UNUSED_ARG(uptr); + SIM_UNUSED_ARG(val); + SIM_UNUSED_ARG(desc); + + number = eth_devices(ETH_MAX_DEVICE, list, FALSE); fprintf(st, "ETH devices:\n"); if (number == -1) @@ -1055,6 +1066,8 @@ static int eth_host_pcap_devices(int used, int max, ETH_LIST* list) { int i; +SIM_UNUSED_ARG(max); + for (i=0; ihost_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); dev->have_host_nic_phy_addr = 0; if (dev->eth_api != ETH_API_PCAP) @@ -1984,7 +1999,7 @@ while (dev->handle) { if (do_select) { #ifdef HAVE_SLIRP_NETWORK if (dev->eth_api == ETH_API_NAT) { - sel_ret = sim_slirp_select ((SLIRP*)dev->handle, 250); + sel_ret = sim_slirp_select ((SimSlirpNetwork *) dev->handle, 250); } else #endif @@ -2062,7 +2077,7 @@ while (dev->handle) { #endif /* HAVE_VDE_NETWORK */ #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: - sim_slirp_dispatch ((SLIRP*)dev->handle); + sim_slirp_dispatch ((SimSlirpNetwork *) dev->handle); status = 1; break; #endif /* HAVE_SLIRP_NETWORK */ @@ -2088,17 +2103,6 @@ while (dev->handle) { } break; } - if ((status > 0) && (dev->asynch_io)) { - int wakeup_needed; - - pthread_mutex_lock (&dev->lock); - wakeup_needed = (dev->read_queue.count != 0); - pthread_mutex_unlock (&dev->lock); - if (wakeup_needed) { - sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); - sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); - } - } if (status < 0) { ++dev->receive_packet_errors; _eth_error (dev, "_eth_reader"); @@ -2136,26 +2140,27 @@ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); pthread_mutex_lock (&dev->writer_lock); -while (dev->handle) { +while (dev->handle != NULL) { pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); while (NULL != (request = dev->write_requests)) { - if (dev->handle == NULL) /* Shutting down? */ - break; - /* Pull buffer off request list */ - dev->write_requests = request->next; - pthread_mutex_unlock (&dev->writer_lock); - - if (dev->throttle_delay != ETH_THROT_DISABLED_DELAY) { - uint32 packet_delta_time = sim_os_msec() - dev->throttle_packet_time; - dev->throttle_events <<= 1; - dev->throttle_events += (packet_delta_time < dev->throttle_time) ? 1 : 0; - if ((dev->throttle_events & dev->throttle_mask) == dev->throttle_mask) { - sim_os_ms_sleep (dev->throttle_delay); - ++dev->throttle_count; + if (dev->handle != NULL) { + /* Pull buffer off request list */ + dev->write_requests = request->next; + pthread_mutex_unlock (&dev->writer_lock); + + if (dev->throttle_delay != ETH_THROT_DISABLED_DELAY) { + uint32 packet_delta_time = sim_os_msec() - dev->throttle_packet_time; + dev->throttle_events <<= 1; + dev->throttle_events += (packet_delta_time < dev->throttle_time) ? 1 : 0; + if ((dev->throttle_events & dev->throttle_mask) == dev->throttle_mask) { + sim_os_ms_sleep (dev->throttle_delay); + ++dev->throttle_count; + } + dev->throttle_packet_time = sim_os_msec(); } - dev->throttle_packet_time = sim_os_msec(); - } - dev->write_status = _eth_write(dev, &request->packet, NULL); + dev->write_status = _eth_write(dev, &request->packet, NULL); + } else + break; /* Shutting down? */ pthread_mutex_lock (&dev->writer_lock); /* Put buffer on free buffer list */ @@ -2194,11 +2199,11 @@ dev->asynch_io = sim_asynch_enabled; dev->asynch_io_latency = latency; pthread_mutex_lock (&dev->lock); wakeup_needed = (dev->read_queue.count != 0); -pthread_mutex_unlock (&dev->lock); if (wakeup_needed) { sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } +pthread_mutex_unlock (&dev->lock); #endif return SCPE_OK; } @@ -2644,7 +2649,7 @@ switch (eth_api) { #endif #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: - sim_slirp_close((SLIRP*)pcap); + sim_slirp_close((SimSlirpNetwork *) pcap); break; #endif case ETH_API_UDP: @@ -3082,7 +3087,7 @@ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { #endif #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: - status = sim_slirp_send((SLIRP*)dev->handle, (char *)packet->msg, (size_t)packet->len, 0); + status = sim_slirp_send((SimSlirpNetwork *)dev->handle, (char *)packet->msg, (size_t)packet->len, 0); if ((status == (int)packet->len) || (status == 0)) status = 0; else @@ -3113,7 +3118,7 @@ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { } /* if packet->len */ /* call optional write callback function */ -if (routine) +if (routine != NULL) (routine)(status); return ((status == 0) ? SCPE_OK : SCPE_IOERR); @@ -3792,6 +3797,7 @@ if (bpf_used ? to_me : (to_me && !from_me)) { /* This must be done before any needed CRC calculation */ _eth_fix_ip_xsum_offload(dev, (const u_char*)data, len); + memset(crc_data, 0, sizeof(crc_data) / sizeof(crc_data[0])); if (dev->need_crc) crc_len = eth_get_packet_crc32_data(data, len, crc_data); @@ -3799,8 +3805,25 @@ if (bpf_used ? to_me : (to_me && !from_me)) { pthread_mutex_lock (&dev->lock); ethq_insert_data(&dev->read_queue, ETH_ITM_NORMAL, data, 0, len, crc_len, crc_data, 0); + if (dev->read_queue.count == 1 || (dev->asynch_io && !sim_unit_aio_pending(dev->dptr->units))) { + /* Kick the simulator's lower half UNIT handler if the read queue's depth is 1 (just received + * a packet) or an ansynchronous service event is not already scheduled. + * + * This allows packets to accumulate while ensuring that there is a pending UNIT event + * to processes them. Otherwise, the simulator's receiver will stall and unprocessed + * packets will accumulate. */ + + sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); + sim_debug(dev->dbit, dev->dptr, "Scheduling UNIT service event to drain queue (depth = %d)\n", + dev->read_queue.count); + } else { + sim_debug(dev->dbit, dev->dptr, "Deferred -- pending UNIT service event (depth = %d)\n", + dev->read_queue.count); + } + ++dev->packets_received; pthread_mutex_unlock (&dev->lock); + free(moved_data); } #else /* !USE_READER_THREAD */ @@ -3837,7 +3860,7 @@ if (bpf_used ? to_me : (to_me && !from_me)) { int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { -int status; +int status = 0; /* make sure device exists */ @@ -4119,9 +4142,9 @@ if (hash) { if (dev->dptr->dctrl & dev->dbit) { sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); for (i = 0; i < addr_count; i++) { - char mac[20]; - eth_mac_fmt(&dev->filter_address[i], mac); - sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); + char mac_daddy[20]; + eth_mac_fmt(&dev->filter_address[i], mac_daddy); + sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac_daddy); } if (dev->all_multicast) { sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); @@ -4294,7 +4317,7 @@ if (dev->bpf_filter) fprintf(st, " BPF Filter: %s\n", dev->bpf_filter); #if defined(HAVE_SLIRP_NETWORK) if (dev->eth_api == ETH_API_NAT) - sim_slirp_show ((SLIRP *)dev->handle, st); + sim_slirp_show ((SimSlirpNetwork *) dev->handle, st); #endif } @@ -4338,6 +4361,9 @@ static uint32 valcrc32[] = { 0x6C311115, 0x8520007D, 0x65623584, 0x8C7324EC, 0x7E975837, 0x9786495F, 0x77C47CA6, 0x9ED56DCE, 0x497D8351, 0xA06C9239, 0x402EA7C0, 0xA93FB6A8, 0x5BDBCA73, 0xB2CADB1B, 0x5288EEE2, 0xBB99FF8A}; +SIM_UNUSED_ARG(dptr); + + for (val=0; val <= 0xFF; val++) { memset (data, val, sizeof (data)); if (valcrc32[val] != eth_crc32 (0, data, sizeof (data))) { @@ -4496,6 +4522,8 @@ return (errors == 0) ? SCPE_OK : SCPE_IERR; t_stat sim_ether_test (DEVICE *dptr, const char *cptr) { +SIM_UNUSED_ARG(cptr); + t_stat stat = SCPE_OK; SIM_TEST_INIT; diff --git a/sim_printf_fmts.h b/sim_printf_fmts.h index e31389381..3363045d3 100644 --- a/sim_printf_fmts.h +++ b/sim_printf_fmts.h @@ -29,17 +29,17 @@ * POINTER_FMT: Format modifier for pointers, e.g. "%08" POINTER_FMT "X" */ -#if defined (_WIN32) || defined(_WIN64) +#if defined (_WIN32) || defined(_WIN64) || defined(__MINGW64__) || defined(__MINGW32__) -# if defined(__MINGW64__) -# define LL_FMT "I64" -# define SIZE_T_FMT "I64" -# elif defined(_MSC_VER) || defined(__MINGW32__) +# if defined(__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO # define LL_FMT "ll" -# define SIZE_T_FMT "z" # else - /* Graceful fail -- shouldn't ever default to this on a Windows platform. */ -# define LL_FMT "ll" +# define LL_FMT "I64" +# endif + +# if defined(__MINGW64__) || defined(_WIN64) +# define SIZE_T_FMT "I64" +# elif defined(__MINGW32__) || defined(_WIN32) # define SIZE_T_FMT "I32" # endif @@ -75,7 +75,6 @@ # define POINTER_FMT "" #endif - #if defined (USE_INT64) && defined (USE_ADDR64) # define T_ADDR_FMT T_UINT64_FMT #else diff --git a/sim_slirp/config/.gitignore b/sim_slirp/config/.gitignore new file mode 100644 index 000000000..20eb0abd3 --- /dev/null +++ b/sim_slirp/config/.gitignore @@ -0,0 +1,3 @@ +mk-glib-endian +libslirp-version.h +glib-endian.h diff --git a/sim_slirp/config/libslirp-version.awk b/sim_slirp/config/libslirp-version.awk new file mode 100644 index 000000000..edc7ba010 --- /dev/null +++ b/sim_slirp/config/libslirp-version.awk @@ -0,0 +1,52 @@ +# libslirp-version.awk: Generate the libslirp-version.h header: +# +# 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 AUTHOR 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. +# +# Except as contained in this notice, the name of the author shall not be +# used in advertising or otherwise to promote the sale, use or other dealings +# in this Software without prior written authorization from the author. + +/ +version : '.*'/ { + vstr = $3; + sub(/'/, "", vstr); + sub(/',/, "", vstr); + split(vstr, vparts, "."); + print "#ifndef LIBSLIRP_VERSION_H_"; + print "#define LIBSLIRP_VERSION_H_"; + print ""; + print "#ifdef __cplusplus"; + print "extern \"C\" {"; + print "#endif"; + print ""; + print "#define SLIRP_MAJOR_VERSION ", vparts[1]; + print "#define SLIRP_MINOR_VERSION ", vparts[2]; + print "#define SLIRP_MICRO_VERSION ", vparts[3]; + print "#define SLIRP_VERSION_STRING \"" vstr "\""; + print ""; + print "#define SLIRP_CHECK_VERSION(major,minor,micro) \\"; + print " (SLIRP_MAJOR_VERSION > (major) || \\"; + print " (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION > (minor)) || \\"; + print " (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION == (minor) && \\"; + print " SLIRP_MICRO_VERSION >= (micro)))"; + print ""; + print "#ifdef __cplusplus"; + print "} /* extern \"C\" */"; + print "#endif"; + print ""; + print "#endif /* LIBSLIRP_VERSION_H_ */"; +} diff --git a/sim_slirp/config/libslirp_config.c b/sim_slirp/config/libslirp_config.c new file mode 100644 index 000000000..4519e094b --- /dev/null +++ b/sim_slirp/config/libslirp_config.c @@ -0,0 +1,46 @@ +/* Feature test program for libslirp. + * + * Only used by the makefile to set the minimal glib features. Patterned + * off the way CMake does testing for library functions. + * + * - If the source compiles, with the appropriate TEST_* symbol defined, + * then the resulting libslirp_config program is executed, which should + * exit with a success (0) status. + * + * - Any failure along the way and the makefile will not detect the feature + * and won't update the LIBSLIR_FEATURES variable. + */ + +#if defined(TEST_VASPRINTF_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include + +#if defined(TEST_CLOCK_GETTIME) +#include +#endif +#if defined(TEST_GETTIMEOFDAY) +#include +#endif +#if defined(TEST_INET_PTON) +#include +#endif + +int main(void) +{ +#if defined(TEST_VASPRINTF) || defined(TEST_VASPRINTF_GNU_SOURCE) + (void (*)()) (vasprintf); +#endif +#if defined(TEST_CLOCK_GETTIME) + (void (*)()) (clock_gettime); +#endif +#if defined(TEST_GETTIMEOFDAY) + (void (*)()) (gettimeofday); +#endif +#if defined(TEST_INET_PTON) + (void (*)()) (inet_pton); +#endif + + return 0; +} diff --git a/sim_slirp/config/mk-glib-endian.c b/sim_slirp/config/mk-glib-endian.c new file mode 100644 index 000000000..51bf05e55 --- /dev/null +++ b/sim_slirp/config/mk-glib-endian.c @@ -0,0 +1,65 @@ +#include +#include +#include "sim_printf_fmts.h" + +#if defined(HAVE_INTTYPES_H) +#include +#endif + +/* Bridge until printf format patch accepted. */ +#if defined(__MINGW64__) || defined(_WIN64) || defined(__WIN64) +# if defined(PRIu64) +# define SIM_PRIsize_t PRIu64 +# else +# define SIM_PRIsize_t "llu" +# endif +#elif defined(__MINGW32__) || defined(_WIN32) || defined(__WIN32) +# define SIM_PRIsize_t "zu" +#elif defined(__GNU_LIBRARY__) || defined(__GLIBC__) || defined(__GLIBC_MINOR__) || \ + defined(__APPLE__) +/* GNU libc (Linux) and macOS */ +# define SIM_PRIsize_t "zu" +#else +# define SIM_PRIsize_t "u" +#endif + +const char *preamble[] = { + "/* Automagically generated endianness include.", + " *", + " * Also defines GLIB_SIZE_VOID_P using the CMake CMAKE_SIZEOF_VOID_P", + " * substitution. Probably not where this manifest constant really", + " * belongs, but there's not really a better place for it. */", + "", + "#if !defined(_GLIB_ENDIAN_H)", + "#define _GLIB_ENDIAN_H", + "" + "#define G_BIG_ENDIAN 0x1234", + "#define G_LITTLE_ENDIAN 0x4321", + "" +}; + +const char *epilog[] = { + "", + "#endif /* _GLIB_ENDIAN_H */" +}; + +const int endian_test = 0x1234; + +int main(void) { + size_t i; + + for (i = 0; i < sizeof(preamble) / sizeof(preamble[0]); ++i) { + fputs(preamble[i], stdout); + fputc('\n', stdout); + } + + printf("#define G_BYTE_ORDER %s\n", (*((const char *) &endian_test) == 1) ? "G_BIG_ENDIAN" : "G_LITTLE_ENDIAN"); + printf("#define GLIB_SIZEOF_VOID_P %" SIM_PRIsize_t "\n", sizeof(void *)); + + for (i = 0; i < sizeof(epilog) / sizeof(epilog[0]); ++i) { + fputs(epilog[i], stdout); + fputc('\n', stdout); + } + + return 0; +} diff --git a/sim_slirp/sim_slirp.c b/sim_slirp/sim_slirp.c new file mode 100644 index 000000000..b684cd0bb --- /dev/null +++ b/sim_slirp/sim_slirp.c @@ -0,0 +1,719 @@ +/* sim_slirp.c: + ------------------------------------------------------------------------------ + Copyright (c) 2015, Mark Pizzolato + Copyright (c) 2024, B. Scott Michel + + 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 AUTHOR 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. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This module provides the interface needed between sim_ether and libslirp to + provide NAT network functionality. + +*/ +#define SIMH_IP_NETWORK 0x0a000200 /* aka 10.0.2.0 */ +#define SIMH_IP_NETMASK 0xffffff00 /* aka 255.255.255.0 */ +#define SIMH_GATEWAY_ADDR 0x0a000202 /* aka 10.0.2.2 (SIMH's address) */ +#define SIMH_RESOLVER_ADDR 0x0a000203 /* aka 10.0.2.3 (DNS resolver) */ + +/* IPv6 private address range (ULA) blessed by IANA */ +#define SIMH_IP6_NETWORK "fd00:cafe:dead:beef::" +#define SIMH_IP6_PREFIX_LEN 64 +#define SIMH_GW_ADDR6 "fd00:cafe:dead:beef::2" /* SIMH's IPv6 address */ + +#include +#include /* Paranoia for Win32/64 */ + +#include "libslirp.h" +#include "sim_defs.h" +#include "scp.h" +#include "sim_sock.h" +#include "sim_slirp_network.h" +#include "sim_printf_fmts.h" + +#if !defined(_WIN32) +# define simh_inet_aton inet_aton +#else +# define simh_inet_aton slirp_inet_aton +# if WINVER < 0x0601 +# undef inet_pton +# define inet_pton sim_inet_pton +# endif +#endif + +#define IS_TCP 0 +#define IS_UDP 1 + +static const char *tcpudp[] = {"TCP", "UDP"}; + +/* Additional debugging flags added to the device's debug table. */ +DEBTAB slirp_dbgtable[] = { + { "POLL", 0, "Show libslirp polling callback activity" }, + { "SOCKET", 0, "Show libslirp socket registration activity" }, + { NULL } +}; + +/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= + * Port redirection management: + *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ + +static int parse_redirect_port(struct redir_tcp_udp **head, const char *buff, int is_udp) +{ + char gbuf[4 * CBUFSIZE]; + uint32 inaddr = INADDR_ANY; + int port = 0; + int lport = 0; + char *ipaddrstr = NULL; + char *portstr = NULL; + struct redir_tcp_udp *newp; + + gbuf[sizeof(gbuf) - 1] = '\0'; + strncpy(gbuf, buff, sizeof(gbuf) - 1); + if (((ipaddrstr = strchr(gbuf, ':')) == NULL) || (*(ipaddrstr + 1) == 0)) { + sim_printf("redir %s syntax error\n", tcpudp[is_udp]); + return -1; + } + *ipaddrstr++ = 0; + + if ((ipaddrstr) && (((portstr = strchr(ipaddrstr, ':')) == NULL) || (*(portstr + 1) == 0))) { + sim_printf("redir %s syntax error\n", tcpudp[is_udp]); + return -1; + } + *portstr++ = 0; + + sscanf(gbuf, "%d", &lport); + sscanf(portstr, "%d", &port); + if (ipaddrstr != NULL) { + struct in_addr addr; + + if (simh_inet_aton(ipaddrstr, &addr)) { + inaddr = addr.s_addr; + } + } + + if (inaddr == INADDR_ANY) { + sim_printf("%s redirection error: an IP address must be specified\n", tcpudp[is_udp]); + return -1; + } + + if ((newp = (struct redir_tcp_udp *) calloc(1, sizeof(struct redir_tcp_udp))) == NULL) + return -1; + + newp->is_udp = is_udp; + newp->simh_host_port = port; + simh_inet_aton(ipaddrstr, &newp->sim_local_inaddr); + newp->sim_local_port = lport; + newp->next = *head; + *head = newp; + return 0; +} + +/* do_redirects: Adds the proxied (forwarded) ports from the guest network to the + * outside network. */ +static int do_redirects(SimSlirpNetwork *slirp, struct redir_tcp_udp *head) +{ + struct in_addr host_addr; + int ret = 0; + + host_addr.s_addr = htonl(INADDR_ANY); + if (head != NULL) { + ret = do_redirects(slirp, head->next); + if (slirp_add_hostfwd(slirp->slirp_cxn, head->is_udp, host_addr, head->sim_local_port, + head->sim_local_inaddr, head->simh_host_port) < 0) { + sim_printf("Can't establish redirector for: redir %s =%d:%s:%d\n", tcpudp[head->is_udp], + head->sim_local_port, sim_inet_ntoa4(&head->sim_local_inaddr), + head->simh_host_port); + ++ret; + } + } + return ret; +} + +static unsigned int collect_slirp_debug(const char *dbg_tokens, int *err) +{ + unsigned int slirp_dbg = 0; + + while (dbg_tokens != NULL && *dbg_tokens != '\0' && *err == 0) { + if (!strncasecmp(dbg_tokens, "CALL", 4)) { + slirp_dbg |= SLIRP_DBG_CALL; + } else if (!strncasecmp(dbg_tokens, "MISC", 4)) { + slirp_dbg |= SLIRP_DBG_MISC; + } else if (!strncasecmp(dbg_tokens, "ERROR", 5)) { + slirp_dbg |= SLIRP_DBG_ERROR; + } else if (!strncasecmp(dbg_tokens, "VERBOSE_CALL", 13)) { + slirp_dbg |= SLIRP_DBG_VERBOSE_CALL; + } else if (!strncasecmp(dbg_tokens, "ALL", 3)) { + slirp_dbg |= (SLIRP_DBG_CALL | SLIRP_DBG_MISC | SLIRP_DBG_ERROR | SLIRP_DBG_VERBOSE_CALL); + } else + *err = 1; + + if (*err == 0) { + dbg_tokens = strchr(dbg_tokens, ';'); + if (dbg_tokens != NULL) + ++dbg_tokens; + } + } + + return slirp_dbg; +} + +/* SIMH uses a custom logger to output libslirp messages. */ + +static void output_stderr_noexit(const char *fmt, va_list args) +{ + char stackbuf[256]; + char *buf = stackbuf; + size_t bufsize = sizeof(stackbuf); + int vlen; + + do { + vlen = vsnprintf(buf, bufsize, fmt, args); + if (vlen < 0 || ((size_t) vlen) >= bufsize - 2) { + /* Reallocate a larger buffer... */ + if (buf != stackbuf) + free(buf); + bufsize *= 2; + if (bufsize < (size_t) (vlen + 2)) + bufsize = (size_t) (vlen + 2); + buf = (char *) calloc(bufsize, sizeof(char)); + if (buf == NULL) + return; + } + } while (vlen < 0 || (size_t) vlen >= bufsize - 1); + + buf[vlen++] = '\n'; + buf[vlen] = '\0'; + + sim_misc_debug("libslirp", "trace", buf); + + if (buf != stackbuf) + free(buf); +} + +static void output_stderr_exit(const char *fmt, va_list args) +{ + output_stderr_noexit(fmt, args); + exit(1); + g_assert_not_reached(); +} + +static void libslirp_guest_error(const char *msg, void *opaque) +{ + /* SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; */ + GLIB_UNUSED_PARAM(opaque); + + if (sim_deb != NULL) { + fprintf(sim_deb, "libslirp guest error: %s\n", msg); + fflush(sim_deb); + } + + fprintf(stderr, "libslirp guest error: %s\n", msg); + fflush(stderr); +} + +static GLibLogger simh_logger = { + .logging_enabled = 1, + .output_warning = output_stderr_noexit, + .output_debug = output_stderr_noexit, + .output_error = output_stderr_exit, + .output_critical = output_stderr_exit +}; + +/* Forward decl's... */ +static int initialize_poll_fds(SimSlirpNetwork *slirp); +static slirp_ssize_t invoke_sim_packet_callback(const void *buf, size_t len, void *opaque); +static void notify_callback(void *opaque); + +SimSlirpNetwork *sim_slirp_open(const char *args, void *pkt_opaque, packet_callback pkt_callback, DEVICE *dptr, uint32 dbit, + char *errbuf, size_t errbuf_size) +{ + SimSlirpNetwork *slirp = (SimSlirpNetwork *) calloc(1, sizeof(*slirp)); + SlirpConfig *cfg = &slirp->slirp_config; + SlirpCb *cbs = &slirp->slirp_callbacks; + + char *targs = strdup(args); + const char *tptr = targs; + const char *cptr; + char tbuf[CBUFSIZE], gbuf[CBUFSIZE], abuf[CBUFSIZE]; + int err; + struct in6_addr default_ipv6_prefix, default_ipv6_gw; + + /* Default IPv6 address -- FIXME */ + inet_pton(AF_INET6, SIMH_IP6_NETWORK, &default_ipv6_prefix); + inet_pton(AF_INET6, SIMH_GW_ADDR6, &default_ipv6_gw); + + /* Version 1 config */ + cfg->version = SLIRP_CONFIG_VERSION_MAX; + cfg->restricted = 0; + cfg->in_enabled = 1; + cfg->vnetwork.s_addr = htonl(SIMH_IP_NETWORK); + cfg->vnetmask.s_addr = htonl(SIMH_IP_NETMASK); + cfg->vhost.s_addr = htonl(SIMH_GATEWAY_ADDR); + cfg->in6_enabled = 0; + cfg->vprefix_addr6 = default_ipv6_prefix; + cfg->vprefix_len = SIMH_IP6_PREFIX_LEN; + cfg->vhost6 = default_ipv6_gw; + cfg->vnameserver.s_addr = htonl(SIMH_RESOLVER_ADDR); + + /* Version 2 config: nothing used */ + /* Version 3 config: */ + cfg->disable_dns = 0; + + /* Version 4 config */ + /* DHCP enabled by default. */ + cfg->disable_dhcp = 0; + + /* Version 5 config: nothing used */ + + /* SIMH state/config */ + slirp->args = (char *) calloc(1 + strlen(args), sizeof(char)); + strcpy(slirp->args, args); + pthread_mutex_init(&slirp->libslirp_access, NULL); + + slirp->original_debflags = dptr->debflags; + dptr->debflags = sim_combine_debtabs(dptr->debflags, slirp_dbgtable); + sim_fill_debtab_flags(dptr->debflags); + slirp->flag_offset = sim_debtab_nelems(slirp->original_debflags); + + /* Parse through arguments... */ + err = 0; + while (*tptr && !err) { + tptr = get_glyph_nc(tptr, tbuf, ','); + if (!tbuf[0]) + break; + cptr = tbuf; + cptr = get_glyph(cptr, gbuf, '='); + if (0 == MATCH_CMD(gbuf, "DHCP")) { + cfg->disable_dhcp = 0; + if (cptr != NULL && *cptr != '\0') + simh_inet_aton(cptr, &cfg->vdhcp_start); + continue; + } + if (0 == MATCH_CMD(gbuf, "TFTP")) { + if (cptr != NULL && *cptr != '\0') + slirp->the_tftp_path = strdup(cptr); + else { + strlcpy(errbuf, "Missing TFTP Path", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "BOOTFILE")) { + if (cptr && *cptr) + slirp->the_bootfile = strdup(cptr); + else { + strlcpy(errbuf, "Missing DHCP Boot file name", errbuf_size); + err = 1; + } + continue; + } + if ((0 == MATCH_CMD(gbuf, "NAMESERVER")) || (0 == MATCH_CMD(gbuf, "DNS"))) { + if (cptr && *cptr) + simh_inet_aton(cptr, &cfg->vnameserver); + else { + strlcpy(errbuf, "Missing nameserver", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "DNSSEARCH")) { + if (cptr != NULL && *cptr) { + int count = 0; + char *name; + + slirp->dns_search = strdup(cptr); + name = slirp->dns_search; + do { + ++count; + slirp->dns_search_domains = (char **)realloc(cfg->vdnssearch, (count + 1) * sizeof(char *)); + slirp->dns_search_domains[count] = NULL; + slirp->dns_search_domains[count - 1] = name; + if (NULL != (name = strchr(name, ','))) { + *name = '\0'; + ++name; + } + } while (NULL != name && *name); + } else { + strlcpy(errbuf, "Missing DNS search list", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "GATEWAY") || 0 == MATCH_CMD(gbuf, "GW")) { + if (cptr && *cptr) { + cptr = get_glyph(cptr, abuf, '/'); + if (cptr && *cptr) + cfg->vnetmask.s_addr = htonl(~((1 << (32 - atoi(cptr))) - 1)); + simh_inet_aton(abuf, &cfg->vhost); + } else { + strlcpy(errbuf, "Missing host", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "NETWORK")) { + if (cptr && *cptr) { + cptr = get_glyph(cptr, abuf, '/'); + if (cptr && *cptr) + cfg->vnetmask.s_addr = htonl(~((1 << (32 - atoi(cptr))) - 1)); + simh_inet_aton(abuf, &cfg->vnetwork); + } else { + strlcpy(errbuf, "Missing network", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "NODHCP")) { + cfg->disable_dhcp = 1; + continue; + } + if (0 == MATCH_CMD(gbuf, "UDP")) { + if (cptr && *cptr) + err = parse_redirect_port(&slirp->rtcp, cptr, IS_UDP); + else { + strlcpy(errbuf, "Missing UDP port mapping", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "TCP")) { + if (cptr && *cptr) + err = parse_redirect_port(&slirp->rtcp, cptr, IS_TCP); + else { + strlcpy(errbuf, "Missing TCP port mapping", errbuf_size); + err = 1; + } + continue; + } + if (0 == MATCH_CMD(gbuf, "IPV6")) { + cfg->in6_enabled = 1; + continue; + } + if (0 == MATCH_CMD(gbuf, "SLIRP")) { + unsigned int slirp_dbg = collect_slirp_debug(cptr, &err); + + if (!err) { + slirp_set_debug(slirp_dbg); + continue; + } + } + if (0 == MATCH_CMD(gbuf, "NOSLIRP")) { + unsigned int slirp_dbg = collect_slirp_debug(cptr, &err); + + if (!err) { + slirp_reset_debug(slirp_dbg); + continue; + } + } + snprintf(errbuf, errbuf_size - 1, "Unexpected NAT argument: %s", gbuf); + err = 1; + } + + if (err) { + sim_slirp_close(slirp); + free(targs); + return NULL; + } + + /* Adjust the network prefix, update the guest's configuration. */ + cfg->vnetwork.s_addr = cfg->vhost.s_addr & cfg->vnetmask.s_addr; + if ((cfg->vhost.s_addr & ~cfg->vnetmask.s_addr) == 0) + cfg->vhost.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) | 2); + if (cfg->vdhcp_start.s_addr == 0) + cfg->vdhcp_start.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) | 15); + if (cfg->vnameserver.s_addr == 0) + cfg->vnameserver.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) | 3); + + /* Set the DNS search domains */ + cfg->vdnssearch = (const char **) slirp->dns_search_domains; + + /* Set the BOOTP file and TFTP path in the Slirp config: */ + cfg->bootfile = slirp->the_bootfile; + cfg->tftp_path = slirp->the_tftp_path; + + /* Initialize the callbacks: */ + slirp->pkt_callback = pkt_callback; + slirp->pkt_opaque = pkt_opaque; + glib_set_logging_hooks(&simh_logger); + + cbs->send_packet = invoke_sim_packet_callback; + cbs->guest_error = libslirp_guest_error; + cbs->clock_get_ns = sim_clock_get_ns; + cbs->register_poll_socket = register_poll_socket; + cbs->unregister_poll_socket = unregister_poll_socket; + cbs->notify = notify_callback; + cbs->timer_mod = simh_timer_mod; + cbs->timer_free = simh_timer_free; + cbs->timer_new_opaque = simh_timer_new_opaque; + + slirp->slirp_cxn = slirp_new(cfg, cbs, (void *) slirp); + + /* Capture the debugging info. */ + slirp->dbit = dptr->dctrl = dbit; + slirp->dptr = dptr; + + initialize_poll_fds(slirp); + + if (do_redirects(slirp, slirp->rtcp)) { + sim_slirp_close(slirp); + slirp = NULL; + } else { + sim_slirp_show(slirp, stdout); + if (sim_log != NULL && sim_log != stdout) + sim_slirp_show(slirp, sim_log); + if (sim_deb != NULL && sim_deb != stdout && sim_deb != sim_log) + sim_slirp_show(slirp, sim_deb); + } + + free(targs); + return slirp; +} + +void sim_slirp_close(SimSlirpNetwork *slirp) +{ + struct redir_tcp_udp *rtmp; + + if (slirp == NULL) + return; + + free(slirp->args); + free(slirp->the_tftp_path); + free(slirp->the_bootfile); + free(slirp->dns_search); + free(slirp->dns_search_domains); + + if (slirp->slirp_cxn != NULL) { + while ((rtmp = slirp->rtcp) != NULL) { + slirp_remove_hostfwd(slirp->slirp_cxn, rtmp->is_udp, rtmp->sim_local_inaddr, rtmp->sim_local_port); + slirp->rtcp = rtmp->next; + free(rtmp); + } + + slirp_cleanup(slirp->slirp_cxn); + } + + if (slirp->dptr != NULL) { + free(slirp->dptr->debflags); + slirp->dptr->debflags = slirp->original_debflags; + } + +#if SIM_USE_SELECT + free(slirp->lut); +#else + free(slirp->fds); +#endif + + pthread_mutex_destroy(&slirp->libslirp_access); + free(slirp); +} + +t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ + GLIB_UNUSED_PARAM(dptr); + GLIB_UNUSED_PARAM(uptr); + GLIB_UNUSED_PARAM(flag); + GLIB_UNUSED_PARAM(cptr); + + fprintf(st, "%s", + "NAT options:\n" + " DHCP{=dhcp_start_address} Enables DHCP server and specifies\n" + " guest LAN DHCP start IP address\n" + " BOOTFILE=bootfilename specifies DHCP returned Boot Filename\n" + " TFTP=tftp-base-path Enables TFTP server and specifies\n" + " base file path\n" + " NAMESERVER=nameserver_ipaddres specifies DHCP nameserver IP address\n" + " DNS=nameserver_ipaddres specifies DHCP nameserver IP address\n" + " DNSSEARCH=domain{:domain{:domain}} specifies DNS Domains search suffixes\n" + " GATEWAY=host_ipaddress{/masklen} specifies LAN gateway IP address\n" + " NETWORK=network_ipaddress{/masklen} specifies LAN network address\n" + " UDP=port:address:address's-port maps host UDP port to guest port\n" + " TCP=port:address:address's-port maps host TCP port to guest port\n" + " NODHCP disables DHCP server\n\n" + "Default NAT Options: GATEWAY=10.0.2.2, masklen=24(netmask is 255.255.255.0)\n" + " DHCP=10.0.2.15, NAMESERVER=10.0.2.3\n" + " Nameserver defaults to proxy traffic to host system's active nameserver\n\n" + "The 'address' field in the UDP and TCP port mappings are the simulated\n" + "(guest) system's IP address which, if DHCP allocated would default to\n" + "10.0.2.15 or could be statically configured to any address including\n" + "10.0.2.4 thru 10.0.2.14.\n\n" + "NAT limitations\n\n" + "There are four limitations of NAT mode which users should be aware of:\n\n" + " 1) ICMP protocol limitations:\n" + " Some frequently used network debugging tools (e.g. ping or tracerouting)\n" + " rely on the ICMP protocol for sending/receiving messages. While some\n" + " ICMP support is available on some hosts (ping may or may not work),\n" + " some other tools may not work reliably.\n\n" + " 2) Receiving of UDP broadcasts is not reliable:\n" + " The guest does not reliably receive broadcasts, since, in order to save\n" + " resources, it only listens for a certain amount of time after the guest\n" + " has sent UDP data on a particular port.\n\n" + " 3) Protocols such as GRE, DECnet, LAT and Clustering are unsupported:\n" + " Protocols other than TCP and UDP are not supported.\n\n" + " 4) Forwarding host ports < 1024 impossible:\n" + " On Unix-based hosts (e.g. Linux, Solaris, Mac OS X) it is not possible\n" + " to bind to ports below 1024 from applications that are not run by root.\n" + " As a result, if you try to configure such a port forwarding, the attach\n" + " will fail.\n\n" + "These limitations normally don't affect standard network use. But the\n" + "presence of NAT has also subtle effects that may interfere with protocols\n" + "that are normally working. One example is NFS, where the server is often\n" + "configured to refuse connections from non-privileged ports (i.e. ports not\n" + " below 1024).\n"); + return SCPE_OK; +} + +/* Initialize the select/poll file descriptor arrays. */ +static int initialize_poll_fds(SimSlirpNetwork *slirp) +{ +#if SIM_USE_SELECT + size_t i; + + FD_ZERO(&slirp->readfds); + FD_ZERO(&slirp->writefds); + FD_ZERO(&slirp->exceptfds); + + /* Start out with a generous number of LUT slots */ + slirp->lut_alloc = FDS_ALLOC_INIT; + slirp->lut = (SOCKET *) malloc(slirp->lut_alloc * sizeof(SOCKET)); + + for (i = 0; i < slirp->lut_alloc; ++i) + slirp->lut[i] = INVALID_SOCKET; +#else + /* poll()-based file descriptor polling. */ + slirp->n_fds = FDS_ALLOC_INIT; + slirp->fd_idx = 0; + slirp->fds = (sim_pollfd_t *) calloc(slirp->n_fds, sizeof(sim_pollfd_t)); +#endif + + return 0; +} + +/* Show NAT network statistics. */ +void sim_slirp_show(SimSlirpNetwork *slirp, FILE *st) +{ + struct redir_tcp_udp *rtmp; + const SlirpConfig *cfg = &slirp->slirp_config; + char *conn_info; + + if (slirp == NULL || slirp->slirp_cxn == NULL) + return; + + fprintf(st, "NAT args: %s\n", (slirp->args != NULL ? slirp->args : "(none given)")); + fprintf(st, "NAT network setup:\n"); + fprintf(st, " gateway = %s", sim_inet_ntoa4(&cfg->vhost)); + fprintf(st, " (%s)\n", sim_inet_ntoa4(&cfg->vnetmask)); +#if defined(AF_INET6) + fprintf(st, " IPv6 = %sabled.\n", slirp->slirp_config.in6_enabled ? "en" : "dis"); + if (slirp->slirp_config.in6_enabled) { + fprintf(st, " V6 Prefix = %s/%d\n", sim_inet_ntoa6(&slirp->slirp_config.vprefix_addr6), + slirp->slirp_config.vprefix_len); + fprintf(st, " V6 Gateway = %s\n", sim_inet_ntoa6(&slirp->slirp_config.vhost6)); + } +#endif + fprintf(st, " DNS = %s\n", sim_inet_ntoa4(&cfg->vnameserver)); + if (cfg->vdhcp_start.s_addr != 0) + fprintf(st, " dhcp_start = %s\n", sim_inet_ntoa4(&cfg->vdhcp_start)); + if (cfg->bootfile != NULL) + fprintf(st, " dhcp bootfile = %s\n", cfg->bootfile); + if (cfg->vdnssearch) { + const char **domains = cfg->vdnssearch; + + fprintf(st, " DNS domains = "); + while (*domains != NULL) { + fprintf(st, "%s%s", (domains != cfg->vdnssearch) ? ", " : "", *domains); + ++domains; + } + fprintf(st, "\n"); + } + if (cfg->tftp_path != NULL) + fprintf(st, " tftp prefix = %s\n", cfg->tftp_path); + rtmp = slirp->rtcp; + while (rtmp != NULL) { + fprintf(st, " redir %3s = %d:%s:%d\n", tcpudp[rtmp->is_udp], rtmp->sim_local_port, + sim_inet_ntoa4(&rtmp->sim_local_inaddr), rtmp->simh_host_port); + rtmp = rtmp->next; + } + + if ((conn_info = slirp_connection_info(slirp->slirp_cxn)) != NULL) { + fputs(conn_info, st); + free(conn_info); + } +} + +/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= + * The libslirp interface. + * + * libslirp has an inverted sense of input and output. "Input" means "input into libslirp", whereas "output" means + * output to the guest (the simulator via sim_ether and friends.) + *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ + +/* Invoke the SIMH packet callback. */ +static slirp_ssize_t invoke_sim_packet_callback(const void *buf, size_t len, void *opaque) +{ + SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; + + /* Note: Should really range check len for int bounds. */ + slirp->pkt_callback(slirp->pkt_opaque, buf, (int) len); + /* FIXME: the packet callback should tell us how many octets were written. + * For the time being, though, assume it was successful. */ + return len; +} + +int sim_slirp_send(SimSlirpNetwork *slirp, const char *msg, size_t len, int flags) +{ + GLIB_UNUSED_PARAM(flags); + + /* Just send the packet up to libslirp. */ + pthread_mutex_lock(&slirp->libslirp_access); + slirp_input(slirp->slirp_cxn, (const uint8_t *) msg, (int) len); + pthread_mutex_unlock(&slirp->libslirp_access); + + return (int) len; +} + +/* I/O thread notify callback from Slirp. Indicates that there's packet data waiting, I/O events + * are pending. */ +static void notify_callback(void *opaque) +{ + /* SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; */ + GLIB_UNUSED_PARAM(opaque); +} + +void sim_slirp_dispatch(SimSlirpNetwork *slirp) +{ + /* Outbound packets directly to slirp_input() and packets going to the + * simulator are handled in sim_slirp_select(). + * + * Packet traffic between the simulator and libslirp is handled in + * sim_slirp_select(). + */ + + GLIB_UNUSED_PARAM(slirp); +} + +int64_t sim_clock_get_ns(void *opaque) +{ + GLIB_UNUSED_PARAM(opaque); + + /* Internally, libslirp cuts the nanoseconds down to milliseconds. */ + return ((uint64_t) sim_os_msec()) * 10000000ull; +} diff --git a/sim_slirp/sim_slirp.h b/sim_slirp/sim_slirp.h new file mode 100644 index 000000000..f7a3aa068 --- /dev/null +++ b/sim_slirp/sim_slirp.h @@ -0,0 +1,28 @@ +#if !defined(SIM_SLIRP_H) + +#if defined(HAVE_SLIRP_NETWORK) + +#include "sim_defs.h" +#include "libslirp.h" + + +typedef struct sim_slirp SimSlirpNetwork; + +typedef void (*packet_callback)(void *opaque, const unsigned char *buf, int len); + +SimSlirpNetwork *sim_slirp_open (const char *args, void *pkt_opaque, packet_callback pkt_callback, DEVICE *dptr, uint32 dbit, char *errbuf, size_t errbuf_size); +void sim_slirp_close (SimSlirpNetwork *slirp); +int sim_slirp_send (SimSlirpNetwork *slirp, const char *msg, size_t len, int flags); +int sim_slirp_select (SimSlirpNetwork *slirp, int ms_timeout); +void sim_slirp_dispatch (SimSlirpNetwork *slirp); +t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +void sim_slirp_show (SimSlirpNetwork *slirp, FILE *st); + +/* Internal support functions: */ +int64_t sim_clock_get_ns(void *opaque); + +#endif /* HAVE_SLIRP_NETWORK */ + + +#define SIM_SLIRP_H +#endif diff --git a/sim_slirp/sim_slirp_network.h b/sim_slirp/sim_slirp_network.h new file mode 100644 index 000000000..c8afa8708 --- /dev/null +++ b/sim_slirp/sim_slirp_network.h @@ -0,0 +1,139 @@ +/* sim_network_state.h: Header file for the SimSlirpNetwork structure. + * + * Previously, this structure was locally defined in sim_slirp.c, which is + * inconvenient when referring to the structure members (scroll up to refer, + * scroll down to code.) Broken out as its own entity. + */ + +#include "sim_sock.h" +#include "sim_slirp.h" +#include "libslirp.h" + +#if !defined(SLIRP_NETWORK_STATE_H) + +#if !defined(USE_READER_THREAD) +#define pthread_mutex_init(mtx, val) +#define pthread_mutex_destroy(mtx) +#define pthread_mutex_lock(mtx) +#define pthread_mutex_unlock(mtx) +#define pthread_mutex_t int +#endif + +/* WSAPoll() is available on Windows Vista (> 0x0600) and higher, otherwise revert to + * select(). */ +#if SIM_USE_POLL && defined(WINVER) && WINVER < 0x0600 +# undef SIM_USE_SELECT +# undef SIM_USE_POLL +# define SIM_USE_SELECT 1 +# define SIM_USE_POLL 0 +#endif + +#if !defined(SIM_USE_SELECT) && !defined(SIM_USE_POLL) +# error "sim_slirp.c: Configuration error: define SIM_USE_SELECT, SIM_USE_POLL" +#endif + +#if SIM_USE_SELECT + SIM_USE_POLL > 1 +# error "sim_slirp.c: Configuration error: set one of SIM_USE_SELECT, SIM_USE_POLL to 1." +#endif + +/* Abstract the poll structure as sim_pollfd_t */ +#if SIM_USE_POLL +# if (defined(_WIN32) || defined(_WIN64)) + typedef WSAPOLLFD sim_pollfd_t; +# else +# include + typedef struct pollfd sim_pollfd_t; +# endif +#endif + +#if SIM_USE_SELECT +#define SIM_INVALID_MAX_FD ((slirp_os_socket) -1) +#endif + +/* sim_slirp debugging: */ +enum { + DBG_POLL = 0, + DBG_SOCKET = 1 +}; + +struct sim_slirp { + SlirpConfig slirp_config; + SlirpCb slirp_callbacks; + Slirp *slirp_cxn; + + char *args; + +#if defined(USE_READER_THREAD) + /* Access lock to libslirp. libslirp is not threaded or protected. */ + pthread_mutex_t libslirp_access; +#endif + + /* DNS search domains (argument copy) */ + char *dns_search; + char **dns_search_domains; + /* Boot file and TFTP path prefix (argument copy) */ + char *the_bootfile; + char *the_tftp_path; + /* UDP and TCP ports that SIMH proxies to the Slirp network */ + struct redir_tcp_udp *rtcp; + /* SIMH's packet callback and associated packet data. */ + packet_callback pkt_callback; + void *pkt_opaque; + + /* Debugging bitmasks: */ + DEBTAB *original_debflags; + size_t flag_offset; + + /* I/O event tracking/handling (used to be the GPollFD array): */ +#if SIM_USE_SELECT + /* select() needs a lookup table to map SOCKETs to an integer index. */ + fd_set readfds; + fd_set writefds; + fd_set exceptfds; + slirp_os_socket max_fd; + + /* Lookup table: */ + slirp_os_socket *lut; + size_t lut_alloc; +#else + /* More generally, this is for poll(). */ + + /* Poll file descriptor array */ + sim_pollfd_t *fds; + /* Next descriptor to use */ + size_t fd_idx; + /* Total allocated descriptors */ + size_t n_fds; +#endif + + /* SIMH debug info: */ + DEVICE *dptr; + uint32 dbit; +}; + +/* Simulator -> host network redirection state. */ +struct redir_tcp_udp { + int is_udp; + /* SIMH host port, e.g., 2223. */ + int simh_host_port; + /* The simulator's IP address, e.g., 10.0.2.4 or 10.0.2.15 */ + struct in_addr sim_local_inaddr; + /* The simulator's port, e.g., 23 */ + int sim_local_port; + struct redir_tcp_udp *next; +}; + +/* File descriptor array initial allocation, incremental (linear) allocation. */ +#define FDS_ALLOC_INIT 32 +#define FDS_ALLOC_INCR 32 + +/* slirp_poll.c externs: */ +void register_poll_socket(slirp_os_socket fd, void *opaque); +void unregister_poll_socket(slirp_os_socket fd, void *opaque); + +void *simh_timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque); +void simh_timer_free(void *the_timer, void *opaque); +void simh_timer_mod(void *timer, int64_t expire_time, void *opaque); + +#define SLIRP_NETWORK_STATE_H +#endif diff --git a/sim_slirp/slirp_poll.c b/sim_slirp/slirp_poll.c new file mode 100644 index 000000000..ef7e0def6 --- /dev/null +++ b/sim_slirp/slirp_poll.c @@ -0,0 +1,553 @@ +/* sim_slirp.c: + ------------------------------------------------------------------------------ + Copyright (c) 2024, B. Scott Michel + + 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 AUTHOR 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. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This module encapsulates the poll()/select() interface to libslirp. + +*/ + +#include +#include /* Paranoia for Win32/64 */ +#include + +#include "libslirp.h" +#include "sim_defs.h" +#include "scp.h" +#include "sim_sock.h" +#include "sim_slirp_network.h" +#include "sim_printf_fmts.h" + +#if defined(HAVE_INTTYPES_H) +#include +#endif + +#if defined(_WIN64) +# if defined(PRIu64) +# define SIM_PRIsocket PRIu64 +# else +# define SIM_PRIsocket "llu" +# endif +#elif defined(_WIN32) +# if defined(PRIu32) +# define SIM_PRIsocket PRIu32 +# else +# define SIM_PRIsocket "lu" +# endif +#else +# define SIM_PRIsocket "u" +#endif + +/* Forward decl's: */ +static int add_poll_callback(slirp_os_socket fd, int events, void *opaque); +static int get_events_callback(int idx, void *opaque); +static void simh_timer_check(Slirp *slirp); + +/* Protocol functions: */ +static void initialize_poll(SimSlirpNetwork *slirp, uint32 tmo, struct timeval *tv); +static int do_poll(SimSlirpNetwork *slirp, int ms_timeout, struct timeval *timeout); +static void report_error(SimSlirpNetwork *slirp); + +static inline uint32_t slirp_dbg_mask(const SimSlirpNetwork *slirp, size_t flag) +{ + return (slirp != NULL && slirp->dptr != NULL) ? slirp->dptr->debflags[slirp->flag_offset + flag].mask : 0; +} + +#if SIM_USE_SELECT +/* Socket identifier pretty-printer: */ +static const char *print_socket(slirp_os_socket s) +{ + static char retbuf[64]; + +#if defined(_WIN64) + sprintf(retbuf, "%llu", s); +#elif defined(_WIN32) + sprintf(retbuf, "%u", s); +#else + sprintf(retbuf, "%d", s); +#endif + + return retbuf; +} +#endif + +/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= + * The libslirp poll()/select() interface: + * + * sim_slirp_select() is simply a driver function that invokes three "protocol" functions to initialize, poll and + * report an error. The protocol functions isolate functionality without making sim_slirp_select() overly complex. + *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ + +int sim_slirp_select(SimSlirpNetwork *slirp, int ms_timeout) +{ + int retval = 0; + uint32 slirp_timeout = ms_timeout; + struct timeval tv; + + if (slirp == NULL) + /* Will cause the reader thread to exit! */ + return -1; + + /* Note: It's a generally good practice to acquire a mutex and hold it until an operation + * completes. In this case, though, poll()/select() will block and prevent outbound writes + * via sim_slirp_send(), which itself blocks waiting for the mutex. + * + * Hold the mutex only when calling libslirp functions, reacquire when needed. + */ + pthread_mutex_lock(&slirp->libslirp_access); + + /* Check on expiring libslirp timers. */ + simh_timer_check(slirp->slirp_cxn); + + /* Ask libslirp to poll for I/O events. */ + initialize_poll(slirp, slirp_timeout, &tv); + slirp_pollfds_fill_socket(slirp->slirp_cxn, &slirp_timeout, add_poll_callback, slirp); + + pthread_mutex_unlock(&slirp->libslirp_access); + + retval = do_poll(slirp, ms_timeout, &tv); + + if (retval > 0) { + /* The libslirp idiom invokes slirp_pollfds_poll within the same function as slirp_pollfds_fill(). */ + pthread_mutex_lock(&slirp->libslirp_access); + slirp_pollfds_poll(slirp->slirp_cxn, 0, get_events_callback, slirp); + pthread_mutex_unlock(&slirp->libslirp_access); + } else if (retval < 0) { + report_error(slirp); + } + + return retval; +} + +/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= + * "Protocol" functions. These functions abide by the one function definition rule. + *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ + +#if (defined(_WIN32) || defined(_WIN64)) && SIM_USE_POLL +static inline int poll(WSAPOLLFD *fds, size_t n_fds, int timeout) +{ + return WSAPoll(fds, (ULONG) n_fds, timeout); +} +#endif + +static void initialize_poll(SimSlirpNetwork *slirp, uint32 tmo, struct timeval *tv) +{ +#if SIM_USE_SELECT + tv->tv_sec = tmo / 1000; + tv->tv_usec = (tmo % 1000) * 1000; + + FD_ZERO(&slirp->readfds); + FD_ZERO(&slirp->writefds); + FD_ZERO(&slirp->exceptfds); + + slirp->max_fd = SIM_INVALID_MAX_FD; +#elif SIM_USE_POLL + /* Reinitialize and reset */ + memset(slirp->fds, 0, slirp->fd_idx * sizeof(sim_pollfd_t)); + slirp->fd_idx = 0; +#endif +} + +static int do_poll(SimSlirpNetwork *slirp, int ms_timeout, struct timeval *timeout) +{ + int retval = 0; + +#if SIM_USE_SELECT +# if defined(_WIN32) + /* Windows: The first argument to select(), nfds, is ignored by winsock2. You could pass a + * random value and winsock2 wouldn't care. 0xdeadbeef seems appropriate. + * + * The only way to know whether there is work to be done is to examine the fd_count member in + * the fd_set structure. + */ + if (slirp->readfds.fd_count + slirp->writefds.fd_count + slirp->exceptfds.fd_count > 0) { + retval = select(/* ignored */ 0xdeadbeef, &slirp->readfds, &slirp->writefds, &slirp->exceptfds, timeout); + if (retval > 0) + sim_debug(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, + "do_poll(): select() returned %d (read: %u, write: %u, except: %u)\n", + retval, slirp->readfds.fd_count, slirp->writefds.fd_count, slirp->exceptfds.fd_count); + } +# else + if (slirp->max_fd != SIM_INVALID_MAX_FD) { + retval = select(slirp->max_fd + 1, &slirp->readfds, &slirp->writefds, &slirp->exceptfds, &imeout); + if (retval > 0) + sim_debug(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, "do_poll(): select() returned %d\n", retval); + } +# endif +#elif SIM_USE_POLL + if (slirp->fd_idx > 0) { + sim_debug(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, + "poll()-ing for %" SIZE_T_FMT "u sockets\n", slirp->fd_idx); + + retval = poll(slirp->fds, slirp->fd_idx, ms_timeout); + if (retval > 0) + sim_debug(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, "do_poll(): poll() returned %d\n", retval); + } +#endif + + return retval; +} + +static void report_error(SimSlirpNetwork *slirp) +{ +#if defined(_WIN32) + char *wsaMsgBuffer = NULL; + int wsaError = WSAGetLastError(); + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* The user default language */ + (LPTSTR) &wsaMsgBuffer, + 0, + NULL); + + if (sim_deb != stdout && sim_deb != stderr) { + fprintf(stderr, "sim_slirp_select() winsock error: %d (%s)", wsaError, wsaMsgBuffer); + fflush(stderr); + } + sim_messagef(SCPE_IERR, "sim_slirp_select(): WSAGetLastError() %d (%s)\n", wsaError, wsaMsgBuffer); + LocalFree(wsaMsgBuffer); +#else + sim_messagef(SCPE_IERR, "sim_slirp_select(): do_poll() -- errno = %d, %s\n", errno, g_strerror(errno)); +#endif +} + +/* Add new socket file descriptors to the Slirp I/O event tracking state. */ +void register_poll_socket(slirp_os_socket fd, void *opaque) +{ +#if SIM_USE_SELECT + SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; + size_t i; + + for (i = 0; i < slirp->lut_alloc; ++i) { + if (slirp->lut[i] == INVALID_SOCKET) { + slirp->lut[i] = fd; + break; + } + } + + if (i == slirp->lut_alloc) { + SOCKET *new_lut; + size_t j = slirp->lut_alloc; + + /* Linear growth. */ + slirp->lut_alloc += FDS_ALLOC_INCR; + new_lut = (SOCKET *) realloc(slirp->lut, slirp->lut_alloc * sizeof(SOCKET)); + ASSURE(new_lut != NULL); + for (/* empty */; j < slirp->lut_alloc; ++j) + new_lut[j] = INVALID_SOCKET; + + slirp->lut = new_lut; + slirp->lut[i] = fd; + } + + sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, "register_poll_socket(%s) index %" SIZE_T_FMT "d\n", + print_socket(fd), i); +#elif SIM_USE_POLL + /* Not necessary for poll(). */ + (void) opaque; +#endif +} + +/* Reap a disused socket. */ +void unregister_poll_socket(slirp_os_socket fd, void *opaque) +{ + GLIB_UNUSED_PARAM(opaque); + +#if SIM_USE_SELECT + SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; + size_t i; + + for (i = 0; i < slirp->lut_alloc; ++i) { + if (slirp->lut[i] == fd) { + slirp->lut[i] = INVALID_SOCKET; + sim_debug(slirp_dbg_mask(slirp, DBG_SOCKET), slirp->dptr, + "unregister_poll_socket(%s) index %" SIZE_T_FMT "d\n", print_socket(fd), i); + break; + } + } +#elif SIM_USE_POLL + /* Not necessary for poll(). */ + (void) opaque; +#endif +} + +/* Debugging output for add/get event callbacks. */ +static void poll_debugging(uint32_t dbits, DEVICE *dev, const char *prefix, int events) +{ + static struct transtab { + int slirp_event; + const char *slirp_event_str; + } translations[] = { + { SLIRP_POLL_IN, " SLIRP_POLL_IN" }, + { SLIRP_POLL_OUT, " SLIRP_POLL_OUT" }, + { SLIRP_POLL_PRI, " SLIRP_POLL_PRI" }, + { SLIRP_POLL_ERR, " SLIRP_POLL_ERR" }, + { SLIRP_POLL_HUP, " SLIRP_POLL_HUP" } + }; + static const size_t n_translations = sizeof(translations) / sizeof(translations[0]); + char *msg = calloc(96 + strlen(prefix) + 1, sizeof(char)); + + strcpy(msg, prefix); + if (events != 0) { + size_t i; + + strcat(msg, ": "); + for (i = 0; i < n_translations; ++i) { + if (translations[i].slirp_event & events) + strcat(msg, translations[i].slirp_event_str); + } + } else { + strcat(msg, ": [no events]"); + } + strcat(msg, "\n"); + + /* Don't need to escape "%"-s in msg, since there aren't any. */ + sim_debug(dbits, dev, "%s", msg); + if (sim_deb != NULL) + fflush(sim_deb); + free(msg); +} + +/* select()/poll() callback: For the sockets that Slirp (slirp_cxn) needs to examine for + * events, this callback adds them to the select file descriptor set or the poll array. */ +static int add_poll_callback(slirp_os_socket fd, int events, void *opaque) +{ + SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; + int retval = -1; + char prefix[128]; + +#if SIM_USE_SELECT + size_t i; + const int event_mask = (SLIRP_POLL_IN | SLIRP_POLL_OUT | SLIRP_POLL_PRI); + + for (i = 0; i < slirp->lut_alloc; ++i) { + if (slirp->lut[i] == fd) { + if (events & SLIRP_POLL_IN) { + FD_SET(fd, &slirp->readfds); + } + if (events & SLIRP_POLL_OUT) { + FD_SET(fd, &slirp->writefds); + } + if (events & SLIRP_POLL_PRI) { + FD_SET(fd, &slirp->exceptfds); + } + + retval = (int) i; + + if (slirp->max_fd == SIM_INVALID_MAX_FD || fd > slirp->max_fd) + slirp->max_fd = fd; + + break; + } + + sprintf(prefix, "add_poll_callback(%s)/select (0x%04x)", print_socket(fd), events & event_mask); + poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, events & event_mask); + } +#elif SIM_USE_POLL + if (slirp->fd_idx == slirp->n_fds) { + size_t j = slirp->n_fds; + sim_pollfd_t *new_fds; + + slirp->n_fds += FDS_ALLOC_INCR; + new_fds = (sim_pollfd_t *) realloc(slirp->fds, slirp->n_fds * sizeof(sim_pollfd_t)); + ASSURE(new_fds != NULL); + memset(new_fds + j, 0, (slirp->n_fds - j) * sizeof(sim_pollfd_t)); + slirp->fds = new_fds; + } + + short poll_events = 0; + + if (events & SLIRP_POLL_IN) { + poll_events |= POLLIN; + } + if (events & SLIRP_POLL_OUT) { + poll_events |= POLLOUT; + } +#if !defined(_WIN32) && !defined(_WIN64) + /* Not supported on Windows. Unless you like EINVAL. :-) */ + if (events & SLIRP_POLL_PRI) { + poll_events |= POLLPRI; + } + if (events & SLIRP_POLL_ERR) { + poll_events |= POLLERR; + } + if (events & SLIRP_POLL_HUP) { + poll_events |= POLLHUP; + } +#endif + + sprintf(prefix, "add_poll_callback(%" SIM_PRIsocket ")/poll (0x%04x)", fd, events); + poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, events); + + slirp->fds[slirp->fd_idx].fd = fd; + slirp->fds[slirp->fd_idx].events = poll_events; + slirp->fds[slirp->fd_idx].revents = 0; + + retval = (int) slirp->fd_idx++; +#endif + + return retval; +} + +static int get_events_callback(int idx, void *opaque) +{ + const SimSlirpNetwork *slirp = (SimSlirpNetwork *) opaque; + int event = 0; + char prefix[128]; + +#if SIM_USE_SELECT + const int event_mask = (SLIRP_POLL_IN | SLIRP_POLL_OUT | SLIRP_POLL_PRI); + + if (idx >= 0 && (size_t) idx < slirp->lut_alloc) { + slirp_os_socket fd = slirp->lut[idx]; + + if (FD_ISSET(fd, &slirp->readfds)) { + event |= SLIRP_POLL_IN; + } + if (FD_ISSET(fd, &slirp->writefds)) { + event |= SLIRP_POLL_OUT; + } + if (FD_ISSET(fd, &slirp->exceptfds)) { + event |= SLIRP_POLL_PRI; + } + + sprintf(prefix, "get_events_callback(%s)/select (0x%04x)", print_socket(fd), event & event_mask); + poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, event & event_mask); + } +#elif SIM_USE_POLL + if (idx < slirp->fd_idx) { + short poll_events = slirp->fds[idx].revents; + + if (poll_events & POLLIN) { + event |= SLIRP_POLL_IN; + } + if (poll_events & POLLOUT) { + event |= SLIRP_POLL_OUT; + } + if (poll_events & POLLPRI) { + event |= SLIRP_POLL_PRI; + } + if (poll_events & POLLERR) { + event |= SLIRP_POLL_ERR; + } + if (poll_events & POLLHUP) { + event |= SLIRP_POLL_HUP; + } + + sprintf(prefix, "get_events_callback(%" SIM_PRIsocket ")/poll (0x%04x)", slirp->fds[idx].fd, event); + poll_debugging(slirp_dbg_mask(slirp, DBG_POLL), slirp->dptr, prefix, event); + } +#endif + + return event; +} + + +/*~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~= + * libslirp timer management: This exclusively applies to IPv6. + *~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=*/ + +static struct slirp_timer_s { + SlirpTimerId id; /*< libslirp's timer identifier */ + void *cb_opaque; /*< Opaque thing to pass to the callback */ + int64_t expire_time_ns; /*< Expiration time, nanoseconds */ + struct slirp_timer_s *next; +} *slirp_timer_list = NULL; + +void *simh_timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque) +{ + struct slirp_timer_s *retval = (struct slirp_timer_s *) calloc(1, sizeof(struct slirp_timer_s)); + + GLIB_UNUSED_PARAM(opaque); + + retval->id = id; + retval->cb_opaque = cb_opaque; + + retval->next = slirp_timer_list; + slirp_timer_list = retval; + + return retval; +} + +void simh_timer_free(void *the_timer, void *opaque) +{ + struct slirp_timer_s *timer = the_timer; + struct slirp_timer_s **t; + + GLIB_UNUSED_PARAM(opaque); + + for (t = &slirp_timer_list; *t != NULL && *t != timer; t = &((*t) ->next)) + /* empty */; + + if (*t != NULL) + *t = timer->next; + + free(timer); +} + +void simh_timer_mod(void *the_timer, int64_t expire_time, void *opaque) +{ + struct slirp_timer_s *timer = the_timer; + struct slirp_timer_s **t; + + GLIB_UNUSED_PARAM(opaque); + + /* Upconvert to nanoseconds, unexpire. */ + timer->expire_time_ns = expire_time * 1000ll * 1000ll; + + /* Remove from the list. */ + for (t = &slirp_timer_list; *t != NULL && *t != timer; t = &((*t)->next)) + /* empty */; + + if (*t != NULL) + *t = timer->next; + + /* Reinsert in sorted order. */ + for (t = &slirp_timer_list; *t != NULL && expire_time < (*t)->expire_time_ns; t = &((*t)->next)) + /* empty */; + + timer->next = *t; + *t = timer; +} + +void simh_timer_check(Slirp *slirp) +{ + int64_t time_now = sim_clock_get_ns(NULL); + struct slirp_timer_s *t; + + GLIB_UNUSED_PARAM(slirp); + + for (t= slirp_timer_list; t != NULL && t->expire_time_ns <= time_now; /* empty */) { + /* libslirp's timer function that invoked us might call simh_timer_mod(), which + * potentially moves t->next. Keep track of the next element separately so that + * we keep synchronized with the list's order. */ + struct slirp_timer_s *t_next = t->next; + + slirp_handle_timer(slirp, t->id, t->cb_opaque); + t = t_next; + } +} diff --git a/sim_sock.c b/sim_sock.c index cd67895c3..071b1af4e 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -51,10 +51,6 @@ extern "C" { #include #include -#if defined(AF_INET6) && defined(_WIN32) -#include -#endif - #ifdef SIM_HAVE_DLOPEN #include #endif @@ -872,6 +868,83 @@ return sim_parse_addr (cptr, host, hostlen, default_host, port, port_len, defaul } +/* Convert IP address to its printable format. Supports IPv6. */ +static const char *sim_inet_ntoa(int family, const void *addr) +{ +#if !defined(WINVER) || WINVER >= 0x0603 + static char fmt_address[48]; + + inet_ntop(family, addr, fmt_address, sizeof(fmt_address) - 1); + return fmt_address; +#else + /* Squelch unused arg warning */ + (void)(family); + + return inet_ntoa(*((const struct in_addr *) addr)); +#endif +} + +/* Convert IPv4 address to printable format. */ +const char *sim_inet_ntoa4(const struct in_addr *v4addr) +{ + return sim_inet_ntoa(AF_INET, v4addr); +} + +/* Convert IPv6 address to printable format. */ +#if defined(AF_INET6) +const char *sim_inet_ntoa6(const struct in6_addr *addr) +{ +# if !defined(WINVER) || WINVER >= 0x0603 + return sim_inet_ntoa(AF_INET6, addr); +# else + struct sockaddr_storage ss; + DWORD s = INET6_ADDRSTRLEN; + static char dst[INET6_ADDRSTRLEN + 1]; + + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = AF_INET6; + ((struct sockaddr_in6 *) &ss)->sin6_addr = *addr; + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL; +# endif +} +#else +const char *sim_inet_ntoa6(const void *v6addr) +{ + return "IPv6 not supported"; +} +#endif + + +#if defined(WINVER) && WINVER < 0x0601 +/* Windows 7 and below stub, used by sim_slirp/sim_slirp.c */ + +int sim_inet_pton(int af, const char *src, void *dst) +{ + struct sockaddr_storage ss; + int size = sizeof(ss); + char src_copy[INET6_ADDRSTRLEN+1]; + + ZeroMemory(&ss, sizeof(ss)); + /* stupid non-const API */ + strncpy (src_copy, src, INET6_ADDRSTRLEN+1); + src_copy[INET6_ADDRSTRLEN] = 0; + + if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { + switch(af) { + case AF_INET: + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + case AF_INET6: + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + } + return 0; +} + +#endif + + void sim_init_sock (void) { #if defined (_WIN32) diff --git a/sim_sock.h b/sim_sock.h index 7ae40f59f..7daab6274 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -53,6 +53,8 @@ extern "C" { #if defined (_WIN32) /* Windows */ #include #include +#include +#include #elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ #include /* for fcntl, getpid */ @@ -69,7 +71,10 @@ extern "C" { #define WSAGetLastError() errno /* Windows macros */ #define WSASetLastError(err) errno = err +#if !defined(closesocket) +/* libslirp defines this too. */ #define closesocket close +#endif #define SOCKET int #if defined(__hpux) #define WSAEWOULDBLOCK EAGAIN @@ -111,31 +116,46 @@ extern "C" { #define sim_printf printf #endif -int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, - char *port, size_t port_len, const char *default_port, - const char *validate_addr); -int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, - char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port); -int sim_addr_acl_check (const char *validate_addr, const char *acl); -#define SIM_SOCK_OPT_REUSEADDR 0x0001 -#define SIM_SOCK_OPT_DATAGRAM 0x0002 -#define SIM_SOCK_OPT_NODELAY 0x0004 -#define SIM_SOCK_OPT_BLOCKING 0x0008 -SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags); -#define sim_master_sock(hostport, parse_status) sim_master_sock_ex(hostport, parse_status, ((sim_switches & SWMASK ('U')) ? SIM_SOCK_OPT_REUSEADDR : 0)) -SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags); +int sim_parse_addr(const char *cptr, char *host, size_t hostlen, const char *default_host, + char *port, size_t port_len, const char *default_port, + const char *validate_addr); +int sim_parse_addr_ex(const char *cptr, char *host, size_t hostlen, const char *default_host, + char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port); +const char *sim_inet_ntoa4(const struct in_addr *addr); +#if defined(AF_INET6) +const char *sim_inet_ntoa6(const struct in6_addr *addr); +#else +const char *sim_inet_ntoa6(const void *addr); +#endif +#if defined(WINVER) && WINVER < 0x0601 +int sim_inet_pton(int af, const char *src, void *dst); +#endif + +int sim_addr_acl_check(const char *validate_addr, const char *acl); + +#define SIM_SOCK_OPT_REUSEADDR 0x0001 +#define SIM_SOCK_OPT_DATAGRAM 0x0002 +#define SIM_SOCK_OPT_NODELAY 0x0004 +#define SIM_SOCK_OPT_BLOCKING 0x0008 +SOCKET sim_master_sock_ex(const char *hostport, int *parse_status, int opt_flags); + +#define sim_master_sock(hostport, parse_status) sim_master_sock_ex(hostport, parse_status, ((sim_switches & SWMASK('U')) ? SIM_SOCK_OPT_REUSEADDR : 0)) +SOCKET sim_connect_sock_ex(const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags); + #define sim_connect_sock(hostport, default_host, default_port) sim_connect_sock_ex(NULL, hostport, default_host, default_port, SIM_SOCK_OPT_BLOCKING) -SOCKET sim_accept_conn_ex (SOCKET master, char **connectaddr, int opt_flags); +SOCKET sim_accept_conn_ex(SOCKET master, char **connectaddr, int opt_flags); + #define sim_accept_conn(master, connectaddr) sim_accept_conn_ex(master, connectaddr, 0) -int sim_check_conn (SOCKET sock, int rd); -int sim_read_sock (SOCKET sock, char *buf, int nbytes); -int sim_write_sock (SOCKET sock, const char *msg, int nbytes); -void sim_close_sock (SOCKET sock); -const char *sim_get_err_sock (const char *emsg); -SOCKET sim_err_sock (SOCKET sock, const char *emsg); -int sim_getnames_sock (SOCKET sock, char **socknamebuf, char **peernamebuf); -void sim_init_sock (void); -void sim_cleanup_sock (void); + +int sim_check_conn(SOCKET sock, int rd); +int sim_read_sock(SOCKET sock, char *buf, int nbytes); +int sim_write_sock(SOCKET sock, const char *msg, int nbytes); +void sim_close_sock(SOCKET sock); +const char *sim_get_err_sock(const char *emsg); +SOCKET sim_err_sock(SOCKET sock, const char *emsg); +int sim_getnames_sock(SOCKET sock, char **socknamebuf, char **peernamebuf); +void sim_init_sock(void); +void sim_cleanup_sock(void); #ifdef __cplusplus } diff --git a/slirp/CMakeLists.txt b/slirp/CMakeLists.txt deleted file mode 100644 index 4875471cf..000000000 --- a/slirp/CMakeLists.txt +++ /dev/null @@ -1,56 +0,0 @@ -## Compile SLirp as its own standalone library - -if (WITH_NETWORK) - set(SLIRP_SOURCES - "${CMAKE_SOURCE_DIR}/slirp/arp_table.c" - "${CMAKE_SOURCE_DIR}/slirp/bootp.c" - "${CMAKE_SOURCE_DIR}/slirp/bootp.h" - "${CMAKE_SOURCE_DIR}/slirp/cksum.c" - "${CMAKE_SOURCE_DIR}/slirp/dnssearch.c" - "${CMAKE_SOURCE_DIR}/slirp/if.c" - "${CMAKE_SOURCE_DIR}/slirp/ip_icmp.c" - "${CMAKE_SOURCE_DIR}/slirp/ip_input.c" - "${CMAKE_SOURCE_DIR}/slirp/ip_output.c" - "${CMAKE_SOURCE_DIR}/slirp/mbuf.c" - "${CMAKE_SOURCE_DIR}/slirp/misc.c" - "${CMAKE_SOURCE_DIR}/slirp/sbuf.c" - "${CMAKE_SOURCE_DIR}/slirp/slirp.c" - "${CMAKE_SOURCE_DIR}/slirp/socket.c" - "${CMAKE_SOURCE_DIR}/slirp/tcp_input.c" - "${CMAKE_SOURCE_DIR}/slirp/tcp_output.c" - "${CMAKE_SOURCE_DIR}/slirp/tcp_subr.c" - "${CMAKE_SOURCE_DIR}/slirp/tcp_timer.c" - "${CMAKE_SOURCE_DIR}/slirp/tftp.c" - "${CMAKE_SOURCE_DIR}/slirp/udp.c" - "${CMAKE_SOURCE_DIR}/slirp_glue/glib_qemu_stubs.c" - "${CMAKE_SOURCE_DIR}/slirp_glue/sim_slirp.c") - - add_library(slirp STATIC "${SLIRP_SOURCES}") - target_compile_definitions(slirp - PRIVATE - HAVE_SLIRP_NETWORK - USE_SIMH_SLIRP_DEBUG - $<$,$>>: - _WINSOCK_DEPRECATED_NO_WARNINGS - _CRT_NONSTDC_NO_WARNINGS - _CRT_SECURE_NO_WARNINGS> - INTERFACE - HAVE_SLIRP_NETWORK - USE_SIMH_SLIRP_DEBUG) - - target_include_directories(slirp - PRIVATE - "${CMAKE_SOURCE_DIR}" - "${CMAKE_SOURCE_DIR}/slirp" - "${CMAKE_SOURCE_DIR}/slirp_glue/qemu" - PUBLIC - "${CMAKE_SOURCE_DIR}/slirp_glue" - $<$:${CMAKE_SOURCE_DIR}/slirp_glue/qemu/win32/include>) - - target_compile_options(slirp PRIVATE ${EXTRA_TARGET_CFLAGS}) - target_link_options(slirp PRIVATE ${EXTRA_TARGET_LFLAGS}) - target_link_libraries(slirp PUBLIC $<$:Iphlpapi>) -else (WITH_NETWORK) - # Otherwise, just make slirp an empty interface library. - add_library(slirp INTERFACE) -endif (WITH_NETWORK) diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs deleted file mode 100644 index f9d40e3bb..000000000 --- a/slirp/Makefile.objs +++ /dev/null @@ -1,3 +0,0 @@ -common-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o dnssearch.o -common-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o -common-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o arp_table.o diff --git a/slirp/README b/slirp/README deleted file mode 100644 index 23be7e44a..000000000 --- a/slirp/README +++ /dev/null @@ -1,14 +0,0 @@ -October 6, 2015 - -This version of slirp is a fork from QEMU Version 2.4.0.1 slirp implementation -Taken from commit 83c92b45140be773f0c5545dddea35a89db1ad03 -in the qemu repository git://git.qemu.orgu/qemu.git - -The goal being to absolutely mininize changes from the base qemu provided code -so that the more active changes being done by the qemu folks can easily be -adopted in the future by directly replacing the revised modules. This is -achieved by careful manipulation of the include files provided in the -../slirp_glue directory so that things are stubbed out locally and the minimal -capabilities needed for sim_ether integration are provided. - -Thanks to Fabrice Bellard. diff --git a/slirp/bootp.h b/slirp/bootp.h deleted file mode 100644 index ece1a3035..000000000 --- a/slirp/bootp.h +++ /dev/null @@ -1,126 +0,0 @@ -/* bootp/dhcp defines */ -#ifndef SLIRP_BOOTP_H -#define SLIRP_BOOTP_H 1 - -#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_MESSAGE 56 -#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 DHCPNAK 6 - -#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 - -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]; -}; - -typedef struct { - uint16_t allocated; - uint8_t macaddr[6]; -} BOOTPClient; - -#define NB_BOOTP_CLIENTS 16 - -void bootp_input(struct mbuf *m); - -#endif diff --git a/slirp/cksum.c b/slirp/cksum.c deleted file mode 100644 index a95ac9403..000000000 --- a/slirp/cksum.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 - -/* - * 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 mbuf, 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]; \ - (void)ADDCARRY(sum);} - -int cksum(struct mbuf *m, int len) -{ - register uint16_t *w; - register int sum = 0; - register int mlen = 0; - int byte_swapped = 0; - - union { - uint8_t c[2]; - uint16_t s; - } s_util; - union { - uint16_t s[2]; - uint32_t l; - } l_util; - - if (m->m_len == 0) - goto cont; - w = mtod(m, uint16_t *); - - mlen = m->m_len; - - if (len < mlen) - mlen = len; -#ifdef DEBUG - len -= mlen; -#endif - /* - * Force to even boundary. - */ - if ((1 & (uintptr_t)w) && (mlen > 0)) { - REDUCE; - sum <<= 8; - s_util.c[0] = *(uint8_t *)w; - w = (uint16_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; - if (mlen == -1) { - s_util.c[1] = *(uint8_t *)w; - sum += s_util.s; - mlen = 0; - } else - - mlen = -1; - } else if (mlen == -1) - s_util.c[0] = *(uint8_t *)w; - -cont: -#ifdef DEBUG - if (len) { - DEBUG_ERROR("cksum: out of data\n"); - DEBUG_ERROR(" len = %d\n", len); - } -#endif - if (mlen == -1) { - /* The last mbuf 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/slirp/debug.h b/slirp/debug.h deleted file mode 100644 index aba02476c..000000000 --- a/slirp/debug.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef SLIRP_DEBUG_H -#define SLIRP_DEBUG_H - -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#define DBG_CALL 0x1 -#define DBG_MISC 0x2 -#define DBG_ERROR 0x4 - -extern int slirp_debug; - -#ifndef USE_SIMH_SLIRP_DEBUG /* simh build indicator */ - -#ifdef DEBUG - -#define dfd stderr - -#define DEBUG_CALL(x) do {if (slirp_debug & DBG_CALL) { fprintf (dfd, "%s...\n", x); fflush(dfd); };} while (0) -#define DEBUG_ARG(x, y) do {if (slirp_debug & DBG_CALL) { fprintf (dfd, x, y); _sim_debug_device (slirp_dbit, slirp_dptr, "\n"); fflush(dfd); };} while (0) -#define DEBUG_ARGS(...) do {if (slirp_debug & DBG_CALL) { fprintf (dfd, ## __VA_ARGS__); fflush(dfd); };} while (0) -#define DEBUG_MISC(...) do {if (slirp_debug & DBG_MISC) { fprintf (dfd, ## __VA_ARGS__); fflush(dfd); };} while (0) -#define DEBUG_ERROR(...) do {if (slirp_debug & DBG_ERROR) { fprintf (dfd, ## __VA_ARGS__); fflush(dfd); };} while (0) -#define DPRINTF(fmt, ...) do {if (slirp_debug & DBG_CALL) { fprintf (dfd, fmt, ## __VA_ARGS__); fflush(dfd);} while (0) - -#else - -#define DEBUG_CALL(x) do {} while (0) -#define DEBUG_ARG(x, y) do {} while (0) -#define DEBUG_ARGS(...) do {} while (0) -#define DEBUG_MISC(...) do {} while (0) -#define DEBUG_ERROR(...) do {} while (0) -#define DPRINTF(fmt, ...) do {} while (0) - -#endif - -#else /* defined(USE_SIMH_SLIRP_DEBUG) */ - -#include -#define DEVICE void - -#if defined(__cplusplus) -extern "C" { -#endif -extern void *slirp_dptr; -extern unsigned int slirp_dbit; - -extern void _sim_debug_device (unsigned int dbits, DEVICE* dptr, const char* fmt, ...); - -#define DEBUG_CALL(x) do {if (slirp_debug & DBG_CALL) { _sim_debug_device (slirp_dbit, slirp_dptr, "%s...\n", x); };} while (0) -#define DEBUG_ARG(x, y) do {if (slirp_debug & DBG_CALL) {_sim_debug_device (slirp_dbit, slirp_dptr, x, y); _sim_debug_device (slirp_dbit, slirp_dptr, "\n"); };} while (0) -#define DEBUG_ARGS(...) do {if (slirp_debug & DBG_CALL) { _sim_debug_device (slirp_dbit, slirp_dptr, ## __VA_ARGS__); };} while (0) -#define DEBUG_MISC(...) do {if (slirp_debug & DBG_MISC) { _sim_debug_device (slirp_dbit, slirp_dptr, ## __VA_ARGS__); };} while (0) -#define DEBUG_ERROR(...) do {if (slirp_debug & DBG_ERROR) { _sim_debug_device (slirp_dbit, slirp_dptr, ## __VA_ARGS__); };} while (0) -#define DPRINTF(fmt, ...) do {if (slirp_debug & DBG_CALL) { _sim_debug_device (slirp_dbit, slirp_dptr, fmt, ## __VA_ARGS__); };} while (0) - -#if defined(__cplusplus) -} -#endif - -#endif - -#endif diff --git a/slirp/if.c b/slirp/if.c deleted file mode 100644 index a7ecd8997..000000000 --- a/slirp/if.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#include -#include "qemu/timer.h" - -static void -ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead) -{ - ifm->ifs_next = ifmhead->ifs_next; - ifmhead->ifs_next = ifm; - ifm->ifs_prev = ifmhead; - ifm->ifs_next->ifs_prev = ifm; -} - -static void -ifs_remque(struct mbuf *ifm) -{ - ifm->ifs_prev->ifs_next = ifm->ifs_next; - ifm->ifs_next->ifs_prev = ifm->ifs_prev; -} - -void -if_init(Slirp *slirp) -{ - slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq; - slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq; - slirp->next_m = &slirp->if_batchq; -} - -/* - * 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 mbufs, 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(struct socket *so, struct mbuf *ifm) -{ - Slirp *slirp = ifm->slirp; - struct mbuf *ifq; - int on_fastq = 1; - - DEBUG_CALL("if_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("ifm = %lx", (long)ifm); - - /* - * First remove the mbuf 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 = slirp->if_batchq.ifq_prev; ifq && ifq != &slirp->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 = slirp->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 = slirp->if_batchq.ifq_prev; - /* Set next_m if the queue was empty so far */ - if (slirp->next_m == &slirp->if_batchq) { - slirp->next_m = ifm; - } - } - - /* Create a new doubly linked list for this session */ - ifm->ifq_so = so; - ifs_init(ifm); - insque(ifm, ifq); - -diddit: - 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, &slirp->if_batchq); - } - } - -#ifndef FULL_BOLT - /* - * This prevents us from malloc()ing too many mbufs - */ - if_start(ifm->slirp); -#endif -} - -/* - * Send a packet - * We choose a packet based on its 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(Slirp *slirp) -{ - uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - bool from_batchq, next_from_batchq; - struct mbuf *ifm, *ifm_next, *ifqt; - - DEBUG_CALL("if_start"); - - if (slirp->if_start_busy) { - return; - } - slirp->if_start_busy = true; - - if (slirp->if_fastq.ifq_next != &slirp->if_fastq) { - ifm_next = slirp->if_fastq.ifq_next; - next_from_batchq = false; - } else if (slirp->next_m != &slirp->if_batchq) { - /* Nothing on fastq, pick up from batchq via next_m */ - ifm_next = slirp->next_m; - next_from_batchq = true; - } else { - ifm_next = NULL; - } - - while (ifm_next) { - ifm = ifm_next; - from_batchq = next_from_batchq; - - ifm_next = ifm->ifq_next; - if (ifm_next == &slirp->if_fastq) { - /* No more packets in fastq, switch to batchq */ - ifm_next = slirp->next_m; - next_from_batchq = true; - } - if (ifm_next == &slirp->if_batchq) { - /* end of batchq */ - ifm_next = NULL; - } - - /* Try to send packet unless it already expired */ - if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) { - /* Packet is delayed due to pending ARP resolution */ - continue; - } - - if (ifm == slirp->next_m) { - /* Set which packet to send on next iteration */ - slirp->next_m = ifm->ifq_next; - } - - /* Remove it from the queue */ - ifqt = ifm->ifq_prev; - remque(ifm); - - /* If there are more packets for this session, re-queue them */ - if (ifm->ifs_next != ifm) { - struct mbuf *next = ifm->ifs_next; - - insque(next, ifqt); - ifs_remque(ifm); - - if (!from_batchq) { - /* Next packet in fastq is from the same session */ - ifm_next = next; - next_from_batchq = false; - } else if (slirp->next_m == &slirp->if_batchq) { - /* Set next_m and ifm_next if the session packet is now the - * only one on batchq */ - slirp->next_m = ifm_next = next; - } - } - - /* Update so_queued */ - if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) { - /* If there's no more queued, reset nqueued */ - ifm->ifq_so->so_nqueued = 0; - } - - m_free(ifm); - } - - slirp->if_start_busy = false; -} diff --git a/slirp/if.h b/slirp/if.h deleted file mode 100644 index d46cd23a0..000000000 --- a/slirp/if.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 */ - -#define IF_MTU 1500 -#define IF_MRU 1500 -#define IF_COMP IF_AUTOCOMP /* Flags for compression */ - -/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */ -#define IF_MAXLINKHDR (2 + 14 + 40) - -#endif diff --git a/slirp/ip.h b/slirp/ip.h deleted file mode 100644 index 0edcd361b..000000000 --- a/slirp/ip.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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 HOST_WORDS_BIGENDIAN -# undef NTOHL -# undef NTOHS -# undef HTONL -# undef HTONS -# define NTOHL(d) -# define NTOHS(d) -# define HTONL(d) -# define HTONS(d) -#else -# ifndef NTOHL -# define NTOHL(d) ((d) = ntohl((d))) -# endif -# ifndef NTOHS -# define NTOHS(d) ((d) = ntohs((uint16_t)(d))) -# endif -# ifndef HTONL -# define HTONL(d) ((d) = htonl((d))) -# endif -# ifndef HTONS -# define HTONS(d) ((d) = htons((uint16_t)(d))) -# endif -#endif - -typedef uint32_t n_long; /* long as received from the net */ - -#ifdef _MSC_VER -# define PACKED_BEGIN __pragma( pack(push, 1) ) -# define PACKED_END __pragma( pack(pop) ) -#else -# define PACKED_BEGIN -#if defined(_WIN32) -# define PACKED_END __attribute__((gcc_struct, packed)) -#else -# define PACKED_END __attribute__((packed)) -#endif -#endif - -/* - * Definitions for internet protocol version 4. - * Per RFC 791, September 1981. - */ -#define IPVERSION 4 - -/* - * Structure of an internet header, naked of options. - */ -PACKED_BEGIN -struct ip { -#ifdef HOST_WORDS_BIGENDIAN - uint8_t ip_v:4, /* version */ - ip_hl:4; /* header length */ -#else - uint8_t ip_hl:4, /* header length */ - ip_v:4; /* version */ -#endif - uint8_t ip_tos; /* type of service */ - uint16_t ip_len; /* total length */ - uint16_t ip_id; /* identification */ - uint16_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 */ - uint8_t ip_ttl; /* time to live */ - uint8_t ip_p; /* protocol */ - uint16_t ip_sum; /* checksum */ - struct in_addr ip_src,ip_dst; /* source and dest address */ -} PACKED_END; - -#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. - */ -PACKED_BEGIN -struct ip_timestamp { - uint8_t ipt_code; /* IPOPT_TS */ - uint8_t ipt_len; /* size of structure (variable) */ - uint8_t ipt_ptr; /* index of current entry */ -#ifdef HOST_WORDS_BIGENDIAN - uint8_t ipt_oflw:4, /* overflow counter */ - ipt_flg:4; /* flags, see below */ -#else - uint8_t 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_END; - -/* 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 */ - -PACKED_BEGIN -#if SIZEOF_CHAR_P == 4 -struct mbuf_ptr { - struct mbuf *mptr; - uint32_t dummy; -} PACKED_END; -#else -struct mbuf_ptr { - struct mbuf *mptr; -} PACKED_END; -#endif - -struct qlink { - void *next, *prev; -}; - -/* - * Overlay for ip header used by other protocols (tcp, udp). - */ -PACKED_BEGIN -struct ipovly { - struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ - uint8_t ih_x1; /* (unused) */ - uint8_t ih_pr; /* protocol */ - uint16_t ih_len; /* protocol length */ - struct in_addr ih_src; /* source internet address */ - struct in_addr ih_dst; /* destination internet address */ -} PACKED_END; - -/* - * 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 - */ -PACKED_BEGIN -struct ipq { - struct qlink frag_link; /* to ip headers of fragments */ - struct qlink ip_link; /* to other reass headers */ - uint8_t ipq_ttl; /* time for reass q to live */ - uint8_t ipq_p; /* protocol of this fragment */ - uint16_t ipq_id; /* sequence id for reassembly */ - struct in_addr ipq_src,ipq_dst; -} PACKED_END; - -/* - * Ip header, when holding a fragment. - * - * Note: ipf_link must be at same offset as frag_link above - */ -PACKED_BEGIN -struct ipasfrag { - struct qlink ipf_link; - struct ip ipf_ip; -} PACKED_END; - -#define ipf_off ipf_ip.ip_off -#define ipf_tos ipf_ip.ip_tos -#define ipf_len ipf_ip.ip_len -#define ipf_next ipf_link.next -#define ipf_prev ipf_link.prev - -/* - * 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 - -PACKED_BEGIN -struct ipoption { - struct in_addr ipopt_dst; /* first-hop dst if source routed */ - int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */ -} PACKED_END; - -#endif diff --git a/slirp/ip_icmp.c b/slirp/ip_icmp.c deleted file mode 100644 index be350e24b..000000000 --- a/slirp/ip_icmp.c +++ /dev/null @@ -1,450 +0,0 @@ -/* - * 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" - -/* The message sent when emulating PING */ -/* Be nice and tell them it's just a pseudo-ping packet */ -static const char icmp_ping_msg[] = "This is a pseudo-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 const 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 -}; - -void icmp_init(Slirp *slirp) -{ - slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; - slirp->icmp_last_so = &slirp->icmp; -} - -void icmp_cleanup(Slirp *slirp) -{ - while (slirp->icmp.so_next != &slirp->icmp) { - icmp_detach(slirp->icmp.so_next); - } -} - -static int icmp_send(struct socket *so, struct mbuf *m, int hlen) -{ - struct ip *ip = mtod(m, struct ip *); - struct sockaddr_in addr; - - so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); - if (so->s == -1) { - return -1; - } - - so->so_m = m; - so->so_faddr = ip->ip_dst; - so->so_laddr = ip->ip_src; - so->so_iptos = ip->ip_tos; - so->so_type = IPPROTO_ICMP; - so->so_state = SS_ISFCONNECTED; - so->so_expire = curtime + SO_EXPIRE; - - addr.sin_family = AF_INET; - addr.sin_addr = so->so_faddr; - - insque(so, &so->slirp->icmp); - - if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, - (struct sockaddr *)&addr, sizeof(addr)) == -1) { - DEBUG_MISC("icmp_input icmp sendto tx errno = %d-%s\n", - errno, strerror(errno)); - icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); - icmp_detach(so); - } - - return 0; -} - -void icmp_detach(struct socket *so) -{ - closesocket(so->s); - sofree(so); -} - -/* - * Process a received ICMP message. - */ -void -icmp_input(struct mbuf *m, int hlen) -{ - register struct icmp *icp; - register struct ip *ip=mtod(m, struct ip *); - int icmplen=ip->ip_len; - Slirp *slirp = m->slirp; - - DEBUG_CALL("icmp_input"); - DEBUG_ARG("m = %lx", (long )m); - DEBUG_ARG("m_len = %d", m->m_len); - - /* - * Locate icmp structure in mbuf, and check - * that its not corrupted and of at least minimum length. - */ - if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ - freeit: - m_free(m); - goto end_error; - } - - m->m_len -= hlen; - m->m_data += hlen; - icp = mtod(m, struct icmp *); - if (cksum(m, icmplen)) { - goto freeit; - } - m->m_len += hlen; - m->m_data -= hlen; - - DEBUG_ARG("icmp_type = %d", icp->icmp_type); - switch (icp->icmp_type) { - case ICMP_ECHO: - ip->ip_len += hlen; /* since ip_input subtracts this */ - if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { - icmp_reflect(m); - } else if (slirp->restricted) { - goto freeit; - } else { - struct socket *so; - struct sockaddr_in addr; - if ((so = socreate(slirp)) == NULL) goto freeit; - if (icmp_send(so, m, hlen) == 0) { - return; - } - if(udp_attach(so) == -1) { - DEBUG_MISC("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 & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } 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("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: - m_free(m); - break; - - default: - m_free(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. - * mbuf *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(struct mbuf *msrc, u_char type, u_char code, int minsize, - const char *message) -{ - unsigned hlen, shlen, s_ip_len; - register struct ip *ip; - register struct icmp *icp; - register struct mbuf *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 *); -#ifdef DEBUG - { char bufa[20], bufb[20]; - strcpy(bufa, inet_ntoa(ip->ip_src)); - strcpy(bufb, inet_ntoa(ip->ip_dst)); - DEBUG_MISC(" %.16s to %.16s\n", bufa, bufb); - } -#endif - if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ - - /* Do not reply to source-only IPs */ - if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) { - goto end_error; - } - - 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 */ - m = m_get(msrc->slirp); - if (!m) { - goto end_error; - } - - { 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); - -#ifdef 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 = 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 addresses */ - ip->ip_src = m->slirp->vhost_addr; - - (void ) ip_output((struct socket *)NULL, m); - -end_error: - return; -} -#undef ICMP_MAXDATALEN - -/* - * Reflect the ip packet back to the source - */ -void -icmp_reflect(struct mbuf *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_type = ICMP_ECHOREPLY; - 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 - * mbuf's data back, and adjust the IP length. - */ - memmove((caddr_t)(ip + 1), (caddr_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 socket *)NULL, m); -} - -void icmp_receive(struct socket *so) -{ - struct mbuf *m = so->so_m; - struct ip *ip = mtod(m, struct ip *); - int hlen = ip->ip_hl << 2; - u_char error_code; - struct icmp *icp; - int id, len; - - m->m_data += hlen; - m->m_len -= hlen; - icp = mtod(m, struct icmp *); - - id = icp->icmp_id; - len = qemu_recv(so->s, icp, m->m_len, 0); - icp->icmp_id = id; - - m->m_data -= hlen; - m->m_len += hlen; - - if (len == -1 || len == 0) { - if (errno == ENETUNREACH) { - error_code = ICMP_UNREACH_NET; - } else { - error_code = ICMP_UNREACH_HOST; - } - DEBUG_MISC(" udp icmp rx errno = %d-%s\n", errno, - strerror(errno)); - icmp_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); - } else { - icmp_reflect(so->so_m); - so->so_m = NULL; /* Don't m_free() it again! */ - } - icmp_detach(so); -} diff --git a/slirp/ip_icmp.h b/slirp/ip_icmp.h deleted file mode 100644 index 36fefa4b0..000000000 --- a/slirp/ip_icmp.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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 uint32_t n_time; - -/* - * Structure of an icmp header. - */ -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 -}; - -/* - * Lower bounds on packet lengths for various types. - * For the error advice packets must first ensure that the - * packet is large enough 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_init(Slirp *slirp); -void icmp_cleanup(Slirp *slirp); -void icmp_input(struct mbuf *, int); -void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize, - const char *message); -void icmp_reflect(struct mbuf *); -void icmp_receive(struct socket *so); -void icmp_detach(struct socket *so); - -#endif diff --git a/slirp/ip_input.c b/slirp/ip_input.c deleted file mode 100644 index bfce10dce..000000000 --- a/slirp/ip_input.c +++ /dev/null @@ -1,668 +0,0 @@ -/* - * 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 -#include -#include "ip_icmp.h" - -static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp); -static void ip_freef(Slirp *slirp, struct ipq *fp); -static void ip_enq(register struct ipasfrag *p, - register struct ipasfrag *prev); -static void ip_deq(register struct ipasfrag *p); - -/* - * IP initialization: fill in IP protocol switch table. - * All protocols not implemented in kernel go to raw IP protocol handler. - */ -void -ip_init(Slirp *slirp) -{ - slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; - udp_init(slirp); - tcp_init(slirp); - icmp_init(slirp); -} - -void ip_cleanup(Slirp *slirp) -{ - udp_cleanup(slirp); - tcp_cleanup(slirp); - icmp_cleanup(slirp); -} - -/* - * Ip input routine. Checksum and byte swap header. If fragmented - * try to reassemble. Process options. Pass to next level. - */ -void -ip_input(struct mbuf *m) -{ - Slirp *slirp = m->slirp; - register struct ip *ip; - int hlen; - - DEBUG_CALL("ip_input"); - DEBUG_ARG("m = %lx", (long)m); - DEBUG_ARG("m_len = %d", m->m_len); - - if (m->m_len < sizeof (struct ip)) { - return; - } - - ip = mtod(m, struct ip *); - - if (ip->ip_v != IPVERSION) { - goto bad; - } - - hlen = ip->ip_hl << 2; - if (hlenm->m_len) {/* min header length */ - goto bad; /* or packet too short */ - } - - /* keep ip header intact for ICMP reply - * ip->ip_sum = cksum(m, hlen); - * if (ip->ip_sum) { - */ - if(cksum(m,hlen)) { - goto bad; - } - - /* - * Convert fields to host representation. - */ - NTOHS(ip->ip_len); - if (ip->ip_len < hlen) { - 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 mbufs if longer than we expect. - * Drop packet if shorter than we expect. - */ - if (m->m_len < ip->ip_len) { - goto bad; - } - - /* Should drop packet if mbuf 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) { - icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); - goto bad; - } - - /* - * 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; - struct qlink *l; - /* - * Look for queue of fragments - * of this datagram. - */ - for (l = (struct qlink *)slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link; - l = (struct qlink *)l->next) { - fp = container_of(l, struct ipq, ip_link); - 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 = NULL; - 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) - ip->ip_tos |= 1; - else - ip->ip_tos &= ~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 (ip->ip_tos & 1 || ip->ip_off) { - ip = ip_reass(slirp, ip, fp); - if (ip == NULL) - return; - m = dtom(slirp, ip); - } else - if (fp) - ip_freef(slirp, fp); - - } else - ip->ip_len -= hlen; - - /* - * Switch out to protocol's input routine. - */ - switch (ip->ip_p) { - case IPPROTO_TCP: - tcp_input(m, hlen, (struct socket *)NULL); - break; - case IPPROTO_UDP: - udp_input(m, hlen); - break; - case IPPROTO_ICMP: - icmp_input(m, hlen); - break; - default: - m_free(m); - } - return; -bad: - m_free(m); -} - -#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink))) -#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink))) -/* - * 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. - */ -static struct ip * -ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) -{ - register struct mbuf *m = dtom(slirp, 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 mbufs - * 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 == NULL) { - struct mbuf *t = m_get(slirp); - - if (t == NULL) { - goto dropfrag; - } - fp = mtod(t, struct ipq *); - insque(&fp->ip_link, &slirp->ipq.ip_link); - fp->ipq_ttl = IPFRAGTTL; - fp->ipq_p = ip->ip_p; - fp->ipq_id = ip->ip_id; - fp->frag_link.next = fp->frag_link.prev = &fp->frag_link; - fp->ipq_src = ip->ip_src; - fp->ipq_dst = ip->ip_dst; - q = (struct ipasfrag *)fp; - goto insert; - } - - /* - * Find a segment which begins after this one does. - */ - for (q = (struct ipasfrag *)fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; - q = (struct ipasfrag *)q->ipf_next) - if (q->ipf_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 != &fp->frag_link) { - struct ipasfrag *pq = (struct ipasfrag *)q->ipf_prev; - i = pq->ipf_off + pq->ipf_len - ip->ip_off; - if (i > 0) { - if (i >= ip->ip_len) - goto dropfrag; - m_adj(dtom(slirp, 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->frag_link && - ip->ip_off + ip->ip_len > q->ipf_off) { - i = (ip->ip_off + ip->ip_len) - q->ipf_off; - if (i < q->ipf_len) { - q->ipf_len -= i; - q->ipf_off += i; - m_adj(dtom(slirp, q), i); - break; - } - q = (struct ipasfrag *)q->ipf_next; - m_free(dtom(slirp, q->ipf_prev)); - ip_deq((struct ipasfrag*)q->ipf_prev); - } - -insert: - /* - * Stick new segment in its place; - * check for complete reassembly. - */ - ip_enq(iptofrag(ip), (struct ipasfrag*)q->ipf_prev); - next = 0; - for (q = (struct ipasfrag*)fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; - q = (struct ipasfrag*)q->ipf_next) { - if (q->ipf_off != next) - return NULL; - next += q->ipf_len; - } - if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1) - return NULL; - - /* - * Reassembly is complete; concatenate fragments. - */ - q = (struct ipasfrag*)fp->frag_link.next; - m = dtom(slirp, q); - - q = (struct ipasfrag *) q->ipf_next; - while (q != (struct ipasfrag*)&fp->frag_link) { - struct mbuf *t = dtom(slirp, 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. - */ - q = (struct ipasfrag*)fp->frag_link.next; - - /* - * If the fragments concatenated to an mbuf 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 mbuf), so we must point ip - * into the new buffer. - */ - if (m->m_flags & M_EXT) { - int delta = (char *)q - m->m_dat; - q = (struct ipasfrag *)(m->m_ext + delta); - } - - ip = fragtoip(q); - ip->ip_len = next; - ip->ip_tos &= ~1; - ip->ip_src = fp->ipq_src; - ip->ip_dst = fp->ipq_dst; - remque(&fp->ip_link); - (void) m_free(dtom(slirp, fp)); - m->m_len += (ip->ip_hl << 2); - m->m_data -= (ip->ip_hl << 2); - - return ip; - -dropfrag: - m_free(m); - return NULL; -} - -/* - * Free a fragment reassembly header and all - * associated datagrams. - */ -static void -ip_freef(Slirp *slirp, struct ipq *fp) -{ - register struct ipasfrag *q, *p; - - for (q = (struct ipasfrag*)fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) { - p = (struct ipasfrag*)q->ipf_next; - ip_deq(q); - m_free(dtom(slirp, q)); - } - remque(&fp->ip_link); - (void) m_free(dtom(slirp, fp)); -} - -/* - * Put an ip fragment on a reassembly chain. - * Like insque, but pointers in middle of structure. - */ -static void -ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev) -{ - DEBUG_CALL("ip_enq"); - DEBUG_ARG("prev = %lx", (long)prev); - p->ipf_prev = prev; - p->ipf_next = prev->ipf_next; - ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p; - prev->ipf_next = p; -} - -/* - * To ip_enq as remque is to insque. - */ -static void -ip_deq(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(Slirp *slirp) -{ - struct qlink *l; - - DEBUG_CALL("ip_slowtimo"); - - l = (struct qlink*)slirp->ipq.ip_link.next; - - if (l == NULL) - return; - - while (l != &slirp->ipq.ip_link) { - struct ipq *fp = container_of(l, struct ipq, ip_link); - l = (struct qlink*)l->next; - if (--fp->ipq_ttl == 0) { - ip_freef(slirp, fp); - } - } -} - -/* - * 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 mbuf *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, forward = 0; - struct in_addr *sin, dst; -typedef uint32_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((caddr_t)(cp + off), (caddr_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((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_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((caddr_t)(&ip->ip_dst), (caddr_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((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_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((caddr_t)&IA_SIN(ia)->sin_addr, - (caddr_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((caddr_t)sin, (caddr_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((caddr_t)&ntime, (caddr_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: - icmp_error(m, type, code, 0, 0); - - 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(register struct mbuf *m, struct mbuf *mopt) -{ - register int i; - struct ip *ip = mtod(m, struct ip *); - register caddr_t opts; - int olen; - - olen = (ip->ip_hl<<2) - sizeof (struct ip); - opts = (caddr_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/slirp/libslirp.h b/slirp/libslirp.h deleted file mode 100644 index 9f633eae4..000000000 --- a/slirp/libslirp.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef _LIBSLIRP_H -#define _LIBSLIRP_H - -#include "qemu-common.h" - -struct Slirp; -typedef struct Slirp Slirp; - -int get_dns_addr(struct in_addr *pdns_addr); - -Slirp *slirp_init(int restricted, struct in_addr vnetwork, - struct in_addr vnetmask, struct in_addr vhost, - const char *vhostname, const char *tftp_path, - const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, const char **vdnssearch, - void *opaque); -void slirp_cleanup(Slirp *slirp); - -void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); - -void slirp_pollfds_poll(GArray *pollfds, int select_error); - -void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); - -/* you must provide the following functions: */ -void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len); - -int slirp_add_hostfwd(Slirp *slirp, int is_udp, - struct in_addr host_addr, int host_port, - struct in_addr guest_addr, int guest_port); -int slirp_remove_hostfwd(Slirp *slirp, int is_udp, - struct in_addr host_addr, int host_port); -int slirp_add_exec(Slirp *slirp, int do_pty, const void *args, - struct in_addr *guest_addr, int guest_port); - -void slirp_connection_info(Slirp *slirp, Monitor *mon); - -void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port, const uint8_t *buf, int size); -size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port); - -#endif diff --git a/slirp/main.h b/slirp/main.h deleted file mode 100644 index 209e2456c..000000000 --- a/slirp/main.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ -#ifndef SLIRP_MAIN_H -#define SLIRP_MAIN_H 1 - -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#define TOWRITEMAX 512 - -extern int slirp_socket; -extern int slirp_socket_unit; -extern int slirp_socket_port; -extern uint32_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 struct in_addr loopback_addr; -extern unsigned long loopback_mask; -extern char *username; -extern char *socket_path; -extern int towrite_max; -extern int ppp_exit; -extern int tcp_keepintvl; - -#define PROTO_SLIP 0x1 -#ifdef USE_PPP -#define PROTO_PPP 0x2 -#endif - -int if_encap(Slirp *slirp, struct mbuf *ifm); -ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags); - -#endif diff --git a/slirp/mbuf.c b/slirp/mbuf.c deleted file mode 100644 index b471067da..000000000 --- a/slirp/mbuf.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 - -#define MBUF_THRESH 30 - -/* - * Find a nice value for msize - * XXX if_maxlinkhdr already in mtu - */ -#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6) - -void -m_init(Slirp *slirp) -{ - slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist; - slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist; -} - -void m_cleanup(Slirp *slirp) -{ - struct mbuf *m, *next; - - m = slirp->m_usedlist.m_next; - while (m != &slirp->m_usedlist) { - next = m->m_next; - if (m->m_flags & M_EXT) { - free(m->m_ext); - } - free(m); - m = next; - } - m = slirp->m_freelist.m_next; - while (m != &slirp->m_freelist) { - next = m->m_next; - free(m); - m = next; - } -} - -/* - * 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 mbuf * -m_get(Slirp *slirp) -{ - register struct mbuf *m; - int flags = 0; - - DEBUG_CALL("m_get"); - - if (slirp->m_freelist.m_next == &slirp->m_freelist) { - m = (struct mbuf *)malloc(SLIRP_MSIZE); - if (m == NULL) goto end_error; - slirp->mbuf_alloced++; - if (slirp->mbuf_alloced > MBUF_THRESH) - flags = M_DOFREE; - m->slirp = slirp; - } else { - m = slirp->m_freelist.m_next; - remque(m); - } - - /* Insert it in the used list */ - insque(m,&slirp->m_usedlist); - m->m_flags = (flags | M_USEDLIST); - - /* Initialise it */ - m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat); - m->m_data = m->m_dat; - m->m_len = 0; - m->m_nextpkt = NULL; - m->m_prevpkt = NULL; - m->arp_requested = false; - m->expiration_date = (uint64_t)-1; -end_error: - DEBUG_ARG("m = %lx", (long )m); - return m; -} - -void -m_free(struct mbuf *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) { - m->slirp->mbuf_alloced--; - free(m); - } else if ((m->m_flags & M_FREELIST) == 0) { - insque(m,&m->slirp->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(struct mbuf *m, struct mbuf *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(struct mbuf *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); - m->m_data = m->m_ext + datasize; - } else { - char *dat; - datasize = m->m_data - m->m_dat; - dat = (char *)malloc(size); - 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(struct mbuf *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(struct mbuf *n, struct mbuf *m, int off, int 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 mbuf * -dtom(Slirp *slirp, void *dat) -{ - struct mbuf *m; - - DEBUG_CALL("dtom"); - DEBUG_ARG("dat = %lx", (long )dat); - - /* bug corrected for M_EXT buffers */ - for (m = slirp->m_usedlist.m_next; m != &slirp->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("dtom failed"); - - return (struct mbuf *)0; -} diff --git a/slirp/mbuf.h b/slirp/mbuf.h deleted file mode 100644 index 6419f648f..000000000 --- a/slirp/mbuf.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 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 - */ -#define mtod(m,t) ((t)(m)->m_data) - -/* 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. - */ - -/* - * 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 mbuf { - /* XXX should union some of these! */ - /* header at beginning of each mbuf: */ - struct mbuf *m_next; /* Linked list of mbufs */ - struct mbuf *m_prev; - struct mbuf *m_nextpkt; /* Next packet in queue/record */ - struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ - int m_flags; /* Misc flags */ - - int m_size; /* Size of data */ - struct socket *m_so; - - caddr_t m_data; /* Location of data */ - int m_len; /* Amount of data in this mbuf */ - - Slirp *slirp; - bool arp_requested; - uint64_t expiration_date; - /* start of dynamic buffer area, must be last element */ - union { - char m_dat[1]; /* ANSI don't like 0 sized arrays */ - char *m_ext; - }; -}; - -#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 */ - -void m_init(Slirp *); -void m_cleanup(Slirp *slirp); -struct mbuf * m_get(Slirp *); -void m_free(struct mbuf *); -void m_cat(register struct mbuf *, register struct mbuf *); -void m_inc(struct mbuf *, int); -void m_adj(struct mbuf *, int); -int m_copy(struct mbuf *, struct mbuf *, int, int); -struct mbuf * dtom(Slirp *, void *); - -static inline void ifs_init(struct mbuf *ifm) -{ - ifm->ifs_next = ifm->ifs_prev = ifm; -} - -#endif diff --git a/slirp/misc.c b/slirp/misc.c deleted file mode 100644 index 088918f6f..000000000 --- a/slirp/misc.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#include -#include - -#include "monitor/monitor.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -int slirp_debug = -#ifdef DEBUG - DBG_CALL|DBG_MISC|DBG_ERROR; -#else - 0; -#endif - -struct quehead { - struct quehead *qh_link; - struct quehead *qh_rlink; -}; - -void -insque(void *a, void *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(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; -} - -int add_exec(struct ex_list **ex_ptr, int do_pty, const char *exec, - struct in_addr 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.s_addr == tmp_ptr->ex_addr.s_addr) - return -1; - } - - tmp_ptr = *ex_ptr; - *ex_ptr = (struct ex_list *)g_new(struct ex_list, 1); - (*ex_ptr)->ex_fport = port; - (*ex_ptr)->ex_addr = addr; - (*ex_ptr)->ex_pty = do_pty; - (*ex_ptr)->ex_exec = (do_pty == 3) ? exec : g_strdup(exec); - (*ex_ptr)->ex_next = tmp_ptr; - return 0; -} - -#ifndef HAVE_STRERROR - -/* - * For systems with no strerror - */ - -extern int sys_nerr; -extern char *sys_errlist[]; - -char * -strerror(error) - int error; -{ - if (error < sys_nerr) - return sys_errlist[error]; - else - return "Unknown error."; -} - -#endif - - -#ifdef _WIN32 - -int -fork_exec(struct socket *so, const char *ex, int do_pty) -{ - /* not implemented */ - return 0; -} - -#else - -/* - * 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(struct socket *so, const char *ex, int do_pty) -{ -#if defined(NEED_FORK_EXEC) - int s; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int opt; - const char *argv[256]; - /* don't want to clobber the original */ - char *bptr; - const char *curarg; - int c, i, ret; - pid_t pid; - - 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) { - return 0; - } else { - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr.s_addr = INADDR_ANY; - - if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 || - bind(s, (struct sockaddr *)&addr, addrlen) < 0 || - listen(s, 1) < 0) { - error_report("Error: inet socket: %s", strerror(errno)); - closesocket(s); - - return 0; - } - } - - pid = fork(); - switch(pid) { - case -1: - error_report("Error: fork failed: %s", strerror(errno)); - close(s); - return 0; - - case 0: - setsid(); - - /* Set the DISPLAY */ - getsockname(s, (struct sockaddr *)&addr, &addrlen); - close(s); - /* - * Connect to the socket - * XXX If any of these fail, we're in trouble! - */ - s = qemu_socket(AF_INET, SOCK_STREAM, 0); - addr.sin_addr = loopback_addr; - do { - ret = connect(s, (struct sockaddr *)&addr, addrlen); - } while (ret < 0 && errno == EINTR); - - dup2(s, 0); - dup2(s, 1); - dup2(s, 2); - for (s = getdtablesize() - 1; s >= 3; s--) - close(s); - - i = 0; - bptr = g_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++] = g_strdup(curarg); - } while (c); - - argv[i] = NULL; - execvp(argv[0], (char * const *)argv); - - /* Ooops, failed, let's tell the user why */ - fprintf(stderr, "Error: execvp of %s failed: %s\n", - argv[0], strerror(errno)); - close(0); close(1); close(2); /* XXX */ - exit(1); - - default: - qemu_add_child_watch(pid); - /* - * 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); - socket_set_fast_reuse(so->s); - opt = 1; - qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); - qemu_set_nonblock(so->s); - - /* Append the telnet options now */ - if (so->so_m != NULL && do_pty == 1) { - sbappend(so, so->so_m); - so->so_m = NULL; - } - - return 1; - } -#else - return 0; -#endif -} -#endif - -void slirp_connection_info(Slirp *slirp, Monitor *mon) -{ -#if (TCPS_CLOSED != 0) || (TCPS_TIME_WAIT != 10) -#error unexpected TCPS symbol values -#endif - const char * const tcpstates[] = { - /* [TCPS_CLOSED] = */ "CLOSED", - /* [TCPS_LISTEN] = */ "LISTEN", - /* [TCPS_SYN_SENT] = */ "SYN_SENT", - /* [TCPS_SYN_RECEIVED] = */ "SYN_RCVD", - /* [TCPS_ESTABLISHED] = */ "ESTABLISHED", - /* [TCPS_CLOSE_WAIT] = */ "CLOSE_WAIT", - /* [TCPS_FIN_WAIT_1] = */ "FIN_WAIT_1", - /* [TCPS_CLOSING] = */ "CLOSING", - /* [TCPS_LAST_ACK] = */ "LAST_ACK", - /* [TCPS_FIN_WAIT_2] = */ "FIN_WAIT_2", - /* [TCPS_TIME_WAIT] = */ "TIME_WAIT", - }; - struct in_addr dst_addr; - struct sockaddr_in src; - socklen_t src_len; - uint16_t dst_port; - struct socket *so; - const char *state; - char buf[20]; - - monitor_printf(mon, " Protocol[State] FD Source Address Port " - "Dest. Address Port RecvQ SendQ\n"); - - for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { - if (so->so_state & SS_HOSTFWD) { - state = "HOST_FORWARD"; - } else if (so->so_tcpcb) { - state = tcpstates[so->so_tcpcb->t_state]; - } else { - state = "NONE"; - } - if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { - src_len = sizeof(src); - getsockname(so->s, (struct sockaddr *)&src, &src_len); - dst_addr = so->so_laddr; - dst_port = so->so_lport; - } else { - src.sin_addr = so->so_laddr; - src.sin_port = so->so_lport; - dst_addr = so->so_faddr; - dst_port = so->so_fport; - } - snprintf(buf, sizeof(buf), " TCP[%s]", state); - monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", - ntohs(src.sin_port)); - monitor_printf(mon, "%15s %5d %5d %5d\n", - inet_ntoa(dst_addr), ntohs(dst_port), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } - - for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { - if (so->so_state & SS_HOSTFWD) { - snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]"); - src_len = sizeof(src); - (void)getsockname(so->s, (struct sockaddr *)&src, &src_len); - dst_addr = so->so_laddr; - dst_port = so->so_lport; - } else { - snprintf(buf, sizeof(buf), " UDP[%d sec]", - (so->so_expire - curtime) / 1000); - src.sin_addr = so->so_laddr; - src.sin_port = so->so_lport; - dst_addr = so->so_faddr; - dst_port = so->so_fport; - } - monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", - ntohs(src.sin_port)); - monitor_printf(mon, "%15s %5d %5d %5d\n", - inet_ntoa(dst_addr), ntohs(dst_port), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } - - for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { - snprintf(buf, sizeof(buf), " ICMP[%d sec]", - (so->so_expire - curtime) / 1000); - src.sin_addr = so->so_laddr; - dst_addr = so->so_faddr; - monitor_printf(mon, "%-19s %3d %15s - ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); - monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } -} diff --git a/slirp/misc.h b/slirp/misc.h deleted file mode 100644 index 33e9369c8..000000000 --- a/slirp/misc.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#ifndef _MISC_H_ -#define _MISC_H_ - -#include "socket.h" - -struct ex_list { - int ex_pty; /* Do we want a pty? */ - struct in_addr ex_addr; /* Server address */ - int ex_fport; /* Port to telnet to */ - const char *ex_exec; /* Command line of what to exec */ - struct ex_list *ex_next; -}; - -#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 */ - -struct tos_t { - uint16_t lport; - uint16_t fport; - uint8_t tos; - uint8_t emu; -}; - -struct emu_t { - uint16_t lport; - uint16_t fport; - uint8_t tos; - uint8_t emu; - struct emu_t *next; -}; - -void slirp_insque(void *, void *); -void slirp_remque(void *); -int add_exec(struct ex_list **, int, const char *, struct in_addr, int); -int fork_exec(struct socket *so, const char *ex, int do_pty); - -#endif diff --git a/slirp/sbuf.c b/slirp/sbuf.c deleted file mode 100644 index d0888d5c3..000000000 --- a/slirp/sbuf.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#include -#include - -static void sbappendsb(struct sbuf *sb, struct mbuf *m); - -void -sbfree(struct sbuf *sb) -{ - free(sb->sb_data); -} - -void -sbdrop(struct sbuf *sb, int num) -{ - u_int limit = sb->sb_datalen / 2; - - /* - * We can only drop how much we have - * This should never succeed - */ - if((u_int)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; - - if (sb->sb_cc < limit && sb->sb_cc + num >= limit) { - qemu_notify_event(); - } -} - -void -sbreserve(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(struct socket *so, struct mbuf *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); - (void)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 = slirp_send(so, 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 mbuf */ - m_free(m); -} - -/* - * Copy the data from m into sb - * The caller is responsible to make sure there's enough room - */ -static void -sbappendsb(struct sbuf *sb, struct mbuf *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(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 ((u_int)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/slirp/sbuf.h b/slirp/sbuf.h deleted file mode 100644 index 26e901cb8..000000000 --- a/slirp/sbuf.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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(struct sbuf *); -void sbdrop(struct sbuf *, int); -void sbreserve(struct sbuf *, int); -void sbappend(struct socket *, struct mbuf *); -void sbcopy(struct sbuf *, int, int, char *); - -#endif diff --git a/slirp/slirp.c b/slirp/slirp.c deleted file mode 100644 index 1fa8654e7..000000000 --- a/slirp/slirp.c +++ /dev/null @@ -1,1202 +0,0 @@ -/* - * libslirp glue - * - * Copyright (c) 2004-2008 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 "qemu-common.h" -#include "qemu/timer.h" -#include "sysemu/char.h" -#include "slirp.h" -#include "hw/hw.h" - -/* host loopback address */ -struct in_addr loopback_addr; -/* host loopback network mask */ -unsigned long loopback_mask; - -/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ -static const uint8_t special_ethaddr[ETH_ALEN] = { - 0x52, 0x55, 0x00, 0x00, 0x00, 0x00 -}; - -u_int curtime; - -static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances = - QTAILQ_HEAD_INITIALIZER(slirp_instances); - -static struct in_addr dns_addr; -static u_int dns_addr_time; - -#define TIMEOUT_FAST 2 /* milliseconds */ -#define TIMEOUT_SLOW 499 /* milliseconds */ -/* for the aging of certain requests like DNS */ -#define TIMEOUT_DEFAULT 1000 /* milliseconds */ - -#ifdef _WIN32 - -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; - - if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { - *pdns_addr = dns_addr; - return 0; - } - - 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 = (FIXED_INFO *)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; - dns_addr = tmp_addr; - dns_addr_time = curtime; - if (FixedInfo) { - GlobalFree(FixedInfo); - FixedInfo = NULL; - } - return 0; -} - -static void winsock_cleanup(void) -{ - WSACleanup(); -} - -#else - -static struct stat dns_addr_stat; - -int get_dns_addr(struct in_addr *pdns_addr) -{ - char buff[512]; - char buff2[257]; - FILE *f; - int found = 0; - struct in_addr tmp_addr; - - if (dns_addr.s_addr != 0) { - struct stat old_stat; - if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) { - *pdns_addr = dns_addr; - return 0; - } - old_stat = dns_addr_stat; - if (stat("/etc/resolv.conf", &dns_addr_stat) != 0) - return -1; - if ((dns_addr_stat.st_dev == old_stat.st_dev) - && (dns_addr_stat.st_ino == old_stat.st_ino) - && (dns_addr_stat.st_size == old_stat.st_size) - && (dns_addr_stat.st_mtime == old_stat.st_mtime)) { - *pdns_addr = dns_addr; - return 0; - } - } - - f = fopen("/etc/resolv.conf", "r"); - if (!f) - return -1; - -#ifdef DEBUG - fprintf(stderr, "IP address of your DNS(s): "); -#endif - while (fgets(buff, 512, f) != NULL) { - if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { - if (!inet_aton(buff2, &tmp_addr)) - continue; - /* If it's the first one, set it to dns_addr */ - if (!found) { - *pdns_addr = tmp_addr; - dns_addr = tmp_addr; - dns_addr_time = curtime; - } -#ifdef DEBUG - else - fprintf(stderr, ", "); -#endif - if (++found > 3) { -#ifdef DEBUG - fprintf(stderr, "(more)"); -#endif - break; - } -#ifdef DEBUG - else - fprintf(stderr, "%s", inet_ntoa(tmp_addr)); -#endif - } - } - fclose(f); - if (!found) - return -1; - return 0; -} - -#endif - -static void slirp_init_once(void) -{ - static int initialized; -#ifdef _WIN32 - WSADATA Data; -#endif - - if (initialized) { - return; - } - initialized = 1; - -#ifdef _WIN32 - WSAStartup(MAKEWORD(2,0), &Data); - atexit(winsock_cleanup); -#endif - - loopback_addr.s_addr = htonl(INADDR_LOOPBACK); - loopback_mask = htonl(IN_CLASSA_NET); -} - -static void slirp_state_save(QEMUFile *f, void *opaque); -static int slirp_state_load(QEMUFile *f, void *opaque, int version_id); - -Slirp *slirp_init(int restricted, struct in_addr vnetwork, - struct in_addr vnetmask, struct in_addr vhost, - const char *vhostname, const char *tftp_path, - const char *bootfile, struct in_addr vdhcp_start, - struct in_addr vnameserver, const char **vdnssearch, - void *opaque) -{ - Slirp *slirp = (Slirp *)g_malloc0(sizeof(Slirp)); - - slirp_init_once(); - - /* set debug flags (useful when compiled with DEBUG enabled)*/ - /* bitmask values (1 = CALL, 2 = MISC, 3 = ERROR) */ - if (getenv("SLIRP_DEBUG")) - slirp_debug = atoi(getenv("SLIRP_DEBUG")); - - slirp->restricted = restricted; - - if_init(slirp); - ip_init(slirp); - - /* Initialise mbufs *after* setting the MTU */ - m_init(slirp); - - slirp->vnetwork_addr = vnetwork; - slirp->vnetwork_mask = vnetmask; - slirp->vhost_addr = vhost; - if (vhostname) { - pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), - vhostname); - } - slirp->tftp_prefix = g_strdup(tftp_path); - slirp->bootp_filename = g_strdup(bootfile); - slirp->vdhcp_startaddr = vdhcp_start; - slirp->vnameserver_addr = vnameserver; - - if (vdnssearch) { - translate_dnssearch(slirp, vdnssearch); - } - - slirp->opaque = opaque; - - register_savevm(NULL, "slirp", 0, 3, - slirp_state_save, slirp_state_load, slirp); - - QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); - - return slirp; -} - -void slirp_cleanup(Slirp *slirp) -{ - QTAILQ_REMOVE(&slirp_instances, slirp, entry); - - unregister_savevm(NULL, "slirp", slirp); - - ip_cleanup(slirp); - m_cleanup(slirp); - - g_free(slirp->vdnssearch); - g_free(slirp->tftp_prefix); - g_free(slirp->bootp_filename); - g_free(slirp); -} - -#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) - -static void slirp_update_timeout(uint32_t *timeout) -{ - Slirp *slirp; - uint32_t t; - - if (*timeout <= TIMEOUT_FAST) { - return; - } - - t = MIN(1000, *timeout); - - /* If we have tcp timeout with slirp, then we will fill @timeout with - * more precise value. - */ - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - if (slirp->time_fasttimo) { - *timeout = TIMEOUT_FAST; - return; - } - if (slirp->do_slowtimo) { - t = MIN(TIMEOUT_SLOW, t); - } - } - *timeout = t; -} - -void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout) -{ - Slirp *slirp; - struct socket *so, *so_next; - - if (QTAILQ_EMPTY(&slirp_instances)) { - return; - } - - /* - * First, TCP sockets - */ - - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * *_slowtimo needs calling if there are IP fragments - * in the fragment queue, or there are TCP connections active - */ - slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || - (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); - - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - gushort events = 0; - - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if we need a tcp_fasttimo - */ - if (slirp->time_fasttimo == 0 && - so->so_tcpcb->t_flags & TF_DELACK) { - slirp->time_fasttimo = curtime; /* Flag when 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) { - GPollFD pfd = { so->s, G_IO_IN | G_IO_HUP | G_IO_ERR, 0 }; - - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - continue; - } - - /* - * Set for writing sockets which are connecting - */ - if (so->so_state & SS_ISFCONNECTING) { - GPollFD pfd = { so->s, G_IO_OUT | G_IO_ERR, 0 }; - - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - 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) { - events |= G_IO_OUT | G_IO_ERR; - } - - /* - * 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))) { - events |= G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI; - } - - if (events) { - GPollFD pfd = { so->s, events, 0 }; - - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - - /* - * UDP sockets - */ - for (so = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - udp_detach(so); - continue; - } else { - slirp->do_slowtimo = true; /* 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) { - GPollFD pfd = { so->s, G_IO_IN | G_IO_HUP | G_IO_ERR, 0 }; - - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - - /* - * ICMP sockets - */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - icmp_detach(so); - continue; - } else { - slirp->do_slowtimo = true; /* Let socket expire */ - } - } - - if (so->so_state & SS_ISFCONNECTED) { - GPollFD pfd = { so->s, G_IO_IN | G_IO_HUP | G_IO_ERR, 0 }; - - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - } - slirp_update_timeout(timeout); -} - -void slirp_pollfds_poll(GArray *pollfds, int select_error) -{ - Slirp *slirp; - struct socket *so, *so_next; - int ret; - - if (QTAILQ_EMPTY(&slirp_instances)) { - return; - } - - curtime = (u_int)qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * See if anything has timed out - */ - if (slirp->time_fasttimo && - ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { - tcp_fasttimo(slirp); - slirp->time_fasttimo = 0; - } - if (slirp->do_slowtimo && - ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { - ip_slowtimo(slirp); - tcp_slowtimo(slirp); - slirp->last_slowtimo = curtime; - } - - /* - * Check sockets - */ - if (!select_error) { - /* - * Check TCP sockets - */ - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - 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 G_IO_IN below if this succeeds - */ - if (revents & G_IO_PRI) { - sorecvoob(so); - } - /* - * Check sockets for reading - */ - else if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { - /* - * 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 (!(so->so_state & SS_NOFDREF) && - (revents & (G_IO_OUT | G_IO_ERR))) { - /* - * Check for non-blocking, still-connecting sockets - */ - if (so->so_state & SS_ISFCONNECTING) { - /* Connected */ - so->so_state &= ~SS_ISFCONNECTING; - - ret = send(so->s, (const char *) &ret, 0, 0); - if (ret == SOCKET_ERROR) { - /* XXXXX Must fix, zero bytes is a NOP */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) { - continue; - } - - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } - /* else so->so_state &= ~SS_ISFCONNECTING; */ - - /* - * Continue tcp_input - */ - tcp_input((struct mbuf *)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 = qemu_recv(so->s, &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_PERSISTENT_MASK; - 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_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } else { - so->so_state &= ~SS_ISFCONNECTING; - } - - } - tcp_input((struct mbuf *)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 = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - if (so->s != -1 && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { - sorecvfrom(so); - } - } - - /* - * Check incoming ICMP relies. - */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - if (so->s != -1 && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { - icmp_receive(so); - } - } - } - - if_start(slirp); - } -} - -static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) -{ - const struct arphdr *ah = (const struct arphdr *)(pkt + ETH_HLEN); - uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)]; - 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 (ah->ar_tip == ah->ar_sip) { - /* Gratuitous ARP */ - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - return; - } - - if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (ah->ar_tip == slirp->vnameserver_addr.s_addr || - ah->ar_tip == slirp->vhost_addr.s_addr) - goto arp_ok; - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_addr.s_addr == ah->ar_tip) - goto arp_ok; - } - return; - arp_ok: - memset(arp_reply, 0, sizeof(arp_reply)); - - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - - /* ARP request for alias/dns mac address */ - memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); - memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); - memcpy(&reh->h_source[2], &ah->ar_tip, 4); - 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); - rah->ar_sip = ah->ar_tip; - memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); - rah->ar_tip = ah->ar_sip; - slirp_output(slirp->opaque, arp_reply, sizeof(arp_reply)); - } - break; - case ARPOP_REPLY: - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - break; - default: - break; - } -} - -void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) -{ - struct mbuf *m; - int proto; - - if (pkt_len < ETH_HLEN) - return; - - proto = ntohs(*(const uint16_t *)(pkt + 12)); - switch(proto) { - case ETH_P_ARP: - arp_input(slirp, pkt, pkt_len); - break; - case ETH_P_IP: - m = m_get(slirp); - if (!m) - return; - /* Note: we add to align the IP header */ - if (M_FREEROOM(m) < pkt_len + 2) { - m_inc(m, pkt_len + 2); - } - 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. Returns 0 if the packet must be - * re-queued. - */ -int if_encap(Slirp *slirp, struct mbuf *ifm) -{ - uint8_t buf[1600]; - struct ethhdr *eh = (struct ethhdr *)buf; - uint8_t ethaddr[ETH_ALEN]; - const struct ip *iph = (const struct ip *)ifm->m_data; - - if (ifm->m_len + ETH_HLEN > sizeof(buf)) { - return 1; - } - - if (iph->ip_dst.s_addr == 0) { - /* 0.0.0.0 can not be a destination address, something went wrong, - * avoid making it worse */ - return 1; - } - if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { - uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)]; - struct ethhdr *reh = (struct ethhdr *)arp_req; - struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); - - if (!ifm->arp_requested) { - /* If the client addr is not known, send an ARP request */ - memset(reh->h_dest, 0xff, ETH_ALEN); - memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); - memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); - 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_REQUEST); - - /* source hw addr */ - memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); - memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); - - /* source IP */ - rah->ar_sip = slirp->vhost_addr.s_addr; - - /* target hw addr (none) */ - memset(rah->ar_tha, 0, ETH_ALEN); - - /* target IP */ - rah->ar_tip = iph->ip_dst.s_addr; - slirp->client_ipaddr = iph->ip_dst; - slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); - ifm->arp_requested = true; - - /* Expire request and drop outgoing packet after 1 second */ - ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL; - } - return 0; - } else { - memcpy(eh->h_dest, ethaddr, ETH_ALEN); - memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); - /* XXX: not correct */ - memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); - eh->h_proto = htons(ETH_P_IP); - memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); - slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); - return 1; - } -} - -/* Drop host forwarding rule, return 0 if found. */ -int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, - int host_port) -{ - struct socket *so; - struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb); - struct sockaddr_in addr; - int port = htons(host_port); - socklen_t addr_len; - - for (so = head->so_next; so != head; so = so->so_next) { - addr_len = sizeof(addr); - if ((so->so_state & SS_HOSTFWD) && - getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && - addr.sin_addr.s_addr == host_addr.s_addr && - addr.sin_port == port) { - closesocket(so->s); - sofree(so); - return 0; - } - } - - return -1; -} - -int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, - int host_port, struct in_addr guest_addr, int guest_port) -{ - if (!guest_addr.s_addr) { - guest_addr = slirp->vdhcp_startaddr; - } - if (is_udp) { - if (!udp_listen(slirp, host_addr.s_addr, htons(host_port), - guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) - return -1; - } else { - if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port), - guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) - return -1; - } - return 0; -} - -int slirp_add_exec(Slirp *slirp, int do_pty, const void *args, - struct in_addr *guest_addr, int guest_port) -{ - if (!guest_addr->s_addr) { - guest_addr->s_addr = slirp->vnetwork_addr.s_addr | - (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr); - } - if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) != - slirp->vnetwork_addr.s_addr || - guest_addr->s_addr == slirp->vhost_addr.s_addr || - guest_addr->s_addr == slirp->vnameserver_addr.s_addr) { - return -1; - } - return add_exec(&slirp->exec_list, do_pty, (char *)args, *guest_addr, - htons(guest_port)); -} - -ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) -{ - if (so->s == -1 && so->extra) { - qemu_chr_fe_write((CharDriverState*)so->extra, (const uint8_t *)buf, len); - return len; - } - - return send(so->s, (const char *)buf, len, flags); -} - -static struct socket * -slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port) -{ - struct socket *so; - - for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { - if (so->so_faddr.s_addr == guest_addr.s_addr && - htons(so->so_fport) == guest_port) { - return so; - } - } - return NULL; -} - -size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port) -{ - struct iovec iov[2]; - struct socket *so; - - so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); - - if (!so || so->so_state & SS_NOFDREF) { - return 0; - } - - if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) { - return 0; - } - - return sopreprbuf(so, iov, NULL); -} - -void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, - const uint8_t *buf, int size) -{ - int ret; - struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); - - if (!so) - return; - - ret = soreadbuf(so, (const char *)buf, size); - - if (ret > 0) - tcp_output(sototcpcb(so)); -} - -static void slirp_tcp_save(QEMUFile *f, struct tcpcb *tp) -{ - int i; - - qemu_put_sbe16(f, tp->t_state); - for (i = 0; i < TCPT_NTIMERS; i++) - qemu_put_sbe16(f, tp->t_timer[i]); - qemu_put_sbe16(f, tp->t_rxtshift); - qemu_put_sbe16(f, tp->t_rxtcur); - qemu_put_sbe16(f, tp->t_dupacks); - qemu_put_be16(f, tp->t_maxseg); - qemu_put_sbyte(f, tp->t_force); - qemu_put_be16(f, tp->t_flags); - qemu_put_be32(f, tp->snd_una); - qemu_put_be32(f, tp->snd_nxt); - qemu_put_be32(f, tp->snd_up); - qemu_put_be32(f, tp->snd_wl1); - qemu_put_be32(f, tp->snd_wl2); - qemu_put_be32(f, tp->iss); - qemu_put_be32(f, tp->snd_wnd); - qemu_put_be32(f, tp->rcv_wnd); - qemu_put_be32(f, tp->rcv_nxt); - qemu_put_be32(f, tp->rcv_up); - qemu_put_be32(f, tp->irs); - qemu_put_be32(f, tp->rcv_adv); - qemu_put_be32(f, tp->snd_max); - qemu_put_be32(f, tp->snd_cwnd); - qemu_put_be32(f, tp->snd_ssthresh); - qemu_put_sbe16(f, tp->t_idle); - qemu_put_sbe16(f, tp->t_rtt); - qemu_put_be32(f, tp->t_rtseq); - qemu_put_sbe16(f, tp->t_srtt); - qemu_put_sbe16(f, tp->t_rttvar); - qemu_put_be16(f, tp->t_rttmin); - qemu_put_be32(f, tp->max_sndwnd); - qemu_put_byte(f, tp->t_oobflags); - qemu_put_byte(f, tp->t_iobc); - qemu_put_sbe16(f, tp->t_softerror); - qemu_put_byte(f, tp->snd_scale); - qemu_put_byte(f, tp->rcv_scale); - qemu_put_byte(f, tp->request_r_scale); - qemu_put_byte(f, tp->requested_s_scale); - qemu_put_be32(f, tp->ts_recent); - qemu_put_be32(f, tp->ts_recent_age); - qemu_put_be32(f, tp->last_ack_sent); -} - -static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf) -{ - uint32_t off; - - qemu_put_be32(f, sbuf->sb_cc); - qemu_put_be32(f, sbuf->sb_datalen); - off = (uint32_t)(sbuf->sb_wptr - sbuf->sb_data); - qemu_put_sbe32(f, off); - off = (uint32_t)(sbuf->sb_rptr - sbuf->sb_data); - qemu_put_sbe32(f, off); - qemu_put_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen); -} - -static void slirp_socket_save(QEMUFile *f, struct socket *so) -{ - qemu_put_be32(f, so->so_urgc); - qemu_put_be32(f, so->so_faddr.s_addr); - qemu_put_be32(f, so->so_laddr.s_addr); - qemu_put_be16(f, so->so_fport); - qemu_put_be16(f, so->so_lport); - qemu_put_byte(f, so->so_iptos); - qemu_put_byte(f, so->so_emu); - qemu_put_byte(f, so->so_type); - qemu_put_be32(f, so->so_state); - slirp_sbuf_save(f, &so->so_rcv); - slirp_sbuf_save(f, &so->so_snd); - slirp_tcp_save(f, so->so_tcpcb); -} - -static void slirp_bootp_save(QEMUFile *f, Slirp *slirp) -{ - int i; - - for (i = 0; i < NB_BOOTP_CLIENTS; i++) { - qemu_put_be16(f, slirp->bootp_clients[i].allocated); - qemu_put_buffer(f, slirp->bootp_clients[i].macaddr, 6); - } -} - -static void slirp_state_save(QEMUFile *f, void *opaque) -{ - Slirp *slirp = (Slirp *)opaque; - struct ex_list *ex_ptr; - - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_pty == 3) { - struct socket *so; - so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, - ntohs(ex_ptr->ex_fport)); - if (!so) - continue; - - qemu_put_byte(f, 42); - slirp_socket_save(f, so); - } - qemu_put_byte(f, 0); - - qemu_put_be16(f, slirp->ip_id); - - slirp_bootp_save(f, slirp); -} - -static void slirp_tcp_load(QEMUFile *f, struct tcpcb *tp) -{ - int i; - - tp->t_state = qemu_get_sbe16(f); - for (i = 0; i < TCPT_NTIMERS; i++) - tp->t_timer[i] = qemu_get_sbe16(f); - tp->t_rxtshift = qemu_get_sbe16(f); - tp->t_rxtcur = qemu_get_sbe16(f); - tp->t_dupacks = qemu_get_sbe16(f); - tp->t_maxseg = qemu_get_be16(f); - tp->t_force = qemu_get_sbyte(f); - tp->t_flags = qemu_get_be16(f); - tp->snd_una = qemu_get_be32(f); - tp->snd_nxt = qemu_get_be32(f); - tp->snd_up = qemu_get_be32(f); - tp->snd_wl1 = qemu_get_be32(f); - tp->snd_wl2 = qemu_get_be32(f); - tp->iss = qemu_get_be32(f); - tp->snd_wnd = qemu_get_be32(f); - tp->rcv_wnd = qemu_get_be32(f); - tp->rcv_nxt = qemu_get_be32(f); - tp->rcv_up = qemu_get_be32(f); - tp->irs = qemu_get_be32(f); - tp->rcv_adv = qemu_get_be32(f); - tp->snd_max = qemu_get_be32(f); - tp->snd_cwnd = qemu_get_be32(f); - tp->snd_ssthresh = qemu_get_be32(f); - tp->t_idle = qemu_get_sbe16(f); - tp->t_rtt = qemu_get_sbe16(f); - tp->t_rtseq = qemu_get_be32(f); - tp->t_srtt = qemu_get_sbe16(f); - tp->t_rttvar = qemu_get_sbe16(f); - tp->t_rttmin = qemu_get_be16(f); - tp->max_sndwnd = qemu_get_be32(f); - tp->t_oobflags = qemu_get_byte(f); - tp->t_iobc = qemu_get_byte(f); - tp->t_softerror = qemu_get_sbe16(f); - tp->snd_scale = qemu_get_byte(f); - tp->rcv_scale = qemu_get_byte(f); - tp->request_r_scale = qemu_get_byte(f); - tp->requested_s_scale = qemu_get_byte(f); - tp->ts_recent = qemu_get_be32(f); - tp->ts_recent_age = qemu_get_be32(f); - tp->last_ack_sent = qemu_get_be32(f); - tcp_template(tp); -} - -static int slirp_sbuf_load(QEMUFile *f, struct sbuf *sbuf) -{ - uint32_t off, sb_cc, sb_datalen; - - sb_cc = qemu_get_be32(f); - sb_datalen = qemu_get_be32(f); - - sbreserve(sbuf, sb_datalen); - - if (sbuf->sb_datalen != sb_datalen) - return -ENOMEM; - - sbuf->sb_cc = sb_cc; - - off = qemu_get_sbe32(f); - sbuf->sb_wptr = sbuf->sb_data + off; - off = qemu_get_sbe32(f); - sbuf->sb_rptr = sbuf->sb_data + off; - qemu_get_buffer(f, (unsigned char*)sbuf->sb_data, sbuf->sb_datalen); - - return 0; -} - -static int slirp_socket_load(QEMUFile *f, struct socket *so) -{ - if (tcp_attach(so) < 0) - return -ENOMEM; - - so->so_urgc = qemu_get_be32(f); - so->so_faddr.s_addr = qemu_get_be32(f); - so->so_laddr.s_addr = qemu_get_be32(f); - so->so_fport = qemu_get_be16(f); - so->so_lport = qemu_get_be16(f); - so->so_iptos = qemu_get_byte(f); - so->so_emu = qemu_get_byte(f); - so->so_type = qemu_get_byte(f); - so->so_state = qemu_get_be32(f); - if (slirp_sbuf_load(f, &so->so_rcv) < 0) - return -ENOMEM; - if (slirp_sbuf_load(f, &so->so_snd) < 0) - return -ENOMEM; - slirp_tcp_load(f, so->so_tcpcb); - - return 0; -} - -static void slirp_bootp_load(QEMUFile *f, Slirp *slirp) -{ - int i; - - for (i = 0; i < NB_BOOTP_CLIENTS; i++) { - slirp->bootp_clients[i].allocated = qemu_get_be16(f); - qemu_get_buffer(f, slirp->bootp_clients[i].macaddr, 6); - } -} - -static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) -{ - Slirp *slirp = (Slirp *)opaque; - struct ex_list *ex_ptr; - - while (qemu_get_byte(f)) { - int ret; - struct socket *so = socreate(slirp); - - if (!so) - return -ENOMEM; - - ret = slirp_socket_load(f, so); - - if (ret < 0) - return ret; - - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) != - slirp->vnetwork_addr.s_addr) { - return -EINVAL; - } - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_pty == 3 && - so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr && - so->so_fport == ex_ptr->ex_fport) { - break; - } - } - if (!ex_ptr) - return -EINVAL; - - so->extra = (void *)ex_ptr->ex_exec; - } - - if (version_id >= 2) { - slirp->ip_id = qemu_get_be16(f); - } - - if (version_id >= 3) { - slirp_bootp_load(f, slirp); - } - - return 0; -} diff --git a/slirp/slirp.h b/slirp/slirp.h deleted file mode 100644 index 28c45f7ae..000000000 --- a/slirp/slirp.h +++ /dev/null @@ -1,366 +0,0 @@ -#ifndef __COMMON_H__ -#define __COMMON_H__ - -#include "config-host.h" -#include "slirp_config.h" - -#ifdef _WIN32 -#ifndef _MSC_VER -# include -#endif - -typedef char *caddr_t; - -# include -# include -# include -# include -# include -# include - -#else -# define ioctlsocket ioctl -# define closesocket(s) close(s) -# if !defined(__HAIKU__) -# define O_BINARY 0 -# endif -#endif - -#include -#ifdef HAVE_SYS_BITYPES_H -# include -#endif - -#include - -#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 -# ifdef HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#ifdef HAVE_STRING_H -# include -#else -# include -#endif - -#ifndef _WIN32 -#include -#endif - -#ifndef _WIN32 -#include -#include -#endif - -/* Systems lacking strdup() definition in . */ -#if defined(ultrix) -char *strdup(const char *); -#endif - -/* Systems lacking malloc() definition in . */ -#if defined(ultrix) || defined(hcx) -void *malloc(size_t arg); -void free(void *ptr); -#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" - -#include "qemu/queue.h" -#include "qemu/sockets.h" - -#include "libslirp.h" -#include "ip.h" -#include "tcp.h" -#include "tcp_timer.h" -#include "tcp_var.h" -#include "tcpip.h" -#include "udp.h" -#include "ip_icmp.h" -#include "mbuf.h" -#include "sbuf.h" -#include "socket.h" -#include "if.h" -#include "main.h" -#include "misc.h" -#ifdef USE_PPP -#include "ppp/pppd.h" -#include "ppp/ppp.h" -#endif - -#include "bootp.h" -#include "tftp.h" - -#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 */ - -PACKED_BEGIN -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 */ -} PACKED_END; - -PACKED_BEGIN -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 */ - uint32_t ar_sip; /* sender IP address */ - unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ - uint32_t ar_tip; /* target IP address */ -} PACKED_END; - -#define ARP_TABLE_SIZE 16 - -typedef struct ArpTable { - struct arphdr table[ARP_TABLE_SIZE]; - int next_victim; -} ArpTable; - -void arp_table_add(Slirp *slirp, uint32_t ip_addr, const uint8_t ethaddr[ETH_ALEN]); - -bool arp_table_search(Slirp *slirp, uint32_t ip_addr, - uint8_t out_ethaddr[ETH_ALEN]); - -struct Slirp { - QTAILQ_ENTRY(Slirp) entry; - u_int time_fasttimo; - u_int last_slowtimo; - bool do_slowtimo; - - /* virtual network configuration */ - struct in_addr vnetwork_addr; - struct in_addr vnetwork_mask; - struct in_addr vhost_addr; - struct in_addr vdhcp_startaddr; - struct in_addr vnameserver_addr; - - struct in_addr client_ipaddr; - char client_hostname[33]; - - int restricted; - struct ex_list *exec_list; - - /* mbuf states */ - struct mbuf m_freelist, m_usedlist; - int mbuf_alloced; - - /* if states */ - struct mbuf if_fastq; /* fast queue (for interactive data) */ - struct mbuf if_batchq; /* queue for non-interactive data */ - struct mbuf *next_m; /* pointer to next mbuf to output */ - bool if_start_busy; /* avoid if_start recursion */ - - /* ip states */ - struct ipq ipq; /* ip reass. queue */ - uint16_t ip_id; /* ip packet ctr, for ids */ - - /* bootp/dhcp states */ - BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; - char *bootp_filename; - size_t vdnssearch_len; - uint8_t *vdnssearch; - - /* tcp states */ - struct socket tcb; - struct socket *tcp_last_so; - tcp_seq tcp_iss; /* tcp initial send seq # */ - uint32_t tcp_now; /* for RFC 1323 timestamps */ - - /* udp states */ - struct socket udb; - struct socket *udp_last_so; - - /* icmp states */ - struct socket icmp; - struct socket *icmp_last_so; - - /* tftp states */ - char *tftp_prefix; - struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; - - ArpTable arp_table; - - void *opaque; -}; - -extern Slirp *slirp_instance; - -#ifndef NULL -#define NULL (void *)0 -#endif - -#ifndef FULL_BOLT -void if_start(Slirp *); -#else -void if_start(struct ttys *); -#endif - -#ifndef HAVE_STRERROR - char *strerror(int error); -#endif - -#ifndef HAVE_INDEX - char *index(const char *, int); -#endif - -#ifndef HAVE_GETHOSTID - long gethostid(void); -#endif - -#ifndef _WIN32 -#include -#endif - -#define DEFAULT_BAUD 115200 - -#define SO_OPTIONS DO_KEEPALIVE -#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL) - -/* dnssearch.c */ -int translate_dnssearch(Slirp *s, const char ** names); - -/* cksum.c */ -int cksum(struct mbuf *m, int len); - -/* if.c */ -void if_init(Slirp *); -void if_output(struct socket *, struct mbuf *); - -/* ip_input.c */ -void ip_init(Slirp *); -void ip_cleanup(Slirp *); -void ip_input(struct mbuf *); -void ip_slowtimo(Slirp *); -void ip_stripoptions(register struct mbuf *, struct mbuf *); - -/* ip_output.c */ -int ip_output(struct socket *, struct mbuf *); - -/* tcp_input.c */ -void tcp_input(register struct mbuf *, int, struct socket *); -int tcp_mss(register struct tcpcb *, u_int); - -/* tcp_output.c */ -int tcp_output(register struct tcpcb *); -void tcp_setpersist(register struct tcpcb *); - -/* tcp_subr.c */ -void tcp_init(Slirp *); -void tcp_cleanup(Slirp *); -void tcp_template(struct tcpcb *); -void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int); -struct tcpcb * tcp_newtcpcb(struct socket *); -struct tcpcb * tcp_close(register struct tcpcb *); -void tcp_sockclosed(struct tcpcb *); -int tcp_fconnect(struct socket *); -void tcp_connect(struct socket *); -int tcp_attach(struct socket *); -uint8_t tcp_tos(struct socket *); -int tcp_emu(struct socket *, struct mbuf *); -int tcp_ctl(struct socket *); -struct tcpcb *tcp_drop(struct tcpcb *tp, int err); - -#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 - -#endif diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h deleted file mode 100644 index c06f80bf8..000000000 --- a/slirp/slirp_config.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * User definable configuration options - */ - -/* 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 - -/* Define if you have unistd.h */ -#ifndef _MSC_VER -#define HAVE_UNISTD_H -#endif - -/* Define if you have stdlib.h */ -#define HAVE_STDLIB_H - -/* Define if you have sys/ioctl.h */ -#undef HAVE_SYS_IOCTL_H -#ifndef _WIN32 -#define HAVE_SYS_IOCTL_H -#endif - -/* Define if you have sys/filio.h */ -#undef HAVE_SYS_FILIO_H -#ifdef __APPLE__ -#define HAVE_SYS_FILIO_H -#endif - -/* Define if you have strerror */ -#define HAVE_STRERROR - -/* Define according to how time.h should be included */ -#define TIME_WITH_SYS_TIME 0 -#undef HAVE_SYS_TIME_H - -/* Define if you have sys/bitypes.h */ -#undef HAVE_SYS_BITYPES_H - -/* Define if the machine is big endian */ -//#undef HOST_WORDS_BIGENDIAN - -/* 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 you have a POSIX.1 sys/wait.h */ -#undef HAVE_SYS_WAIT_H - -/* Define if you have sys/select.h */ -#undef HAVE_SYS_SELECT_H -#ifndef _WIN32 -#define HAVE_SYS_SELECT_H -#endif - -/* Define if you have strings.h */ -#define HAVE_STRING_H - -/* Define if you have arpa/inet.h */ -#undef HAVE_ARPA_INET_H -#ifndef _WIN32 -#define HAVE_ARPA_INET_H -#endif - -/* Define if you have sys/signal.h */ -#undef HAVE_SYS_SIGNAL_H - -/* Define if you have sys/stropts.h */ -#undef HAVE_SYS_STROPTS_H - -/* Define to whatever your compiler thinks inline should be */ -//#define inline inline - -/* Define to whatever your compiler thinks const should be */ -//#define const const - -/* Define if your compiler doesn't like prototypes */ -#undef NO_PROTOTYPES - -/* Define to sizeof(char) */ -#define SIZEOF_CHAR 1 - -/* Define to sizeof(short) */ -#define SIZEOF_SHORT 2 - -/* Define to sizeof(int) */ -#define SIZEOF_INT 4 - -/* Define to sizeof(char *) */ -#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8) - -/* Define if you have random() */ -#undef HAVE_RANDOM - -/* Define if you have srandom() */ -#undef HAVE_SRANDOM - -/* Define if you have inet_aton */ -#undef HAVE_INET_ATON -#ifndef _WIN32 -#define HAVE_INET_ATON -#endif - -/* Define if you have setenv */ -#undef HAVE_SETENV - -/* Define if you have index() */ -#define 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 */ -#define HAVE_GETHOSTID - -/* Define if you DON'T have unix-domain sockets */ -#undef NO_UNIX_SOCKETS -#ifdef _WIN32 -#define NO_UNIX_SOCKETS -#endif - -/* 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/slirp/socket.c b/slirp/socket.c deleted file mode 100644 index eebe282ed..000000000 --- a/slirp/socket.c +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#include "qemu-common.h" -#include -#include "ip_icmp.h" -#ifdef __sun__ -#include -#endif - -static void sofcantrcvmore(struct socket *so); -static void sofcantsendmore(struct socket *so); - -struct socket * -solookup(struct socket *head, struct in_addr laddr, u_int lport, - struct in_addr faddr, u_int fport) -{ - struct socket *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 socket *)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 socket * -socreate(Slirp *slirp) -{ - struct socket *so; - - so = (struct socket *)malloc(sizeof(struct socket)); - if(so) { - memset(so, 0, sizeof(struct socket)); - so->so_state = SS_NOFDREF; - so->s = -1; - so->slirp = slirp; - so->pollfds_idx = -1; - } - return(so); -} - -/* - * remque and free a socket, clobber cache - */ -void -sofree(struct socket *so) -{ - Slirp *slirp = so->slirp; - - if (so->so_emu==EMU_RSH && so->extra) { - sofree((struct socket *)so->extra); - so->extra=NULL; - } - if (so == slirp->tcp_last_so) { - slirp->tcp_last_so = &slirp->tcb; - } else if (so == slirp->udp_last_so) { - slirp->udp_last_so = &slirp->udb; - } else if (so == slirp->icmp_last_so) { - slirp->icmp_last_so = &slirp->icmp; - } - m_free(so->so_m); - - if(so->so_next && so->so_prev) - remque(so); /* crashes if so is not in a queue */ - - free(so); -} - -size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np) -{ - int n, lss, total; - struct sbuf *sb = &so->so_snd; - int len = sb->sb_datalen - sb->sb_cc; - int mss = so->so_tcpcb->t_maxseg; - - DEBUG_CALL("sopreprbuf"); - DEBUG_ARG("so = %lx", (long )so); - - iov[0].iov_base = sb->sb_wptr; - iov[1].iov_base = NULL; - iov[1].iov_len = 0; - if (np) - *np = 1; - if (len <= 0) - return 0; - - 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; - } - } - if (np) - *np = n; - - return iov[0].iov_len + (n - 1) * iov[1].iov_len; -} - -/* - * 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(struct socket *so) -{ - int n, nn; - struct sbuf *sb = &so->so_snd; - struct iovec iov[2]; - - 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 - */ - sopreprbuf(so, iov, &n); - -#ifdef HAVE_READV - nn = readv(so->s, (struct iovec *)iov, n); - DEBUG_MISC(" ... read nn = %d bytes\n", nn); -#else - nn = qemu_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(" --- 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 = qemu_recv(so->s, iov[1].iov_base, iov[1].iov_len,0); - if (ret > 0) - nn += ret; - } - - DEBUG_MISC(" ... 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; -} - -int soreadbuf(struct socket *so, const char *buf, size_t size) -{ - int n, nn, copy = size; - struct sbuf *sb = &so->so_snd; - struct iovec iov[2]; - - DEBUG_CALL("soreadbuf"); - 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 - */ - if (sopreprbuf(so, iov, &n) < size) - goto err; - - nn = MIN(iov[0].iov_len, copy); - memcpy(iov[0].iov_base, buf, nn); - - copy -= nn; - buf += nn; - - if (copy == 0) - goto done; - - memcpy(iov[1].iov_base, buf, copy); - -done: - /* Update fields */ - sb->sb_cc += size; - sb->sb_wptr += size; - if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) - sb->sb_wptr -= sb->sb_datalen; - return size; -err: - - sofcantrcvmore(so); - tcp_sockclosed(sototcpcb(so)); - fprintf(stderr, "soreadbuf buffer to small"); - return -1; -} - -/* - * 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(struct socket *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(struct socket *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 = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */ - so->so_urgc -= n; - - DEBUG_MISC(" --- 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 = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ -#ifdef DEBUG - if (n != len) - DEBUG_ERROR("Didn't send all data urgently XXXXX\n"); -#endif - DEBUG_MISC(" ---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(struct socket *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) { - (void)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 - */ - - iov[0].iov_base = sb->sb_rptr; - iov[1].iov_base = NULL; - iov[1].iov_len = 0; - 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(" ... wrote nn = %d bytes\n", nn); -#else - nn = slirp_send(so, 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(" --- 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 = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0); - if (ret > 0) - nn += ret; - } - DEBUG_MISC(" ... 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(struct socket *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(" 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 = NULL; /* Don't m_free() it again! */ - } - /* No need for this socket anymore, udp_detach it */ - udp_detach(so); - } else { /* A "normal" UDP packet */ - struct mbuf *m; - u_int len; -#ifdef _WIN32 - unsigned long n; -#else - int n; -#endif - - m = m_get(so->slirp); - if (!m) { - 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(" 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(" 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 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(struct socket *so, struct mbuf *m) -{ - Slirp *slirp = so->slirp; - 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 & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; - - DEBUG_MISC(" 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_PERSISTENT_MASK; - so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */ - return 0; -} - -/* - * Listen for incoming TCP connections - */ -struct socket * -tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, - u_int lport, int flags) -{ - struct sockaddr_in addr; - struct socket *so; - int s, opt = 1; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, addrlen); - - DEBUG_CALL("tcp_listen"); - DEBUG_ARG("haddr = %x", haddr); - DEBUG_ARG("hport = %d", hport); - DEBUG_ARG("laddr = %x", laddr); - DEBUG_ARG("lport = %d", lport); - DEBUG_ARG("flags = %x", flags); - - so = socreate(slirp); - if (!so) { - 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, &slirp->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_PERSISTENT_MASK; - so->so_state |= (SS_FACCEPTCONN | flags); - so->so_lport = lport; /* Kept in network format */ - so->so_laddr.s_addr = laddr; /* Ditto */ - - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = haddr; - addr.sin_port = hport; - - if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) == SOCKET_ERROR) || - (socket_set_fast_reuse(s) == SOCKET_ERROR) || - (bind(s,(struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) || - (listen(s,1) == SOCKET_ERROR)) { - int tmperrno = errno; /* Don't clobber the real reason we failed */ - - if (s != SOCKET_ERROR) - closesocket(s); - sofree(so); - fprintf (stderr, "Socket Error %d", tmperrno); - /* Restore the real errno */ -#ifdef _WIN32 - WSASetLastError(tmperrno); -#else - errno = tmperrno; -#endif - return NULL; - } - (void)qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); - - (void)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 = slirp->vhost_addr; - else - so->so_faddr = addr.sin_addr; - - so->s = s; - return so; -} - -/* - * 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(struct socket *so) -{ - so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE| - SS_FCANTSENDMORE|SS_FWDRAIN); - so->so_state |= SS_ISFCONNECTING; /* Clobber other states */ -} - -void -soisfconnected(struct socket *so) -{ - so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF); - so->so_state |= SS_ISFCONNECTED; /* Clobber other states */ -} - -static void -sofcantrcvmore(struct socket *so) -{ - if ((so->so_state & SS_NOFDREF) == 0) { - shutdown(so->s,0); - } - so->so_state &= ~(SS_ISFCONNECTING); - if (so->so_state & SS_FCANTSENDMORE) { - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; /* Don't select it */ - } else { - so->so_state |= SS_FCANTRCVMORE; - } -} - -static void -sofcantsendmore(struct socket *so) -{ - if ((so->so_state & SS_NOFDREF) == 0) { - shutdown(so->s,1); /* send FIN to fhost */ - } - so->so_state &= ~(SS_ISFCONNECTING); - if (so->so_state & SS_FCANTRCVMORE) { - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; /* as above */ - } else { - so->so_state |= SS_FCANTSENDMORE; - } -} - -/* - * Set write drain mode - * Set CANTSENDMORE once all data has been write()n - */ -void -sofwdrain(struct socket *so) -{ - if (so->so_rcv.sb_cc) - so->so_state |= SS_FWDRAIN; - else - sofcantsendmore(so); -} diff --git a/slirp/socket.h b/slirp/socket.h deleted file mode 100644 index e737bdc88..000000000 --- a/slirp/socket.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#ifndef _SLIRP_SOCKET_H_ -#define _SLIRP_SOCKET_H_ - -#define SO_EXPIRE 240000 -#define SO_EXPIREFAST 10000 - -/* - * Our socket structure - */ - -struct socket { - struct socket *so_next,*so_prev; /* For a linked list of sockets */ - - int s; /* The actual socket */ - - int pollfds_idx; /* GPollFD GArray index */ - - Slirp *slirp; /* managing slirp instance */ - - /* XXX union these with not-yet-used sbuf params */ - struct mbuf *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 */ - uint16_t so_fport; /* foreign port */ - uint16_t so_lport; /* local port */ - - uint8_t so_iptos; /* Type of service */ - uint8_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_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 */ - -#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */ -#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ -#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */ - -struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int); -struct socket * socreate(Slirp *); -void sofree(struct socket *); -int soread(struct socket *); -void sorecvoob(struct socket *); -int sosendoob(struct socket *); -int sowrite(struct socket *); -void sorecvfrom(struct socket *); -int sosendto(struct socket *, struct mbuf *); -struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, - int); -void soisfconnecting(register struct socket *); -void soisfconnected(register struct socket *); -void sofwdrain(struct socket *); -struct iovec; /* For win32 */ -size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); -int soreadbuf(struct socket *so, const char *buf, size_t size); - -#endif /* _SOCKET_H_ */ diff --git a/slirp/tcp.h b/slirp/tcp.h deleted file mode 100644 index 22f1c8a55..000000000 --- a/slirp/tcp.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - * 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 uint32_t tcp_seq; - -#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ -#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ - -#define TCP_SNDSPACE 8192 -#define TCP_RCVSPACE 8192 - -/* - * TCP header. - * Per RFC 793, September, 1981. - */ -#define tcphdr slirp_tcphdr -struct tcphdr { - uint16_t th_sport; /* source port */ - uint16_t th_dport; /* destination port */ - tcp_seq th_seq; /* sequence number */ - tcp_seq th_ack; /* acknowledgement number */ -#ifdef HOST_WORDS_BIGENDIAN - uint8_t th_off:4, /* data offset */ - th_x2:4; /* (unused) */ -#else - uint8_t th_x2:4, /* (unused) */ - th_off:4; /* data offset */ -#endif - uint8_t th_flags; - uint16_t th_win; /* window */ - uint16_t th_sum; /* checksum */ - uint16_t th_urp; /* urgent pointer */ -}; - -#include "tcp_var.h" - -#ifndef TH_FIN -#define TH_FIN 0x01 -#define TH_SYN 0x02 -#define TH_RST 0x04 -#define TH_PUSH 0x08 -#define TH_ACK 0x10 -#define TH_URG 0x20 -#endif - -#ifndef TCPOPT_EOL -#define TCPOPT_EOL 0 -#define TCPOPT_NOP 1 -#define TCPOPT_MAXSEG 2 -#define TCPOPT_WINDOW 3 -#define TCPOPT_SACK_PERMITTED 4 /* Experimental */ -#define TCPOPT_SACK 5 /* Experimental */ -#define TCPOPT_TIMESTAMP 8 - -#define TCPOPT_TSTAMP_HDR \ - (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP) -#endif - -#ifndef TCPOLEN_MAXSEG -#define TCPOLEN_MAXSEG 4 -#define TCPOLEN_WINDOW 3 -#define TCPOLEN_SACK_PERMITTED 2 -#define TCPOLEN_TIMESTAMP 10 -#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */ -#endif - -/* - * 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. - */ -#undef TCP_MSS -#define TCP_MSS 1460 - -#undef TCP_MAXWIN -#define TCP_MAXWIN 65535u /* largest value for (unscaled) window */ - -#undef TCP_MAX_WINSHIFT -#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 - -/* - * 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 */ - -#endif diff --git a/slirp/tcp_input.c b/slirp/tcp_input.c deleted file mode 100644 index 11abaf7ab..000000000 --- a/slirp/tcp_input.c +++ /dev/null @@ -1,1496 +0,0 @@ -/* - * 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 "ip_icmp.h" - -#define TCPREXMTTHRESH 3 - -#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 && \ - tcpfrag_list_empty(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; \ - if (so->so_emu) { \ - if (tcp_emu((so),(m))) sbappend((so), (m)); \ - } else \ - sbappend((so), (m)); \ - } 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 && \ - tcpfrag_list_empty(tp) && \ - (tp)->t_state == TCPS_ESTABLISHED) { \ - tp->t_flags |= TF_DELACK; \ - (tp)->rcv_nxt += (ti)->ti_len; \ - flags = (ti)->ti_flags & TH_FIN; \ - if (so->so_emu) { \ - if (tcp_emu((so),(m))) sbappend(so, (m)); \ - } else \ - sbappend((so), (m)); \ - } else { \ - (flags) = tcp_reass((tp), (ti), (m)); \ - tp->t_flags |= TF_ACKNOW; \ - } \ -} -#endif -static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, - struct tcpiphdr *ti); -static void tcp_xmit_timer(register struct tcpcb *tp, int rtt); - -static int -tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, - struct mbuf *m) -{ - register struct tcpiphdr *q; - struct socket *so = tp->t_socket; - int flags; - - /* - * Call with ti==NULL after become established to - * force pre-ESTABLISHED data up to user socket. - */ - if (ti == NULL) - goto present; - - /* - * Find a segment which begins after this one does. - */ - for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp); - q = tcpiphdr_next(q)) - 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 (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) { - register int i; - q = tcpiphdr_prev(q); - /* 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) { - m_free(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 = tcpiphdr_next(q); - } - ti->ti_mbuf = m; - - /* - * While we overlap succeeding segments trim them or, - * if they are completely covered, dequeue them. - */ - while (!tcpfrag_list_end(q, tp)) { - register 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(q->ti_mbuf, i); - break; - } - q = tcpiphdr_next(q); - m = tcpiphdr_prev(q)->ti_mbuf; - remque(tcpiphdr2qlink(tcpiphdr_prev(q))); - m_free(m); - } - - /* - * Stick new segment in its place. - */ - insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); - -present: - /* - * Present data to user, advancing rcv_nxt through - * completed sequence space. - */ - if (!TCPS_HAVEESTABLISHED(tp->t_state)) - return (0); - ti = tcpfrag_list_first(tp); - if (tcpfrag_list_end(ti, 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(tcpiphdr2qlink(ti)); - m = ti->ti_mbuf; - ti = tcpiphdr_next(ti); - if (so->so_state & SS_FCANTSENDMORE) - m_free(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); - return (flags); -} - -/* - * TCP input routine, follows pages 65-76 of the - * protocol specification dated September, 1981 very closely. - */ -void -tcp_input(struct mbuf *m, int iphlen, struct socket *inso) -{ - struct ip save_ip, *ip; - register struct tcpiphdr *ti; - caddr_t optp = NULL; - int optlen = 0; - int len, tlen, off; - register struct tcpcb *tp = NULL; - register int tiflags; - struct socket *so = NULL; - int todrop, acked, ourfinisacked, needoutput = 0; - int iss = 0; - u_long tiwin; - int ret; - struct ex_list *ex_ptr; - Slirp *slirp; - - DEBUG_CALL("tcp_input"); - DEBUG_ARGS(" 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; - slirp = so->slirp; - - /* Re-set a few variables */ - tp = sototcpcb(so); - m = so->so_m; - so->so_m = NULL; - ti = so->so_ti; - tiwin = ti->ti_win; - tiflags = ti->ti_flags; - - goto cont_conn; - } - slirp = m->slirp; - - /* - * Get IP and TCP header together in first mbuf. - * Note: IP leaves IP header in first mbuf. - */ - ti = mtod(m, struct tcpiphdr *); - if (iphlen > sizeof(struct ip )) { - ip_stripoptions(m, (struct mbuf *)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; - tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; - memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); - ti->ti_x1 = 0; - ti->ti_len = htons((uint16_t)tlen); - len = sizeof(struct ip ) + tlen; - if(cksum(m, len)) { - 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) { - goto drop; - } - tlen -= off; - ti->ti_len = tlen; - if (off > sizeof (struct tcphdr)) { - optlen = off - sizeof (struct tcphdr); - optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr); - } - 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 = slirp->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(&slirp->tcb, ti->ti_src, ti->ti_sport, - ti->ti_dst, ti->ti_dport); - if (so) - slirp->tcp_last_so = so; - } - - /* - * 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 == NULL) { - if (slirp->restricted) { - /* Any hostfwds will have an existing socket, so we only get here - * for non-hostfwd connections. These should be dropped, unless it - * happens to be a guestfwd. - */ - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_fport == ti->ti_dport && - ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) { - break; - } - } - if (!ex_ptr) { - goto dropwithreset; - } - } - - if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN) - goto dropwithreset; - - if ((so = socreate(slirp)) == 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); - - 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 == NULL) - goto dropwithreset; - if (tp->t_state == TCPS_CLOSED) - goto drop; - - 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] = TCPTV_KEEPINTVL; - else - tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; - - /* - * 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); - - /* - * 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 && - ti->ti_seq == tp->rcv_nxt && - tiwin && tiwin == tp->snd_wnd && - tp->snd_nxt == tp->snd_max) { - 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. - */ - 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; - sbdrop(&so->so_snd, acked); - tp->snd_una = ti->ti_ack; - m_free(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; - - /* - * 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 && - tcpfrag_list_empty(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. - */ - tp->rcv_nxt += ti->ti_len; - /* - * Add data to socket buffer. - */ - if (so->so_emu) { - if (tcp_emu(so,m)) sbappend(so, m); - } else - sbappend(so, m); - - /* - * 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 & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr && - so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) { - /* May be an add exec */ - for (ex_ptr = slirp->exec_list; ex_ptr; - ex_ptr = ex_ptr->ex_next) { - if(ex_ptr->ex_fport == so->so_fport && - so->so_faddr.s_addr == ex_ptr->ex_addr.s_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) && -#if defined(_WIN32) - socket_error() != WSAEINPROGRESS && socket_error() != WSAEWOULDBLOCK -#else - (errno != EINPROGRESS) && (errno != EWOULDBLOCK) -#endif - ) { - u_char code=ICMP_UNREACH_NET; - DEBUG_MISC(" 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)); - } - tcp_close(tp); - m_free(m); - } else { - /* - * Haven't connected yet, save the current mbuf - * 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; - tcp_template(tp); - } - 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); - - if (iss) - tp->iss = iss; - else - tp->iss = slirp->tcp_iss; - slirp->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; - 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) { - 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)) { - soisfconnected(so); - tp->t_state = TCPS_ESTABLISHED; - - (void) tcp_reass(tp, (struct tcpiphdr *)0, - (struct mbuf *)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; - } - 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. - * 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. - */ - 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; - } - 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); - 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) { - if (todrop >= 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; - } else { - goto dropafterack; - } - } - m_adj(m, -todrop); - ti->ti_len -= todrop; - tiflags &= ~(TH_PUSH|TH_FIN); - } - - /* - * 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: - case TCPS_ESTABLISHED: - case TCPS_FIN_WAIT_1: - case TCPS_FIN_WAIT_2: - case TCPS_CLOSE_WAIT: - tp->t_state = TCPS_CLOSED; - tcp_close(tp); - goto drop; - - case TCPS_CLOSING: - case TCPS_LAST_ACK: - case TCPS_TIME_WAIT: - 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; - 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_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; /* CTL_CMD */ - } else { - needoutput = 1; - tp->t_state = TCPS_FIN_WAIT_1; - } - } else { - soisfconnected(so); - } - - (void) tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)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) { - DEBUG_MISC(" 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)) { - goto dropafterack; - } - acked = ti->ti_ack - tp->snd_una; - - /* - * If 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 (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). - */ - { - register u_int cw = tp->snd_cwnd; - register 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 > (int)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; - } - 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) { - 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; - } - 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) { - 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))))) { - 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: - - /* - * If this is a small packet, then ACK now - with Nagel - * congestion avoidance sender won't send more until - * he gets an ACK. - */ - if (ti->ti_len && (unsigned)ti->ti_len <= 5 && - ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { - tp->t_flags |= TF_ACKNOW; - } - - /* - * 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); - } 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. - */ - 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; - 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; - } - } - - /* - * 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_free(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); -} - -static void -tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) -{ - uint16_t mss; - int opt, optlen; - - DEBUG_CALL("tcp_dooptions"); - DEBUG_ARGS(" 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; - } - } -} - - -/* - * 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 socket *so; - struct tcpiphdr *ti; - register struct mbuf *m; -{ - int cnt = ti->ti_urp - 1; - - while (cnt >= 0) { - if (m->m_len > cnt) { - char *cp = mtod(m, caddr_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. - */ - -static void -tcp_xmit_timer(register struct tcpcb *tp, int rtt) -{ - register short delta; - - DEBUG_CALL("tcp_xmit_timer"); - DEBUG_ARG("tp = %lx", (long)tp); - DEBUG_ARG("rtt = %d", rtt); - - 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 mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES - * to utilize large mbufs. 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(struct tcpcb *tp, u_int offer) -{ - struct socket *so = tp->t_socket; - u_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(" returning mss = %d\n", mss); - - return mss; -} diff --git a/slirp/tcp_output.c b/slirp/tcp_output.c deleted file mode 100644 index 3c20b8b90..000000000 --- a/slirp/tcp_output.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * 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 - -static const 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, -}; - - -#undef MAX_TCPOPTLEN -#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(struct tcpcb *tp) -{ - register struct socket *so = tp->t_socket; - register long len, win; - int off, flags, error; - register struct mbuf *m; - register 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(" --- 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 < (int)so->so_snd.sb_cc) - flags &= ~TH_FIN; - win = 1; - } else { - tp->t_timer[TCPT_PERSIST] = 0; - tp->t_rxtshift = 0; - } - } - - len = min((long)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) >= (long)so->so_snd.sb_cc)) - goto send; - if (tp->t_force) - goto send; - if ((len >= (long)(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. - */ - 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 mbuf, 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) { - uint16_t mss; - - opt[0] = TCPOPT_MAXSEG; - opt[1] = 4; - mss = htons((uint16_t) tcp_mss(tp, 0)); - memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss)); - optlen = 4; - } - } - - hdrlen += optlen; - - /* - * Adjust data length if insertion of options will - * bump the packet length beyond the t_maxseg length. - */ - if (len > (long)(tp->t_maxseg - optlen)) { - len = tp->t_maxseg - optlen; - sendalot = 1; - } - - /* - * Grab a header mbuf, attaching a copy of data to - * be transmitted, and initialize the header from - * the template for sends on this connection. - */ - if (len) { - m = m_get(so->slirp); - if (m == NULL) { - error = 1; - goto out; - } - m->m_data += IF_MAXLINKHDR; - m->m_len = hdrlen; - - sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen); - m->m_len += len; - - /* - * 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 { - m = m_get(so->slirp); - if (m == NULL) { - error = 1; - goto out; - } - m->m_data += IF_MAXLINKHDR; - m->m_len = hdrlen; - } - - ti = mtod(m, struct tcpiphdr *); - - memcpy((caddr_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((caddr_t)(ti + 1), (caddr_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((uint16_t) (win>>tp->rcv_scale)); - - if (SEQ_GT(tp->snd_up, tp->snd_una)) { - ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); - 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((uint16_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; - } - } - - /* - * 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 = IPDEFTTL; - ((struct ip *)ti)->ip_tos = so->so_iptos; - - error = ip_output(so, m); - } - if (error) { -out: - return (error); - } - - /* - * 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(struct tcpcb *tp) -{ - int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; - - /* - * 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/slirp/tcp_subr.c b/slirp/tcp_subr.c deleted file mode 100644 index 8d25af8fa..000000000 --- a/slirp/tcp_subr.c +++ /dev/null @@ -1,926 +0,0 @@ -/* - * 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. - */ - -#include - -/* patchable/settable parameters for tcp */ -/* Don't do rfc1323 performance enhancements */ -#define TCP_DO_RFC1323 0 - -/* - * Tcp initialization - */ -void -tcp_init(Slirp *slirp) -{ - slirp->tcp_iss = 1; /* wrong */ - slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb; - slirp->tcp_last_so = &slirp->tcb; -} - -void tcp_cleanup(Slirp *slirp) -{ - while (slirp->tcb.so_next != &slirp->tcb) { - tcp_close(sototcpcb(slirp->tcb.so_next)); - } -} - -/* - * 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. - */ -void -tcp_template(struct tcpcb *tp) -{ - struct socket *so = tp->t_socket; - register struct tcpiphdr *n = &tp->t_template; - - n->ti_mbuf = NULL; - 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 mbuf containing it and any other - * attached mbufs. - * - * In any case the ack and sequence number of the transmitted - * segment are as specified by the parameters. - */ -void -tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, - tcp_seq ack, tcp_seq seq, int flags) -{ - register int tlen; - int win = 0; - - DEBUG_CALL("tcp_respond"); - DEBUG_ARG("tp = %p", tp); - DEBUG_ARG("ti = %p", ti); - DEBUG_ARG("m = %p", 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 == NULL) { - if (!tp || (m = m_get(tp->t_socket->slirp)) == NULL) - return; - tlen = 0; - 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 mbuf point to ti - */ - m->m_data = (caddr_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, uint32_t); - xchg(ti->ti_dport, ti->ti_sport, uint16_t); -#undef xchg - } - ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); - tlen += sizeof (struct tcpiphdr); - m->m_len = tlen; - - ti->ti_mbuf = NULL; - 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((uint16_t) (win >> tp->rcv_scale)); - else - ti->ti_win = htons((uint16_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 = IPDEFTTL; - - (void) ip_output((struct socket *)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(struct socket *so) -{ - register 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 = (struct tcpiphdr*)tp; - tp->t_maxseg = TCP_MSS; - - 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 = TCPTV_SRTTDFLT << 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) -{ - 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); - } - 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(struct tcpcb *tp) -{ - register struct tcpiphdr *t; - struct socket *so = tp->t_socket; - Slirp *slirp = so->slirp; - register struct mbuf *m; - - DEBUG_CALL("tcp_close"); - DEBUG_ARG("tp = %lx", (long )tp); - - /* free the reassembly queue, if any */ - t = tcpfrag_list_first(tp); - while (!tcpfrag_list_end(t, tp)) { - t = tcpiphdr_next(t); - m = tcpiphdr_prev(t)->ti_mbuf; - remque(tcpiphdr2qlink(tcpiphdr_prev(t))); - m_free(m); - } - free(tp); - so->so_tcpcb = NULL; - /* clobber input socket cache if we're closing the cached connection */ - if (so == slirp->tcp_last_so) - slirp->tcp_last_so = &slirp->tcb; - closesocket(so->s); - sbfree(&so->so_rcv); - sbfree(&so->so_snd); - sofree(so); - return ((struct tcpcb *)0); -} - -/* - * 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(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; - } - 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(struct socket *so) -{ - Slirp *slirp = so->slirp; - int ret=0; - - DEBUG_CALL("tcp_fconnect"); - DEBUG_ARG("so = %lx", (long )so); - - if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) { - int opt, s=so->s; - struct sockaddr_in addr; - - (void)qemu_set_nonblock(s); - socket_set_fast_reuse(s); - opt = 1; - (void)qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); - - addr.sin_family = AF_INET; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - /* It's an alias */ - if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { - if (get_dns_addr(&addr.sin_addr) < 0) - addr.sin_addr = loopback_addr; - } else { - addr.sin_addr = loopback_addr; - } - } else - addr.sin_addr = so->so_faddr; - addr.sin_port = so->so_fport; - - DEBUG_MISC(" 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(struct socket *inso) -{ - Slirp *slirp = inso->slirp; - struct socket *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 { - so = socreate(slirp); - if (so == 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; - } - - tcp_mss(sototcpcb(so), 0); - - s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); - if (s < 0) { - tcp_close(sototcpcb(so)); /* This will sofree() as well */ - return; - } - qemu_set_nonblock(s); - (void)socket_set_fast_reuse(s); - opt = 1; - (void)qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); - (void)socket_set_nodelay(s); - - 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_mask) == - (loopback_addr.s_addr & loopback_mask)) { - so->so_faddr = slirp->vhost_addr; - } - - /* Close the accept() socket, set right state */ - if (inso->so_state & SS_FACCEPTONCE) { - /* If we only accept once, close the accept() socket */ - closesocket(so->s); - - /* Don't select it yet, even though we have an FD */ - /* if it's not FACCEPTONCE, it's already NOFDREF */ - so->so_state = SS_NOFDREF; - } - so->s = s; - so->so_state |= SS_INCOMING; - - so->so_iptos = tcp_tos(so); - tp = sototcpcb(so); - - tcp_template(tp); - - tp->t_state = TCPS_SYN_SENT; - tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; - tp->iss = slirp->tcp_iss; - slirp->tcp_iss += TCP_ISSINCR/2; - tcp_sendseqinit(tp); - tcp_output(tp); -} - -/* - * Attach a TCPCB to a socket. - */ -int -tcp_attach(struct socket *so) -{ - if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) - return -1; - - insque(so, &so->slirp->tcb); - - return 0; -} - -/* - * Set the socket's type of service field - */ -static const 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} -}; - -static struct emu_t *tcpemu = NULL; - -/* - * Return TOS according to the above table - */ -uint8_t -tcp_tos(struct socket *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; -} - -/* - * 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 mbuf m is still valid and should be - * sbappend()ed - * - * NOTE: if you return 0 you MUST m_free() the mbuf! - */ -int -tcp_emu(struct socket *so, struct mbuf *m) -{ - Slirp *slirp = so->slirp; - u_int n1, n2, n3, n4, n5, n6; - char buff[257]; - uint32_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 socket *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, "%u%*[ ,]%u", &n1, &n2) == 2) { - HTONS(n1); - HTONS(n2); - /* n2 is the one on our host */ - for (tmpso = slirp->tcb.so_next; - tmpso != &slirp->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 = snprintf(so_rcv->sb_data, - so_rcv->sb_datalen, - "%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; - } - - case EMU_FTP: /* ftp */ - *(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */ - if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) { - /* - * Need to emulate the PORT command - */ - x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\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 = tcp_listen(slirp, INADDR_ANY, 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 += snprintf(bptr, m->m_size - m->m_len, - "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 (%u,%u,%u,%u,%u,%u)\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 = tcp_listen(slirp, INADDR_ANY, 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 += snprintf(bptr, m->m_size - m->m_len, - "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 = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr, - htons(lport), SS_FACCEPTONCE)) != NULL) - m->m_len = snprintf(m->m_data, m->m_size, "%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 = tcp_listen(slirp, INADDR_ANY, 0, - htonl(laddr), htons(lport), - SS_FACCEPTONCE)) == NULL) { - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ - m->m_len += snprintf(bptr, m->m_size, - "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 = tcp_listen(slirp, INADDR_ANY, 0, - htonl(laddr), htons(lport), - SS_FACCEPTONCE)) == NULL) { - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ - m->m_len += snprintf(bptr, m->m_size, - "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 = tcp_listen(slirp, INADDR_ANY, 0, - htonl(laddr), htons(lport), - SS_FACCEPTONCE)) == NULL) { - return 1; - } - m->m_len = bptr - m->m_data; /* Adjust length */ - m->m_len += snprintf(bptr, m->m_size, - "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(slirp, INADDR_ANY, - 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(struct socket *so) -{ - Slirp *slirp = so->slirp; - struct sbuf *sb = &so->so_snd; - struct ex_list *ex_ptr; - int do_pty; - - DEBUG_CALL("tcp_ctl"); - DEBUG_ARG("so = %lx", (long )so); - - if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { - /* Check if it's pty_exec */ - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_fport == so->so_fport && - so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { - if (ex_ptr->ex_pty == 3) { - so->s = -1; - so->extra = (void *)ex_ptr->ex_exec; - return 1; - } - do_pty = ex_ptr->ex_pty; - DEBUG_MISC(" executing %s\n", ex_ptr->ex_exec); - return fork_exec(so, ex_ptr->ex_exec, do_pty); - } - } - } - sb->sb_cc = - snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data), - "Error: No application configured.\r\n"); - sb->sb_wptr += sb->sb_cc; - return 0; -} diff --git a/slirp/tcp_timer.c b/slirp/tcp_timer.c deleted file mode 100644 index 9917b5043..000000000 --- a/slirp/tcp_timer.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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 - -static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer); - -/* - * Fast timeout routine for processing delayed acks - */ -void -tcp_fasttimo(Slirp *slirp) -{ - register struct socket *so; - register struct tcpcb *tp; - - DEBUG_CALL("tcp_fasttimo"); - - so = slirp->tcb.so_next; - if (so) - for (; so != &slirp->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; - (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(Slirp *slirp) -{ - register struct socket *ip, *ipnxt; - register struct tcpcb *tp; - register int i; - - DEBUG_CALL("tcp_slowtimo"); - - /* - * Search through tcb's and update active timers. - */ - ip = slirp->tcb.so_next; - if (ip == NULL) { - return; - } - for (; ip != &slirp->tcb; ip = ipnxt) { - ipnxt = ip->so_next; - tp = sototcpcb(ip); - if (tp == NULL) { - 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: - ; - } - slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */ - slirp->tcp_now++; /* for timestamps */ -} - -/* - * Cancel all timers for TCP tp. - */ -void -tcp_canceltimers(struct tcpcb *tp) -{ - register int i; - - for (i = 0; i < TCPT_NTIMERS; i++) - tp->t_timer[i] = 0; -} - -const int tcp_backoff[TCP_MAXRXTSHIFT + 1] = - { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 }; - -/* - * TCP timer processing. - */ -static struct tcpcb * -tcp_timers(register struct tcpcb *tp, int timer) -{ - register 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] = TCPTV_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; - 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; - } - 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) { - 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: - 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: - if (tp->t_state < TCPS_ESTABLISHED) - goto dropit; - - if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) { - if (tp->t_idle >= TCPTV_KEEP_IDLE + 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. - */ - tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL, - tp->rcv_nxt, tp->snd_una - 1, 0); - tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; - } else - tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; - break; - - dropit: - tp = tcp_drop(tp, 0); - break; - } - - return (tp); -} diff --git a/slirp/tcp_var.h b/slirp/tcp_var.h deleted file mode 100644 index ab3459fbc..000000000 --- a/slirp/tcp_var.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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" - -/* - * Tcp control block, one per tcp; fields: - */ -struct tcpcb { - struct tcpiphdr *seg_next; /* sequencing queue */ - struct tcpiphdr *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 */ - - struct tcpiphdr t_template; /* static skeletal packet for xmit */ - - struct socket *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 */ - uint32_t snd_wnd; /* send window */ -/* receive sequence variables */ - uint32_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) */ - uint32_t snd_cwnd; /* congestion-controlled window */ - uint32_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 */ - uint32_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; - uint32_t ts_recent; /* timestamp echo data */ - uint32_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) - -#endif diff --git a/slirp/tftp.c b/slirp/tftp.c deleted file mode 100644 index 2d7d5a3d1..000000000 --- a/slirp/tftp.c +++ /dev/null @@ -1,442 +0,0 @@ -/* - * 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 -#include "qemu-common.h" - -static inline int tftp_session_in_use(struct tftp_session *spt) -{ - return (spt->slirp != NULL); -} - -static inline void tftp_session_update(struct tftp_session *spt) -{ - spt->timestamp = curtime; -} - -static void tftp_session_terminate(struct tftp_session *spt) -{ - if (spt->f != NULL) { - fclose(spt->f); - spt->f = NULL; - } - g_free(spt->filename); - spt->slirp = NULL; -} - -static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp) -{ - struct tftp_session *spt; - int k; - - for (k = 0; k < TFTP_SESSIONS_MAX; k++) { - spt = &slirp->tftp_sessions[k]; - - if (!tftp_session_in_use(spt)) - goto found; - - /* sessions time out after 5 inactive seconds */ - if ((int)(curtime - spt->timestamp) > 5000) { - tftp_session_terminate(spt); - goto found; - } - } - - return -1; - - found: - memset(spt, 0, sizeof(*spt)); - memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip)); - spt->f = NULL; - spt->client_port = tp->udp.uh_sport; - spt->slirp = slirp; - - tftp_session_update(spt); - - return k; -} - -static int tftp_session_find(Slirp *slirp, struct tftp_t *tp) -{ - struct tftp_session *spt; - int k; - - for (k = 0; k < TFTP_SESSIONS_MAX; k++) { - spt = &slirp->tftp_sessions[k]; - - if (tftp_session_in_use(spt)) { - 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, uint32_t block_nr, - uint8_t *buf, int len) -{ - int bytes_read = 0; - - if (spt->f == NULL) { - spt->f = fopen(spt->filename, "rb"); - } - - if (spt->f == NULL) { - return -1; - } - - if (len) { - (void)fseek(spt->f, block_nr * 512, SEEK_SET); - - bytes_read = fread(buf, 1, len, spt->f); - } - - return bytes_read; -} - -static int tftp_send_oack(struct tftp_session *spt, - const char *keys[], uint32_t values[], int nb, - struct tftp_t *recv_tp) -{ - struct sockaddr_in saddr, daddr; - struct mbuf *m; - struct tftp_t *tp; - int i, n = 0; - - m = m_get(spt->slirp); - - if (!m) - return -1; - - memset(m->m_data, 0, m->m_size); - - m->m_data += IF_MAXLINKHDR; - tp = (struct tftp_t *)m->m_data; - m->m_data += sizeof(struct udpiphdr); - - tp->tp_op = htons(TFTP_OACK); - for (i = 0; i < nb; i++) { - n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", - keys[i]) + 1; - n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", - values[i]) + 1; - } - - 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 + n - - sizeof(struct ip) - sizeof(struct udphdr); - udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); - - return 0; -} - -static void tftp_send_error(struct tftp_session *spt, - uint16_t errorcode, const char *msg, - struct tftp_t *recv_tp) -{ - struct sockaddr_in saddr, daddr; - struct mbuf *m; - struct tftp_t *tp; - - m = m_get(spt->slirp); - - if (!m) { - goto out; - } - - memset(m->m_data, 0, m->m_size); - - m->m_data += IF_MAXLINKHDR; - tp = (struct tftp_t *)m->m_data; - m->m_data += sizeof(struct udpiphdr); - - tp->tp_op = htons(TFTP_ERROR); - tp->x.tp_error.tp_error_code = htons(errorcode); - pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg); - - 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); - -out: - tftp_session_terminate(spt); -} - -static void tftp_send_next_block(struct tftp_session *spt, - struct tftp_t *recv_tp) -{ - struct sockaddr_in saddr, daddr; - struct mbuf *m; - struct tftp_t *tp; - int nobytes; - - m = m_get(spt->slirp); - - if (!m) { - return; - } - - memset(m->m_data, 0, m->m_size); - - m->m_data += IF_MAXLINKHDR; - tp = (struct tftp_t *)m->m_data; - m->m_data += sizeof(struct udpiphdr); - - tp->tp_op = htons(TFTP_DATA); - tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff); - - 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, spt->block_nr, 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; - } - - 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); - } - - spt->block_nr++; -} - -static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen) -{ - struct tftp_session *spt; - int s, k; - size_t prefix_len; - char *req_fname; - const char *option_name[2]; - uint32_t option_value[2]; - int nb_options = 0; - - /* check if a session already exists and if so terminate it */ - s = tftp_session_find(slirp, tp); - if (s >= 0) { - tftp_session_terminate(&slirp->tftp_sessions[s]); - } - - s = tftp_session_allocate(slirp, tp); - - if (s < 0) { - return; - } - - spt = &slirp->tftp_sessions[s]; - - /* unspecified prefix means service disabled */ - if (!slirp->tftp_prefix) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - - /* skip header fields */ - k = 0; - pktlen -= offsetof(struct tftp_t, x.tp_buf); - - /* prepend tftp_prefix */ - prefix_len = strlen(slirp->tftp_prefix); - spt->filename = (char *)g_malloc(prefix_len + TFTP_FILENAME_MAX + 2); - memcpy(spt->filename, slirp->tftp_prefix, prefix_len); - spt->filename[prefix_len] = '/'; - - /* get name */ - req_fname = spt->filename + prefix_len + 1; - - while (1) { - if (k >= TFTP_FILENAME_MAX || k >= pktlen) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - req_fname[k] = tp->x.tp_buf[k]; - if (req_fname[k++] == '\0') { - break; - } - } - - /* check mode */ - if ((pktlen - k) < 6) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - - if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) { - tftp_send_error(spt, 4, "Unsupported transfer mode", tp); - return; - } - - k += 6; /* skipping octet */ - - /* do sanity checks on the filename */ - if (!strncmp(req_fname, "../", 3) || - req_fname[strlen(req_fname) - 1] == '/' || - strstr(req_fname, "/../")) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - - /* check if the file exists */ - if (tftp_read_data(spt, 0, NULL, 0) < 0) { - tftp_send_error(spt, 1, "File not found", tp); - return; - } - - if (tp->x.tp_buf[pktlen - 1] != 0) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - - while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) { - const char *key, *value; - - key = &tp->x.tp_buf[k]; - k += strlen(key) + 1; - - if (k >= pktlen) { - tftp_send_error(spt, 2, "Access violation", tp); - return; - } - - value = &tp->x.tp_buf[k]; - k += strlen(value) + 1; - - if (strcasecmp(key, "tsize") == 0) { - int tsize = atoi(value); - struct stat stat_p; - - if (tsize == 0) { - if (stat(spt->filename, &stat_p) == 0) - tsize = stat_p.st_size; - else { - tftp_send_error(spt, 1, "File not found", tp); - return; - } - } - - option_name[nb_options] = "tsize"; - option_value[nb_options] = tsize; - nb_options++; - } else if (strcasecmp(key, "blksize") == 0) { - int blksize = atoi(value); - - /* If blksize option is bigger than what we will - * emit, accept the option with our packet size. - * Otherwise, simply do as we didn't see the option. - */ - if (blksize >= 512) { - option_name[nb_options] = "blksize"; - option_value[nb_options] = 512; - nb_options++; - } - } - } - - if (nb_options > 0) { - assert(nb_options <= ARRAY_SIZE(option_name)); - tftp_send_oack(spt, option_name, option_value, nb_options, tp); - return; - } - - spt->block_nr = 0; - tftp_send_next_block(spt, tp); -} - -static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen) -{ - int s; - - s = tftp_session_find(slirp, tp); - - if (s < 0) { - return; - } - - tftp_send_next_block(&slirp->tftp_sessions[s], tp); -} - -static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen) -{ - int s; - - s = tftp_session_find(slirp, tp); - - if (s < 0) { - return; - } - - tftp_session_terminate(&slirp->tftp_sessions[s]); -} - -void tftp_input(struct mbuf *m) -{ - struct tftp_t *tp = (struct tftp_t *)m->m_data; - - switch(ntohs(tp->tp_op)) { - case TFTP_RRQ: - tftp_handle_rrq(m->slirp, tp, m->m_len); - break; - - case TFTP_ACK: - tftp_handle_ack(m->slirp, tp, m->m_len); - break; - - case TFTP_ERROR: - tftp_handle_error(m->slirp, tp, m->m_len); - break; - } -} diff --git a/slirp/tftp.h b/slirp/tftp.h deleted file mode 100644 index 8af289ee6..000000000 --- a/slirp/tftp.h +++ /dev/null @@ -1,49 +0,0 @@ -/* tftp defines */ -#ifndef SLIRP_TFTP_H -#define SLIRP_TFTP_H 1 - -#define TFTP_SESSIONS_MAX 20 - -#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_OACK 6 - -#define TFTP_FILENAME_MAX 512 - -struct tftp_t { - struct ip ip; - struct udphdr udp; - uint16_t tp_op; - union { - struct { - uint16_t tp_block_nr; - uint8_t tp_buf[512]; - } tp_data; - struct { - uint16_t tp_error_code; - uint8_t tp_msg[512]; - } tp_error; - char tp_buf[512 + 2]; - } x; -}; - -struct tftp_session { - Slirp *slirp; - char *filename; - FILE *f; - - struct in_addr client_ip; - uint16_t client_port; - uint32_t block_nr; - - int timestamp; -}; - -void tftp_input(struct mbuf *m); - -#endif diff --git a/slirp/udp.c b/slirp/udp.c deleted file mode 100644 index 72580b4c4..000000000 --- a/slirp/udp.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * 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 "ip_icmp.h" - -static uint8_t udp_tos(struct socket *so); - -void -udp_init(Slirp *slirp) -{ - slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb; - slirp->udp_last_so = &slirp->udb; -} - -void udp_cleanup(Slirp *slirp) -{ - while (slirp->udb.so_next != &slirp->udb) { - udp_detach(slirp->udb.so_next); - } -} - -/* m->m_data points at ip packet header - * m->m_len length ip packet - * ip->ip_len length data (IPDU) - */ -void -udp_input(register struct mbuf *m, int iphlen) -{ - Slirp *slirp = m->slirp; - register struct ip *ip; - register struct udphdr *uh; - int len; - struct ip save_ip; - struct socket *so; - - DEBUG_CALL("udp_input"); - DEBUG_ARG("m = %lx", (long)m); - DEBUG_ARG("iphlen = %d", iphlen); - - /* - * 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 mbuf *)0); - iphlen = sizeof(struct ip); - } - - /* - * Get IP and UDP header together in first mbuf. - */ - ip = mtod(m, struct ip *); - uh = (struct udphdr *)((caddr_t)ip + iphlen); - - /* - * Make mbuf data length reflect UDP length. - * If not enough data to reflect UDP length, drop. - */ - len = ntohs((uint16_t)uh->uh_ulen); - - if (ip->ip_len != len) { - if (len > ip->ip_len) { - 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 (uh->uh_sum) { - memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr)); - ((struct ipovly *)ip)->ih_x1 = 0; - ((struct ipovly *)ip)->ih_len = uh->uh_ulen; - if(cksum(m, len + sizeof(struct ip))) { - goto bad; - } - } - - /* - * handle DHCP/BOOTP - */ - if (ntohs(uh->uh_dport) == BOOTP_SERVER && - (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || - ip->ip_dst.s_addr == 0xffffffff)) { - bootp_input(m); - goto bad; - } - - /* - * handle TFTP - */ - if (ntohs(uh->uh_dport) == TFTP_SERVER && - ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { - tftp_input(m); - goto bad; - } - - if (slirp->restricted) { - goto bad; - } - - /* - * Locate pcb for datagram. - */ - so = slirp->udp_last_so; - if (so == &slirp->udb || so->so_lport != uh->uh_sport || - so->so_laddr.s_addr != ip->ip_src.s_addr) { - struct socket *tmp; - - for (tmp = slirp->udb.so_next; tmp != &slirp->udb; - tmp = tmp->so_next) { - if (tmp->so_lport == uh->uh_sport && - tmp->so_laddr.s_addr == ip->ip_src.s_addr) { - so = tmp; - break; - } - } - if (tmp == &slirp->udb) { - so = NULL; - } else { - slirp->udp_last_so = so; - } - } - - if (so == NULL) { - /* - * If there's no socket for this packet, - * create one - */ - so = socreate(slirp); - if (!so) { - goto bad; - } - if(udp_attach(so) == -1) { - DEBUG_MISC(" udp_attach errno = %d-%s\n", - errno,strerror(errno)); - sofree(so); - goto bad; - } - - /* - * Setup fields - */ - 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(sosendto(so,m) == -1) { - m->m_len += iphlen; - m->m_data -= iphlen; - *ip=save_ip; - DEBUG_MISC("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 mbuf packet */ - m->m_len += iphlen; - m->m_data -= iphlen; - *ip=save_ip; - so->so_m=m; /* ICMP backup */ - - return; -bad: - m_free(m); -} - -int udp_output2(struct socket *so, struct mbuf *m, - struct sockaddr_in *saddr, struct sockaddr_in *daddr, - int iptos) -{ - register 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 mbuf with extended UDP header - * and addresses and length put into network format. - */ - ui = mtod(m, struct udpiphdr *); - memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); - ui->ui_x1 = 0; - ui->ui_pr = IPPROTO_UDP; - ui->ui_len = htons(m->m_len - sizeof(struct ip)); - /* 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 ((ui->ui_sum = cksum(m, m->m_len)) == 0) - ui->ui_sum = 0xffff; - ((struct ip *)ui)->ip_len = m->m_len; - - ((struct ip *)ui)->ip_ttl = IPDEFTTL; - ((struct ip *)ui)->ip_tos = iptos; - - error = ip_output(so, m); - - return (error); -} - -int udp_output(struct socket *so, struct mbuf *m, - struct sockaddr_in *addr) - -{ - Slirp *slirp = so->slirp; - struct sockaddr_in saddr, daddr; - - saddr = *addr; - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; - - if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { - saddr.sin_addr = slirp->vhost_addr; - } else if (addr->sin_addr.s_addr == loopback_addr.s_addr || - so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { - saddr.sin_addr = so->so_faddr; - } - } - 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(struct socket *so) -{ - if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { - so->so_expire = curtime + SO_EXPIRE; - insque(so, &so->slirp->udb); - } - return(so->s); -} - -void -udp_detach(struct socket *so) -{ - closesocket(so->s); - sofree(so); -} - -static const struct tos_t udptos[] = { - {0, 53, IPTOS_LOWDELAY, 0}, /* DNS */ - {0, 0, 0, 0} -}; - -static uint8_t -udp_tos(struct socket *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; -} - -struct socket * -udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, - u_int lport, int flags) -{ - struct sockaddr_in addr; - struct socket *so; - socklen_t addrlen = sizeof(struct sockaddr_in); - - so = socreate(slirp); - if (!so) { - return NULL; - } - so->s = qemu_socket(AF_INET,SOCK_DGRAM,0); - so->so_expire = curtime + SO_EXPIRE; - insque(so, &slirp->udb); - - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = haddr; - addr.sin_port = hport; - - if (bind(so->s,(struct sockaddr *)&addr, addrlen) == SOCKET_ERROR) { - udp_detach(so); - return NULL; - } - (void)socket_set_fast_reuse(so->s); - - (void)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 = slirp->vhost_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_PERSISTENT_MASK; - so->so_state |= SS_ISFCONNECTED | flags; - - return so; -} diff --git a/slirp/udp.h b/slirp/udp.h deleted file mode 100644 index 3f90af620..000000000 --- a/slirp/udp.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 - -/* - * Udp protocol header. - * Per RFC 768, September, 1981. - */ -struct udphdr { - uint16_t uh_sport; /* source port */ - uint16_t uh_dport; /* destination port */ - int16_t uh_ulen; /* udp length */ - uint16_t uh_sum; /* udp checksum */ -}; - -/* - * UDP kernel structures and variables. - */ -struct udpiphdr { - struct ipovly ui_i; /* overlaid ip structure */ - struct udphdr ui_u; /* udp header */ -}; -#define ui_mbuf ui_i.ih_mbuf.mptr -#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 - -/* - * Names for UDP sysctl objects - */ -#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ -#define UDPCTL_MAXID 2 - -struct mbuf; - -void udp_init(Slirp *); -void udp_cleanup(Slirp *); -void udp_input(register struct mbuf *, int); -int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); -int udp_attach(struct socket *); -void udp_detach(struct socket *); -struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, - int); -int udp_output2(struct socket *so, struct mbuf *m, - struct sockaddr_in *saddr, struct sockaddr_in *daddr, - int iptos); -#endif diff --git a/slirp_glue/README b/slirp_glue/README deleted file mode 100644 index 3a4842300..000000000 --- a/slirp_glue/README +++ /dev/null @@ -1,16 +0,0 @@ -The files in and below this directory are the necessary glue and stubs -to allow the effectively unmodified qemu slirp code to be used in simh -as a network connection mechanism. - -Most of the include files here started from related files in the qemu -include directories and have been chopped up and tweaked as necessary -so that the slirp code can be used outside of qemu. - -Slirp depends on a small set of capabilities from glib (GArrays mostly). - -All of the other include files exist so that the references in the -slirp code don't have to be modified. - -- Mark Pizzolato - - diff --git a/slirp_glue/config-host.h b/slirp_glue/config-host.h deleted file mode 100644 index 9bb557ce4..000000000 --- a/slirp_glue/config-host.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef CONFIG_HOST_H -#define CONFIG_HOST_H - -#include -#include -#include -#ifdef _WIN32 -#include -#else -typedef int SOCKET; -#define SOCKET_ERROR (-1) -#endif - -#ifndef __cplusplus -typedef int bool; -#endif -#ifdef _MSC_VER -#include -#else -#include -#endif -#include -#define qemu_add_child_watch(pid) -int qemu_setsockopt (int s, int level, int optname, void *optval, int optlen); -int qemu_recv (int s, void *buf, size_t len, int flags); -#ifdef _MSC_VER -#define snprintf _snprintf -#ifndef strcasecmp -#define strcasecmp stricmp -#endif -#define inline -#else -#ifndef _WIN32 -#define CONFIG_IOVEC 1 -#endif -#endif -#define register_savevm(p1, p2, p3, p4, p5, p6, p7) -#define unregister_savevm(p1, p2, p3) -#define qemu_put_be16(p1, p2) -#define qemu_put_sbe16(p1, p2) -#define qemu_put_be32(p1, p2) -#define qemu_put_sbe32(p1, p2) -#define qemu_put_byte(p1, p2) -#define qemu_put_sbyte(p1, p2) -#define qemu_put_buffer(p1, p2, p3) - -#define qemu_get_be16(p1) 0 -#define qemu_get_sbe16(p1) 0 -#define qemu_get_be32(p1) 0 -#define qemu_get_sbe32(p1) 0 -#define qemu_get_byte(p1) 0 -#define qemu_get_sbyte(p1) 0 -#define qemu_get_buffer(p1, p2, p3) -#define error_report(...) fprintf(stderr, __VA_ARGS__) - -#endif diff --git a/slirp_glue/glib.h b/slirp_glue/glib.h deleted file mode 100644 index 714681aa8..000000000 --- a/slirp_glue/glib.h +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef GLIB_H -#define GLIB_H - -#include -#if defined(_WIN32) -#include -#endif - -typedef char gchar; -typedef unsigned int guint; -typedef unsigned short gushort; -typedef void* gpointer; -typedef unsigned long gsize; -typedef const void *gconstpointer; -typedef int gint; -typedef gint gboolean; -typedef struct _GSource {int dummy;} GSource; -typedef struct GPollFD { -#if defined(_WIN32) - SOCKET fd; -#else - gint fd; -#endif - gushort events; - gushort revents; -} GPollFD; -typedef struct _GArray { - gchar *data; - guint len; -} GArray; - -gpointer g_malloc (gsize n_bytes); -gpointer g_malloc0 (gsize n_bytes); -gpointer g_realloc (gpointer mem, gsize n_bytes); -void g_free (gpointer mem); -gchar *g_strdup (const gchar *str); - -typedef enum { - /* Flags */ - G_LOG_FLAG_RECURSION = 1 << 0, - G_LOG_FLAG_FATAL = 1 << 1, - /* Levels */ - G_LOG_LEVEL_ERROR = 1 << 2, - G_LOG_LEVEL_CRITICAL = 1 << 3, - G_LOG_LEVEL_WARNING = 1 << 4, - G_LOG_LEVEL_MESSAGE = 1 << 5, - G_LOG_LEVEL_INFO = 1 << 6, - G_LOG_LEVEL_DEBUG = 1 << 7, - G_LOG_LEVEL_MASK = ~(G_LOG_FLAG_RECURSION | G_LOG_FLAG_FATAL) - } GLogLevelFlags; - -#define GLIB_SYSDEF_POLLIN =1 -#define GLIB_SYSDEF_POLLOUT =4 -#define GLIB_SYSDEF_POLLPRI =2 -#define GLIB_SYSDEF_POLLHUP =16 -#define GLIB_SYSDEF_POLLERR =8 -#define GLIB_SYSDEF_POLLNVAL =32 - -typedef enum { - G_IO_IN GLIB_SYSDEF_POLLIN, /* There is data to read. */ - G_IO_OUT GLIB_SYSDEF_POLLOUT, /* Data can be written (without blocking). */ - G_IO_PRI GLIB_SYSDEF_POLLPRI, /* There is urgent data to read. */ - G_IO_ERR GLIB_SYSDEF_POLLERR, /* Error condition. */ - G_IO_HUP GLIB_SYSDEF_POLLHUP, /* Hung up (the connection has been broken, usually for pipes and sockets). */ - G_IO_NVAL GLIB_SYSDEF_POLLNVAL /* Invalid request. The file descriptor is not open. */ - } GIOCondition; -void g_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...); -#if !defined(G_LOG_DOMAIN) -#define G_LOG_DOMAIN ((gchar *)NULL) -#endif -#define g_warning(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, __VA_ARGS__) - -#define g_new(struct_type, n_structs) g_malloc (sizeof(struct_type) * n_structs) - - -#define g_array_append_val(array, data) g_array_append_vals (array, &data, 1) -#define g_array_new(zero_terminated, clear, element_size) g_array_sized_new(zero_terminated, clear, element_size, 0) - -GArray * -g_array_sized_new (gboolean zero_terminated, - gboolean clear, - guint element_size, - guint reserved_size); -gchar * -g_array_free (GArray *array, - gboolean free_segment); - -#define g_array_index(array, type, index) (((type *)(void *)((array)->data)))[index] - -GArray * -g_array_set_size (GArray *array, - guint length); -GArray * -g_array_append_vals (GArray *array, - gconstpointer data, - guint len); -guint -g_array_get_element_size (GArray *array); - - -#endif diff --git a/slirp_glue/glib_qemu_stubs.c b/slirp_glue/glib_qemu_stubs.c deleted file mode 100644 index 99f062455..000000000 --- a/slirp_glue/glib_qemu_stubs.c +++ /dev/null @@ -1,346 +0,0 @@ -/* glib_qemu_stubs.c: - ------------------------------------------------------------------------------ - Copyright (c) 2015, Mark Pizzolato - - 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 AUTHOR 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. - - Except as contained in this notice, the name of the author shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from the author. - - ------------------------------------------------------------------------------ - - This module provides the minimal aspects of glib and qemu which are referenced - by the current qemu SLiRP code and are needed to get SLiRP functionality for - the simh network code. - -*/ - -#include -#include -#include -#include -#ifdef _WIN32 -#include -#include -#endif -#include "qemu/compiler.h" -#include "qemu/typedefs.h" -#include -#include -#include "glib.h" - -gpointer -g_malloc (gsize n_bytes) -{ -gpointer ret = (gpointer)malloc (n_bytes); - -if (!ret) - exit (errno); -return ret; -} - -gpointer -g_malloc0 (gsize n_bytes) -{ -gpointer ret = (gpointer)calloc (1, n_bytes); - -if (!ret) - exit (errno); -return ret; -} - -gpointer -g_realloc (gpointer mem, gsize n_bytes) -{ -gpointer ret = (gpointer)realloc (mem, n_bytes); - -if (!ret) - exit (errno); -return ret; -} - -void -g_free (gpointer mem) -{ -free (mem); -} - -gchar * -g_strdup (const gchar *str) -{ -gchar *nstr = NULL; - -if (str) { - nstr = (gchar *)malloc (strlen(str)+1); - if (!nstr) - exit (errno); - strcpy (nstr, str); - } -return nstr; -} - -void pstrcpy(char *buf, int buf_size, const char *str) -{ - int c; - char *q = buf; - - if (buf_size <= 0) - return; - - for(;;) { - c = *str++; - if (c == 0 || q >= buf + buf_size - 1) - break; - *q++ = c; - } - *q = '\0'; -} - -int qemu_socket(int domain, int type, int protocol) -{ -return socket (domain, type, protocol); -} - -int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) -{ -return accept (s, addr, addrlen); -} - -int qemu_setsockopt (int s, int level, int optname, void *optval, int optlen) -{ -return setsockopt ((SOCKET)s, level, optname, (char *)optval, optlen); -} - -int qemu_recv (int s, void *buf, size_t len, int flags) -{ -return recv ((SOCKET)s, (char *)buf, len, flags); -} - -int socket_set_nodelay(int fd) -{ - int v = 1; - return setsockopt((SOCKET)fd, IPPROTO_TCP, TCP_NODELAY, (char *)&v, sizeof(v)); -} - -#ifdef _WIN32 - -void qemu_set_nonblock(int fd) -{ -unsigned long non_block = 1; - - (void)ioctlsocket ((SOCKET)fd, FIONBIO, &non_block); /* set nonblocking */ -} -#else -#include -void qemu_set_nonblock(int fd) -{ - int f; - - f = fcntl(fd, F_GETFL); - if (f != -1) - (void)fcntl(fd, F_SETFL, f | O_NONBLOCK); -} -#endif - -int socket_set_fast_reuse(int fd) -{ - int val = 1, ret; - - ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - (const char *)&val, sizeof(val)); - return ret; -} - -#if defined(__cplusplus) -extern "C" { -#endif -#include -#ifdef _WIN32 -int64_t qemu_clock_get_ns(int type) -{ -uint64_t now, unixbase; - -unixbase = 116444736; -unixbase *= 1000000000; -GetSystemTimeAsFileTime((FILETIME*)&now); -now -= unixbase; -return now*100; -} - -#else - -#if !defined(CLOCK_REALTIME) && !defined(__hpux) -#define CLOCK_REALTIME 1 -int clock_gettime(int clk_id, struct timespec *tp); -#endif - -int64_t qemu_clock_get_ns(int type) -{ - struct timespec tv; - - clock_gettime(CLOCK_REALTIME, &tv); - return tv.tv_sec * 1000000000LL + tv.tv_nsec; -} -#endif -#if defined(__cplusplus) -} -#endif - -void monitor_printf(Monitor *mon, const char *fmt, ...) -{ -va_list arglist; - - va_start (arglist, fmt); - vfprintf ((FILE *)mon, fmt, arglist); - va_end (arglist); -} - -void g_log (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *format, - ...) -{ -va_list arglist; - - fprintf (stderr, "%s(%X): ", log_domain ? log_domain : "", log_level); - va_start (arglist, format); - vfprintf (stderr, format, arglist); - va_end (arglist); -} - -int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) -{ -fprintf (stderr, "qemu_chr_fe_write() called\n"); -return 0; -} - -void qemu_notify_event(void) -{ -} - -#if defined(_WIN32) -int -inet_aton(const char *arg, struct in_addr *addr) -{ -(*addr).s_addr = inet_addr (arg); -return (*addr).s_addr != INADDR_BROADCAST; -} - -#endif - -/* glib GArray functionality is needed */ - -typedef struct { - gchar *data; - guint len; - guint _element_size; /* element size */ - guint _size; /* allocated element count size */ - gboolean _zero_terminated; - gboolean _clear; -} GArrayInternal; - -GArray * -g_array_sized_new (gboolean zero_terminated, - gboolean clear, - guint element_size, - guint reserved_size) -{ -GArrayInternal *ar = (GArrayInternal *)g_malloc (sizeof (*ar)); - -ar->_zero_terminated = zero_terminated ? 1 : 0; -ar->_clear = clear ? 1 : 0; -ar->_element_size = element_size; -ar->_size = reserved_size; -ar->len = 0; -ar->data = clear ? (gchar *)g_malloc0 (element_size*(reserved_size + zero_terminated)) : - (gchar *)g_malloc (element_size*(reserved_size + zero_terminated)); -if (ar->_zero_terminated && !ar->_clear) - memset (ar->data + (ar->len * ar->_element_size), 0, ar->_element_size); -return (GArray *)ar; -} - -gchar * -g_array_free (GArray *array, - gboolean free_segment) -{ -gchar *result = ((array == NULL) || free_segment) ? NULL : array->data; - -if (array != NULL) { - if (free_segment) - free (array->data); - free (array); - } -return result; -} - -GArray * -g_array_set_size (GArray *array, - guint length) -{ -GArrayInternal *ar = (GArrayInternal *)array; - -if (length > ar->_size) { - ar->data = (gchar *)g_realloc (ar->data, (length + ar->_zero_terminated) * ar->_element_size); - if (ar->_clear) - memset (ar->data + (ar->len * ar->_element_size), 0, (length + ar->_zero_terminated - ar->len) * ar->_element_size); - ar->_size = length; - } -ar->len = length; -if (ar->_zero_terminated) - memset (ar->data + (ar->len * ar->_element_size), 0, ar->_element_size); -return array; -} - -GArray * -g_array_append_vals (GArray *array, - gconstpointer data, - guint len) -{ -GArrayInternal *ar = (GArrayInternal *)array; - -if ((ar->len + len) > ar->_size) { - ar->data = (gchar *)g_realloc (ar->data, (ar->len + len + ar->_zero_terminated) * ar->_element_size); - ar->_size = ar->len + len; - } -memcpy (ar->data + (ar->len * ar->_element_size), data, len * ar->_element_size); -ar->len += len; -if (ar->_zero_terminated) - memset (ar->data + (ar->len * ar->_element_size), 0, ar->_element_size); -return array; -} - -guint -g_array_get_element_size (GArray *array) -{ -GArrayInternal *ar = (GArrayInternal *)array; - -return ar->_element_size; -} - -#if defined(_WIN32) -char *socket_strerror(int errnum) -{ -static char buf[512]; - -if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)buf, sizeof(buf), NULL)) - sprintf(buf, "Error Code: %d", errno); -return buf; -} -#endif diff --git a/slirp_glue/qemu/compiler.h b/slirp_glue/qemu/compiler.h deleted file mode 100644 index a8a2cde00..000000000 --- a/slirp_glue/qemu/compiler.h +++ /dev/null @@ -1,64 +0,0 @@ -/* public domain */ - -#ifndef COMPILER_H -#define COMPILER_H - -#include "config-host.h" - -/*---------------------------------------------------------------------------- -| The macro QEMU_GNUC_PREREQ tests for minimum version of the GNU C compiler. -| The code is a copy of SOFTFLOAT_GNUC_PREREQ, see softfloat-macros.h. -*----------------------------------------------------------------------------*/ -#if defined(__GNUC__) && defined(__GNUC_MINOR__) -# define QEMU_GNUC_PREREQ(maj, min) \ - ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) -#else -# define QEMU_GNUC_PREREQ(maj, min) 0 -#endif - -#ifndef container_of -#define container_of(ptr, type, member) \ - ((type *) ((char *)(ptr) - offsetof(type, member))) -#endif - -#ifndef always_inline -#if !((__GNUC__ < 3) || defined(__APPLE__)) -#ifdef __OPTIMIZE__ -#undef inline -#define inline __attribute__ (( always_inline )) __inline__ -#endif -#endif -#else -#undef inline -#define inline always_inline -#endif -#ifdef _MSC_VER -#undef inline -#define inline __inline -#endif - -#define register - -#if defined __GNUC__ -# if !QEMU_GNUC_PREREQ(4, 4) - /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ -# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) -# else - /* Use gnu_printf when supported (qemu uses standard format strings). */ -# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) -# if defined(_WIN32) - /* Map __printf__ to __gnu_printf__ because we want standard format strings - * even when MinGW or GLib include files use __printf__. */ -# define __printf__ __gnu_printf__ -# endif -# endif -#else -#define GCC_FMT_ATTR(n, m) -#endif - -#if defined (__clang__) - #pragma clang diagnostic ignored "-Wunknown-pragmas" - #pragma clang diagnostic ignored "-Waddress-of-packed-member" -#endif - -#endif /* COMPILER_H */ diff --git a/slirp_glue/qemu/error-report.h b/slirp_glue/qemu/error-report.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/slirp_glue/qemu/hw/hw.h b/slirp_glue/qemu/hw/hw.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/slirp_glue/qemu/main-loop.h b/slirp_glue/qemu/main-loop.h deleted file mode 100644 index 3cce36943..000000000 --- a/slirp_glue/qemu/main-loop.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * QEMU System Emulator - * - * Copyright (c) 2003-2008 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. - */ - -#ifndef QEMU_MAIN_LOOP_H -#define QEMU_MAIN_LOOP_H 1 - - -/** - * qemu_notify_event: Force processing of pending events. - * - * Similar to signaling a condition variable, qemu_notify_event forces - * main_loop_wait to look at pending events and exit. The caller of - * main_loop_wait will usually call it again very soon, so qemu_notify_event - * also has the side effect of recalculating the sets of file descriptors - * that the main loop waits for. - * - * Calling qemu_notify_event is rarely necessary, because main loop - * services (bottom halves and timers) call it themselves. - */ -void qemu_notify_event(void); - -#endif diff --git a/slirp_glue/qemu/monitor/monitor.h b/slirp_glue/qemu/monitor/monitor.h deleted file mode 100644 index e088c1626..000000000 --- a/slirp_glue/qemu/monitor/monitor.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef MONITOR_H -#define MONITOR_H - -#include "qemu-common.h" - -void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); - -#endif /* !MONITOR_H */ diff --git a/slirp_glue/qemu/osdep.h b/slirp_glue/qemu/osdep.h deleted file mode 100644 index c3d333941..000000000 --- a/slirp_glue/qemu/osdep.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * OS includes and handling of OS dependencies - * - * This header exists to pull in some common system headers that - * most code in QEMU will want, and to fix up some possible issues with - * it (missing defines, Windows weirdness, and so on). - * - * To avoid getting into possible circular include dependencies, this - * file should not include any other QEMU headers, with the exceptions - * of config-host.h, compiler.h, os-posix.h and os-win32.h, all of which - * are doing a similar job to this file and are under similar constraints. - * - * This header also contains prototypes for functions defined in - * os-*.c and util/oslib-*.c; those would probably be better split - * out into separate header files. - * - * In an ideal world this header would contain only: - * (1) things which everybody needs - * (2) things without which code would work on most platforms but - * fail to compile or misbehave on a minority of host OSes - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ -#ifndef QEMU_OSDEP_H -#define QEMU_OSDEP_H - -#include "config-host.h" -#include "qemu/compiler.h" -#include -#include -#ifdef _MSC_VER -#include -#include -#include -#else -#include -#include -#include -#endif -#include -#include -#include -#include -#ifndef _MSC_VER -#include -#endif -#include -/* Put unistd.h before time.h as that triggers localtime_r/gmtime_r - * function availability on recentish Mingw-w64 platforms. */ -#ifdef HAVE_UNISTD_H -# include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __OpenBSD__ -#include -#endif - -#ifndef _WIN32 -#include -#else -#define WIFEXITED(x) 1 -#define WEXITSTATUS(x) (x) -#endif - -#ifdef _WIN32 -#include "sysemu/os-win32.h" -#endif - -#ifdef CONFIG_POSIX -#include "sysemu/os-posix.h" -#endif - -#if defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10 -/* [u]int_fast*_t not in */ -typedef unsigned char uint_fast8_t; -typedef unsigned int uint_fast16_t; -typedef signed int int_fast16_t; -#endif - -#ifndef O_LARGEFILE -#define O_LARGEFILE 0 -#endif -#ifndef O_BINARY -#define O_BINARY 0 -#endif -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif -#ifndef ENOMEDIUM -#define ENOMEDIUM ENODEV -#endif -#if !defined(ENOTSUP) -#define ENOTSUP 4096 -#endif -#if !defined(ECANCELED) -#define ECANCELED 4097 -#endif -#if !defined(EMEDIUMTYPE) -#define EMEDIUMTYPE 4098 -#endif -#ifndef TIME_MAX -#define TIME_MAX LONG_MAX -#endif - -#ifndef MIN -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif -#ifndef MAX -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - -/* Minimum function that returns zero only iff both values are zero. - * Intended for use with unsigned values only. */ -#ifndef MIN_NON_ZERO -#define MIN_NON_ZERO(a, b) (((a) != 0 && (a) < (b)) ? (a) : (b)) -#endif - -#ifndef ROUND_UP -#define ROUND_UP(n,d) (((n) + (d) - 1) & -(d)) -#endif - -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) -#endif - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif - -#ifndef CONFIG_IOVEC -struct iovec { - void *iov_base; - int iov_len; -}; -/* - * Use the same value as Linux for now. - */ -#define IOV_MAX 1024 - -ssize_t readv(int fd, const struct iovec *iov, int iov_cnt); -ssize_t writev(int fd, const struct iovec *iov, int iov_cnt); -#else -#include -#endif - -#ifdef _WIN32 -static inline void qemu_timersub(const struct timeval *val1, - const struct timeval *val2, - struct timeval *res) -{ - res->tv_sec = val1->tv_sec - val2->tv_sec; - if (val1->tv_usec < val2->tv_usec) { - res->tv_sec--; - res->tv_usec = val1->tv_usec - val2->tv_usec + 1000 * 1000; - } else { - res->tv_usec = val1->tv_usec - val2->tv_usec; - } -} -#else -#define qemu_timersub timersub -#endif - -#endif diff --git a/slirp_glue/qemu/qemu-common.h b/slirp_glue/qemu/qemu-common.h deleted file mode 100644 index b4158e994..000000000 --- a/slirp_glue/qemu/qemu-common.h +++ /dev/null @@ -1,54 +0,0 @@ - -/* Common header file that is included by all of QEMU. - * - * This file is supposed to be included only by .c files. No header file should - * depend on qemu-common.h, as this would easily lead to circular header - * dependencies. - * - * If a header file uses a definition from qemu-common.h, that definition - * must be moved to a separate header file, and the header that uses it - * must include that header. - */ -#ifndef QEMU_COMMON_H -#define QEMU_COMMON_H - -#include "qemu/osdep.h" -#include "qemu/typedefs.h" - -#include "glib.h" -#include "config-host.h" - -#if defined(O_BINARY) /* O_BINARY isn't used in slirp */ -#undef O_BINARY /* Avoid potential redefinition elsewhere */ -#endif - -/* HOST_LONG_BITS is the size of a native pointer in bits. */ -#if UINTPTR_MAX == UINT32_MAX -# define HOST_LONG_BITS 32 -#elif UINTPTR_MAX == UINT64_MAX -# define HOST_LONG_BITS 64 -#else -# error Unknown pointer size -#endif - -/* util/cutils.c */ -/** - * pstrcpy: - * @buf: buffer to copy string into - * @buf_size: size of @buf in bytes - * @str: string to copy - * - * Copy @str into @buf, including the trailing NUL, but do not - * write more than @buf_size bytes. The resulting buffer is - * always NUL terminated (even if the source string was too long). - * If @buf_size is zero or negative then no bytes are copied. - * - * This function is similar to strncpy(), but avoids two of that - * function's problems: - * * if @str fits in the buffer, pstrcpy() does not zero-fill the - * remaining space at the end of @buf - * * if @str is too long, pstrcpy() will copy the first @buf_size-1 - * bytes and then add a NUL - */ -void pstrcpy(char *buf, int buf_size, const char *str); -#endif diff --git a/slirp_glue/qemu/queue.h b/slirp_glue/qemu/queue.h deleted file mode 100644 index 3126c9e6d..000000000 --- a/slirp_glue/qemu/queue.h +++ /dev/null @@ -1,431 +0,0 @@ -/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ - -/* - * QEMU version: Copy from netbsd, removed debug code, removed some of - * the implementations. Left in singly-linked lists, lists, simple - * queues, and tail queues. - */ - -/* - * Copyright (c) 1991, 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. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef QEMU_SYS_QUEUE_H_ -#define QEMU_SYS_QUEUE_H_ - -/* - * This file defines four types of data structures: singly-linked lists, - * lists, simple queues, and tail queues. - * - * A singly-linked list is headed by a single forward pointer. The - * elements are singly linked for minimum space and pointer manipulation - * overhead at the expense of O(n) removal for arbitrary elements. New - * elements can be added to the list after an existing element or at the - * head of the list. Elements being removed from the head of the list - * should use the explicit macro for this purpose for optimum - * efficiency. A singly-linked list may only be traversed in the forward - * direction. Singly-linked lists are ideal for applications with large - * datasets and few or no removals or for implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -/* - * List definitions. - */ -#define QLIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define QLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define QLIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List functions. - */ -#define QLIST_INIT(head) do { \ - (head)->lh_first = NULL; \ -} while (/*CONSTCOND*/0) - -#define QLIST_SWAP(dstlist, srclist, field) do { \ - void *tmplist; \ - tmplist = (srclist)->lh_first; \ - (srclist)->lh_first = (dstlist)->lh_first; \ - if ((srclist)->lh_first != NULL) { \ - (srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \ - } \ - (dstlist)->lh_first = tmplist; \ - if ((dstlist)->lh_first != NULL) { \ - (dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \ - } \ -} while (/*CONSTCOND*/0) - -#define QLIST_FIX_HEAD_PTR(head, field) do { \ - if ((head)->lh_first != NULL) { \ - (head)->lh_first->field.le_prev = &(head)->lh_first; \ - } \ -} while (/*CONSTCOND*/0) - -#define QLIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define QLIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (/*CONSTCOND*/0) - -#define QLIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define QLIST_FOREACH(var, head, field) \ - for ((var) = ((head)->lh_first); \ - (var); \ - (var) = ((var)->field.le_next)) - -#define QLIST_FOREACH_SAFE(var, head, field, next_var) \ - for ((var) = ((head)->lh_first); \ - (var) && ((next_var) = ((var)->field.le_next), 1); \ - (var) = (next_var)) - -/* - * List access methods. - */ -#define QLIST_EMPTY(head) ((head)->lh_first == NULL) -#define QLIST_FIRST(head) ((head)->lh_first) -#define QLIST_NEXT(elm, field) ((elm)->field.le_next) - - -/* - * Singly-linked List definitions. - */ -#define QSLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define QSLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define QSLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List functions. - */ -#define QSLIST_INIT(head) do { \ - (head)->slh_first = NULL; \ -} while (/*CONSTCOND*/0) - -#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define QSLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define QSLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (/*CONSTCOND*/0) - -#define QSLIST_REMOVE_AFTER(slistelm, field) do { \ - (slistelm)->field.sle_next = \ - QSLIST_NEXT(QSLIST_NEXT((slistelm), field), field); \ -} while (/*CONSTCOND*/0) - -#define QSLIST_FOREACH(var, head, field) \ - for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) - -#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = QSLIST_FIRST((head)); \ - (var) && ((tvar) = QSLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -/* - * Singly-linked List access methods. - */ -#define QSLIST_EMPTY(head) ((head)->slh_first == NULL) -#define QSLIST_FIRST(head) ((head)->slh_first) -#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next) - - -/* - * Simple queue definitions. - */ -#define QSIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define QSIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define QSIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue functions. - */ -#define QSIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL)\ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ - QSIMPLEQ_INIT(removed); \ - if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \ - if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \ - (head)->sqh_last = &(head)->sqh_first; \ - } \ - (removed)->sqh_last = &(elm)->field.sqe_next; \ - (elm)->field.sqe_next = NULL; \ - } \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \ - if ((head)->sqh_first == (elm)) { \ - QSIMPLEQ_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->sqh_first; \ - while (curelm->field.sqe_next != (elm)) \ - curelm = curelm->field.sqe_next; \ - if ((curelm->field.sqe_next = \ - curelm->field.sqe_next->field.sqe_next) == NULL) \ - (head)->sqh_last = &(curelm)->field.sqe_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_FOREACH(var, head, field) \ - for ((var) = ((head)->sqh_first); \ - (var); \ - (var) = ((var)->field.sqe_next)) - -#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \ - for ((var) = ((head)->sqh_first); \ - (var) && ((next = ((var)->field.sqe_next)), 1); \ - (var) = (next)) - -#define QSIMPLEQ_CONCAT(head1, head2) do { \ - if (!QSIMPLEQ_EMPTY((head2))) { \ - *(head1)->sqh_last = (head2)->sqh_first; \ - (head1)->sqh_last = (head2)->sqh_last; \ - QSIMPLEQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -#define QSIMPLEQ_LAST(head, type, field) \ - (QSIMPLEQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->sqh_last) - offsetof(struct type, field)))) - -/* - * Simple queue access methods. - */ -#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) -#define QSIMPLEQ_FIRST(head) ((head)->sqh_first) -#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - - -/* - * Tail queue definitions. - */ -#define Q_TAILQ_HEAD(name, type, qual) \ -struct name { \ - qual type *tqh_first; /* first element */ \ - qual type *qual *tqh_last; /* addr of last next element */ \ -} -#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,) - -#define QTAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define Q_TAILQ_ENTRY(type, qual) \ -struct { \ - qual type *tqe_next; /* next element */ \ - qual type *qual *tqe_prev; /* address of previous next element */\ -} -#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,) - -/* - * Tail queue functions. - */ -#define QTAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define QTAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->tqh_first); \ - (var); \ - (var) = ((var)->field.tqe_next)) - -#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \ - for ((var) = ((head)->tqh_first); \ - (var) && ((next_var) = ((var)->field.tqe_next), 1); \ - (var) = (next_var)) - -#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ - (var); \ - (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) - -/* - * Tail queue access methods. - */ -#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) -#define QTAILQ_FIRST(head) ((head)->tqh_first) -#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define QTAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -#define QTAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - -#endif /* !QEMU_SYS_QUEUE_H_ */ diff --git a/slirp_glue/qemu/sockets.h b/slirp_glue/qemu/sockets.h deleted file mode 100644 index 14ad2989f..000000000 --- a/slirp_glue/qemu/sockets.h +++ /dev/null @@ -1,52 +0,0 @@ -/* headers to use the BSD sockets */ -#ifndef QEMU_SOCKET_H -#define QEMU_SOCKET_H - -#ifdef _WIN32 -#include -#include -#include - -#define socket_error() WSAGetLastError() - -extern char *socket_strerror(int errcode); -#define strerror socket_strerror - -int inet_aton(const char *cp, struct in_addr *ia); - -#else - -#include -#include -#include -#include -#include -#include -#include - -#define socket_error() errno - -#endif /* !_WIN32 */ - -/* misc helpers */ -int qemu_socket(int domain, int type, int protocol); -int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); -int socket_set_cork(int fd, int v); -int socket_set_nodelay(int fd); -void qemu_set_block(int fd); -void qemu_set_nonblock(int fd); -int socket_set_fast_reuse(int fd); - -#ifdef _WIN32 -/* MinGW needs type casts for the 'buf' and 'optval' arguments. */ -#define qemu_sendto(sockfd, buf, len, flags, destaddr, addrlen) \ - sendto(sockfd, (const void *)buf, len, flags, destaddr, addrlen) - -/* Windows has different names for the same constants with the same values */ -#define SHUT_RD 0 -#define SHUT_WR 1 -#define SHUT_RDWR 2 -#endif - - -#endif /* QEMU_SOCKET_H */ diff --git a/slirp_glue/qemu/sysemu/char.h b/slirp_glue/qemu/sysemu/char.h deleted file mode 100644 index 339a254fd..000000000 --- a/slirp_glue/qemu/sysemu/char.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef QEMU_CHAR_H -#define QEMU_CHAR_H - -#include "qemu-common.h" - -/** - * @qemu_chr_fe_write: - * - * Write data to a character backend from the front end. This function - * will send data from the front end to the back end. This function - * is thread-safe. - * - * @buf the data - * @len the number of bytes to send - * - * Returns: the number of bytes consumed - */ -int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); - - -#endif diff --git a/slirp_glue/qemu/sysemu/os-win32.h b/slirp_glue/qemu/sysemu/os-win32.h deleted file mode 100644 index fff060a9d..000000000 --- a/slirp_glue/qemu/sysemu/os-win32.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * win32 specific declarations - * - * Copyright (c) 2003-2008 Fabrice Bellard - * Copyright (c) 2010 Jes Sorensen - * - * 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. - */ - -#ifndef QEMU_OS_WIN32_H -#define QEMU_OS_WIN32_H - -#include -#include - -#if defined(_WIN64) -/* MinGW-w64 has a 32 bit off_t, but we want 64 bit off_t. */ -# define off_t off64_t - -/* MinGW-w64 stdio.h defines SYS_OPEN. Allow a redefinition in arm-semi.c. */ -# undef SYS_OPEN -#endif - -/* Workaround for older versions of MinGW. */ -#ifndef ECONNREFUSED -# define ECONNREFUSED WSAECONNREFUSED -#endif -#ifndef EINPROGRESS -# define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef EHOSTUNREACH -# define EHOSTUNREACH WSAEHOSTUNREACH -#endif -#ifndef EINTR -# define EINTR WSAEINTR -#endif -#ifndef EINPROGRESS -# define EINPROGRESS WSAEINPROGRESS -#endif -#ifndef ENETUNREACH -# define ENETUNREACH WSAENETUNREACH -#endif -#ifndef ENOTCONN -# define ENOTCONN WSAENOTCONN -#endif -#ifndef EWOULDBLOCK -# define EWOULDBLOCK WSAEWOULDBLOCK -#endif - -#if defined(_WIN64) -/* On w64, setjmp is implemented by _setjmp which needs a second parameter. - * If this parameter is NULL, longjump does no stack unwinding. - * That is what we need for QEMU. Passing the value of register rsp (default) - * lets longjmp try a stack unwinding which will crash with generated code. */ -# undef setjmp -# define setjmp(env) _setjmp(env, NULL) -#endif -/* QEMU uses sigsetjmp()/siglongjmp() as the portable way to specify - * "longjmp and don't touch the signal masks". Since we know that the - * savemask parameter will always be zero we can safely define these - * in terms of setjmp/longjmp on Win32. - */ -#define sigjmp_buf jmp_buf -#define sigsetjmp(env, savemask) setjmp(env) -#define siglongjmp(env, val) longjmp(env, val) - -/* Missing POSIX functions. Don't use MinGW-w64 macros. */ -#ifndef CONFIG_LOCALTIME_R -#undef gmtime_r -struct tm *gmtime_r(const time_t *timep, struct tm *result); -#undef localtime_r -struct tm *localtime_r(const time_t *timep, struct tm *result); -#endif /* CONFIG_LOCALTIME_R */ - - -static inline void os_setup_signal_handling(void) {} -static inline void os_daemonize(void) {} -static inline void os_setup_post(void) {} -void os_set_line_buffering(void); -static inline void os_set_proc_name(const char *dummy) {} - -size_t getpagesize(void); - -#if !defined(EPROTONOSUPPORT) -# define EPROTONOSUPPORT EINVAL -#endif - -int setenv(const char *name, const char *value, int overwrite); - -typedef struct { - long tv_sec; - long tv_usec; -} qemu_timeval; -int qemu_gettimeofday(qemu_timeval *tp); - -#endif diff --git a/slirp_glue/qemu/timer.h b/slirp_glue/qemu/timer.h deleted file mode 100644 index ba7918b2e..000000000 --- a/slirp_glue/qemu/timer.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef QEMU_TIMER_H -#define QEMU_TIMER_H - -#include "qemu/typedefs.h" -#include "qemu-common.h" - -#define NANOSECONDS_PER_SECOND 1000000000LL - -/* timers */ - -#define SCALE_MS 1000000 -#define SCALE_US 1000 -#define SCALE_NS 1 - -/** - * QEMUClockType: - * - * The following clock types are available: - * - * @QEMU_CLOCK_REALTIME: Real time clock - * - * The real time clock should be used only for stuff which does not - * change the virtual machine state, as it is run even if the virtual - * machine is stopped. The real time clock has a frequency of 1000 - * Hz. - * - * @QEMU_CLOCK_VIRTUAL: virtual clock - * - * The virtual clock is only run during the emulation. It is stopped - * when the virtual machine is stopped. Virtual timers use a high - * precision clock, usually cpu cycles (use ticks_per_sec). - * - * @QEMU_CLOCK_HOST: host clock - * - * The host clock should be use for device models that emulate accurate - * real time sources. It will continue to run when the virtual machine - * is suspended, and it will reflect system time changes the host may - * undergo (e.g. due to NTP). The host clock has the same precision as - * the virtual clock. - * - * @QEMU_CLOCK_VIRTUAL_RT: realtime clock used for icount warp - * - * Outside icount mode, this clock is the same as @QEMU_CLOCK_VIRTUAL. - * In icount mode, this clock counts nanoseconds while the virtual - * machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL - * while the CPUs are sleeping and thus not executing instructions. - */ - -typedef enum { - QEMU_CLOCK_REALTIME = 0, - QEMU_CLOCK_VIRTUAL = 1, - QEMU_CLOCK_HOST = 2, - QEMU_CLOCK_VIRTUAL_RT = 3, - QEMU_CLOCK_MAX -} QEMUClockType; - - - - -/* - * QEMUClockType - */ - -/* - * qemu_clock_get_ns; - * @type: the clock type - * - * Get the nanosecond value of a clock with - * type @type - * - * Returns: the clock value in nanoseconds - */ -#if defined(__cplusplus) -extern "C" { -#endif -int64_t qemu_clock_get_ns(QEMUClockType type); -#if defined(__cplusplus) -} -#endif - -/** - * qemu_clock_get_ms; - * @type: the clock type - * - * Get the millisecond value of a clock with - * type @type - * - * Returns: the clock value in milliseconds - */ -static inline int64_t qemu_clock_get_ms(QEMUClockType type) -{ - return qemu_clock_get_ns(type) / SCALE_MS; -} - -#endif diff --git a/slirp_glue/qemu/typedefs.h b/slirp_glue/qemu/typedefs.h deleted file mode 100644 index 404ce2220..000000000 --- a/slirp_glue/qemu/typedefs.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef QEMU_TYPEDEFS_H -#define QEMU_TYPEDEFS_H - -/* A load of opaque types so that device init declarations don't have to - pull in all the real definitions. */ -struct Monitor; -typedef struct Monitor Monitor; -typedef struct CharDriverState CharDriverState; -typedef struct QEMUFile QEMUFile; - -#endif /* QEMU_TYPEDEFS_H */ diff --git a/slirp_glue/qemu/win32/include/sys/time.h b/slirp_glue/qemu/win32/include/sys/time.h deleted file mode 100644 index b2956a56c..000000000 --- a/slirp_glue/qemu/win32/include/sys/time.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef SYS_TIME_H -#define SYS_TIME_H - -#include - -#endif diff --git a/slirp_glue/qemu/win32/inttypes.h b/slirp_glue/qemu/win32/inttypes.h deleted file mode 100644 index 5e81fb225..000000000 --- a/slirp_glue/qemu/win32/inttypes.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef INTTYPES_H -#define INTTYPES_H - -#include - -#ifdef _WIN64 -typedef __int64 ssize_t; -#else -typedef __int32 ssize_t; -#endif -#endif diff --git a/slirp_glue/qemu/win32/stdbool.h b/slirp_glue/qemu/win32/stdbool.h deleted file mode 100644 index f4f253088..000000000 --- a/slirp_glue/qemu/win32/stdbool.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef STDBOOL_H -#define STDBOOL_H - -#define true 1 -#define false 0 -#define __bool_true_false_are_defined 1 - -#endif diff --git a/slirp_glue/qemu/win32/stdint.h b/slirp_glue/qemu/win32/stdint.h deleted file mode 100644 index ec384da2e..000000000 --- a/slirp_glue/qemu/win32/stdint.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef STDINT_H -#define STDINT_H - -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef long long int64_t; -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned int u_int32_t; -typedef unsigned long long uint64_t; - -#endif diff --git a/slirp_glue/sim_slirp.c b/slirp_glue/sim_slirp.c deleted file mode 100644 index 9971d2a67..000000000 --- a/slirp_glue/sim_slirp.c +++ /dev/null @@ -1,665 +0,0 @@ -/* sim_slirp.c: - ------------------------------------------------------------------------------ - Copyright (c) 2015, Mark Pizzolato - - 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 AUTHOR 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. - - Except as contained in this notice, the name of the author shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from the author. - - ------------------------------------------------------------------------------ - - This module provides the interface needed between sim_ether and SLiRP to - provide NAT network functionality. - -*/ - -/* Actual slirp API interface support, some code taken from slirpvde.c */ - -#define DEFAULT_IP_ADDR "10.0.2.2" - -#include "glib.h" -#include "qemu/timer.h" -#include "libslirp.h" -#include "sim_defs.h" -#include "sim_slirp.h" -#include "sim_sock.h" -#include "libslirp.h" - -#if !defined (USE_READER_THREAD) -#define pthread_mutex_init(mtx, val) -#define pthread_mutex_destroy(mtx) -#define pthread_mutex_lock(mtx) -#define pthread_mutex_unlock(mtx) -#define pthread_mutex_t int -#endif - -#define IS_TCP 0 -#define IS_UDP 1 -static const char *tcpudp[] = { - "TCP", - "UDP" - }; - -struct redir_tcp_udp { - struct in_addr inaddr; - int is_udp; - int port; - int lport; - struct redir_tcp_udp *next; - }; - -static int -_parse_redirect_port (struct redir_tcp_udp **head, const char *buff, int is_udp) -{ -char gbuf[4*CBUFSIZE]; -uint32 inaddr = 0; -int port = 0; -int lport = 0; -char *ipaddrstr = NULL; -char *portstr = NULL; -struct redir_tcp_udp *newp; - -gbuf[sizeof(gbuf)-1] = '\0'; -strncpy (gbuf, buff, sizeof(gbuf)-1); -if (((ipaddrstr = strchr(gbuf, ':')) == NULL) || (*(ipaddrstr+1) == 0)) { - sim_printf ("redir %s syntax error\n", tcpudp[is_udp]); - return -1; - } -*ipaddrstr++ = 0; - -if ((ipaddrstr) && - (((portstr = strchr (ipaddrstr, ':')) == NULL) || (*(portstr+1) == 0))) { - sim_printf ("redir %s syntax error\n", tcpudp[is_udp]); - return -1; - } -*portstr++ = 0; - -sscanf (gbuf, "%d", &lport); -sscanf (portstr, "%d", &port); -if (ipaddrstr) - inaddr = inet_addr (ipaddrstr); - -if (!inaddr) { - sim_printf ("%s redirection error: an IP address must be specified\n", tcpudp[is_udp]); - return -1; - } - -if ((newp = (struct redir_tcp_udp *)g_malloc (sizeof(struct redir_tcp_udp))) == NULL) - return -1; -else { - inet_aton (ipaddrstr, &newp->inaddr); - newp->is_udp = is_udp; - newp->port = port; - newp->lport = lport; - newp->next = *head; - *head = newp; - return 0; - } -} - -static int _do_redirects (Slirp *slirp, struct redir_tcp_udp *head) -{ -struct in_addr host_addr; -int ret = 0; - -host_addr.s_addr = htonl(INADDR_ANY); -if (head) { - ret = _do_redirects (slirp, head->next); - if (slirp_add_hostfwd (slirp, head->is_udp, host_addr, head->lport, head->inaddr, head->port) < 0) { - sim_printf("Can't establish redirector for: redir %s =%d:%s:%d\n", - tcpudp[head->is_udp], head->lport, inet_ntoa(head->inaddr), head->port); - ++ret; - } - } -return ret; -} - -struct slirp_write_request { - struct slirp_write_request *next; - char msg[1518]; - size_t len; - }; - -struct sim_slirp { - Slirp *slirp; - char *args; - struct in_addr vnetwork; - struct in_addr vnetmask; - int maskbits; - struct in_addr vgateway; - int dhcpmgmt; - struct in_addr vdhcp_start; - struct in_addr vnameserver; - char *boot_file; - char *tftp_path; - char *dns_search; - char **dns_search_domains; - struct redir_tcp_udp *rtcp; - GArray *gpollfds; - SOCKET db_chime; /* write packet doorbell */ - struct slirp_write_request *write_requests; - struct slirp_write_request *write_buffers; - pthread_mutex_t write_buffer_lock; - void *opaque; /* opaque value passed during packet delivery */ - packet_callback callback; /* slirp arriving packet delivery callback */ - DEVICE *dptr; - uint32 dbit; - }; - -#if defined(__cplusplus) -extern "C" { -#endif -DEVICE *slirp_dptr; -uint32 slirp_dbit; -#if defined(__cplusplus) -} -#endif - -SLIRP *sim_slirp_open (const char *args, void *opaque, packet_callback callback, DEVICE *dptr, uint32 dbit, char *errbuf, size_t errbuf_size) -{ -SLIRP *slirp = (SLIRP *)g_malloc0(sizeof(*slirp)); -char *targs = g_strdup (args); -const char *tptr = targs; -const char *cptr; -char tbuf[CBUFSIZE], gbuf[CBUFSIZE], abuf[CBUFSIZE]; -int err; - -slirp_dptr = dptr; -slirp_dbit = dbit; -slirp->args = (char *)g_malloc0(1 + strlen(args)); -strcpy (slirp->args, args); -slirp->opaque = opaque; -slirp->callback = callback; -slirp->maskbits = 24; -slirp->dhcpmgmt = 1; -slirp->db_chime = INVALID_SOCKET; -inet_aton(DEFAULT_IP_ADDR,&slirp->vgateway); -pthread_mutex_init (&slirp->write_buffer_lock, NULL); - -err = 0; -while (*tptr && !err) { - tptr = get_glyph_nc (tptr, tbuf, ','); - if (!tbuf[0]) - break; - cptr = tbuf; - cptr = get_glyph (cptr, gbuf, '='); - if (0 == MATCH_CMD (gbuf, "DHCP")) { - slirp->dhcpmgmt = 1; - if (cptr && *cptr) - inet_aton (cptr, &slirp->vdhcp_start); - continue; - } - if (0 == MATCH_CMD (gbuf, "TFTP")) { - if (cptr && *cptr) - slirp->tftp_path = g_strdup (cptr); - else { - strlcpy (errbuf, "Missing TFTP Path", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "BOOTFILE")) { - if (cptr && *cptr) - slirp->boot_file = g_strdup (cptr); - else { - strlcpy (errbuf, "Missing DHCP Boot file name", errbuf_size); - err = 1; - } - continue; - } - if ((0 == MATCH_CMD (gbuf, "NAMESERVER")) || - (0 == MATCH_CMD (gbuf, "DNS"))) { - if (cptr && *cptr) - inet_aton (cptr, &slirp->vnameserver); - else { - strlcpy (errbuf, "Missing nameserver", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "DNSSEARCH")) { - if (cptr && *cptr) { - int count = 0; - char *name; - - slirp->dns_search = g_strdup (cptr); - name = slirp->dns_search; - do { - ++count; - slirp->dns_search_domains = (char **)realloc (slirp->dns_search_domains, (count + 1)*sizeof(char *)); - slirp->dns_search_domains[count] = NULL; - slirp->dns_search_domains[count-1] = name; - name = strchr (name, ':'); - if (name) { - *name = '\0'; - ++name; - } - } while (name && *name); - } - else { - strlcpy (errbuf, "Missing DNS search list", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "GATEWAY")) { - if (cptr && *cptr) { - cptr = get_glyph (cptr, abuf, '/'); - if (cptr && *cptr) - slirp->maskbits = atoi (cptr); - inet_aton (abuf, &slirp->vgateway); - } - else { - strlcpy (errbuf, "Missing host", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "NETWORK")) { - if (cptr && *cptr) { - cptr = get_glyph (cptr, abuf, '/'); - if (cptr && *cptr) - slirp->maskbits = atoi (cptr); - inet_aton (abuf, &slirp->vnetwork); - } - else { - strlcpy (errbuf, "Missing network", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "NODHCP")) { - slirp->dhcpmgmt = 0; - continue; - } - if (0 == MATCH_CMD (gbuf, "UDP")) { - if (cptr && *cptr) - err = _parse_redirect_port (&slirp->rtcp, cptr, IS_UDP); - else { - strlcpy (errbuf, "Missing UDP port mapping", errbuf_size); - err = 1; - } - continue; - } - if (0 == MATCH_CMD (gbuf, "TCP")) { - if (cptr && *cptr) - err = _parse_redirect_port (&slirp->rtcp, cptr, IS_TCP); - else { - strlcpy (errbuf, "Missing TCP port mapping", errbuf_size); - err = 1; - } - continue; - } - snprintf (errbuf, errbuf_size - 1, "Unexpected NAT argument: %s", gbuf); - err = 1; - } -if (err) { - sim_slirp_close (slirp); - g_free (targs); - return NULL; - } - -slirp->vnetmask.s_addr = slirp->maskbits ? htonl(~((1 << (32-slirp->maskbits)) - 1)) : 0xFFFFFFFF; -slirp->vnetwork.s_addr = slirp->vgateway.s_addr & slirp->vnetmask.s_addr; -if ((slirp->vgateway.s_addr & ~slirp->vnetmask.s_addr) == 0) - slirp->vgateway.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 2); -if ((slirp->vdhcp_start.s_addr == 0) && slirp->dhcpmgmt) - slirp->vdhcp_start.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 15); -if (slirp->vnameserver.s_addr == 0) - slirp->vnameserver.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 3); -slirp->slirp = slirp_init (0, slirp->vnetwork, slirp->vnetmask, slirp->vgateway, - NULL, slirp->tftp_path, slirp->boot_file, - slirp->vdhcp_start, slirp->vnameserver, - (const char **)(slirp->dns_search_domains), (void *)slirp); - -if (_do_redirects (slirp->slirp, slirp->rtcp)) { - sim_slirp_close (slirp); - slirp = NULL; - } -else { - char db_host[32]; - GPollFD pfd; - int64_t rnd_val = qemu_clock_get_ns ((QEMUClockType)0) / 1000000; - - slirp->gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); - /* setup transmit packet wakeup doorbell */ - do { - if ((rnd_val & 0xFFFF) == 0) - ++rnd_val; - sprintf (db_host, "localhost:%d", (int)(rnd_val & 0xFFFF)); - slirp->db_chime = sim_connect_sock_ex (db_host, db_host, NULL, NULL, SIM_SOCK_OPT_DATAGRAM | SIM_SOCK_OPT_BLOCKING); - } while (slirp->db_chime == INVALID_SOCKET); - memset (&pfd, 0, sizeof (pfd)); - pfd.fd = slirp->db_chime; - pfd.events = G_IO_IN; - g_array_append_val(slirp->gpollfds, pfd); - slirp->dbit = dbit; - slirp->dptr = dptr; - - sim_slirp_show(slirp, stdout); - if (sim_log && (sim_log != stdout)) - sim_slirp_show(slirp, sim_log); - if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log)) - sim_slirp_show(slirp, sim_deb); - } -g_free (targs); -return slirp; -} - -void sim_slirp_close (SLIRP *slirp) -{ -struct redir_tcp_udp *rtmp; - -if (slirp) { - g_free (slirp->args); - g_free (slirp->tftp_path); - g_free (slirp->boot_file); - g_free (slirp->dns_search); - g_free (slirp->dns_search_domains); - while ((rtmp = slirp->rtcp)) { - slirp_remove_hostfwd(slirp->slirp, rtmp->is_udp, rtmp->inaddr, rtmp->lport); - slirp->rtcp = rtmp->next; - g_free (rtmp); - } - g_array_free(slirp->gpollfds, true); - if (slirp->db_chime != INVALID_SOCKET) - closesocket (slirp->db_chime); - if (1) { - struct slirp_write_request *buffer; - - while (NULL != (buffer = slirp->write_buffers)) { - slirp->write_buffers = buffer->next; - free(buffer); - } - while (NULL != (buffer = slirp->write_requests)) { - slirp->write_requests = buffer->next; - free(buffer); - } - } - pthread_mutex_destroy (&slirp->write_buffer_lock); - if (slirp->slirp) - slirp_cleanup(slirp->slirp); - } -g_free (slirp); -} - -t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) -{ -fprintf (st, "%s", -"NAT options:\n" -" DHCP{=dhcp_start_address} Enables DHCP server and specifies\n" -" guest LAN DHCP start IP address\n" -" BOOTFILE=bootfilename specifies DHCP returned Boot Filename\n" -" TFTP=tftp-base-path Enables TFTP server and specifies\n" -" base file path\n" -" NAMESERVER=nameserver_ipaddres specifies DHCP nameserver IP address\n" -" DNS=nameserver_ipaddres specifies DHCP nameserver IP address\n" -" DNSSEARCH=domain{:domain{:domain}} specifies DNS Domains search suffixes\n" -" GATEWAY=host_ipaddress{/masklen} specifies LAN gateway IP address\n" -" NETWORK=network_ipaddress{/masklen} specifies LAN network address\n" -" UDP=port:address:address's-port maps host UDP port to guest port\n" -" TCP=port:address:address's-port maps host TCP port to guest port\n" -" NODHCP disables DHCP server\n\n" -"Default NAT Options: GATEWAY=10.0.2.2, masklen=24(netmask is 255.255.255.0)\n" -" DHCP=10.0.2.15, NAMESERVER=10.0.2.3\n" -" Nameserver defaults to proxy traffic to host system's active nameserver\n\n" -"The 'address' field in the UDP and TCP port mappings are the simulated\n" -"(guest) system's IP address which, if DHCP allocated would default to\n" -"10.0.2.15 or could be statically configured to any address including\n" -"10.0.2.4 thru 10.0.2.14.\n\n" -"NAT limitations\n\n" -"There are four limitations of NAT mode which users should be aware of:\n\n" -" 1) ICMP protocol limitations:\n" -" Some frequently used network debugging tools (e.g. ping or tracerouting)\n" -" rely on the ICMP protocol for sending/receiving messages. While some\n" -" ICMP support is available on some hosts (ping may or may not work),\n" -" some other tools may not work reliably.\n\n" -" 2) Receiving of UDP broadcasts is not reliable:\n" -" The guest does not reliably receive broadcasts, since, in order to save\n" -" resources, it only listens for a certain amount of time after the guest\n" -" has sent UDP data on a particular port.\n\n" -" 3) Protocols such as GRE, DECnet, LAT and Clustering are unsupported:\n" -" Protocols other than TCP and UDP are not supported.\n\n" -" 4) Forwarding host ports < 1024 impossible:\n" -" On Unix-based hosts (e.g. Linux, Solaris, Mac OS X) it is not possible\n" -" to bind to ports below 1024 from applications that are not run by root.\n" -" As a result, if you try to configure such a port forwarding, the attach\n" -" will fail.\n\n" -"These limitations normally don't affect standard network use. But the\n" -"presence of NAT has also subtle effects that may interfere with protocols\n" -"that are normally working. One example is NFS, where the server is often\n" -"configured to refuse connections from non-privileged ports (i.e. ports not\n" -" below 1024).\n" -); -return SCPE_OK; -} - -int sim_slirp_send (SLIRP *slirp, const char *msg, size_t len, int flags) -{ -struct slirp_write_request *request; -int wake_needed = 0; - -if (!slirp) { - errno = EBADF; - return 0; - } -/* Get a buffer */ -pthread_mutex_lock (&slirp->write_buffer_lock); -if (NULL != (request = slirp->write_buffers)) - slirp->write_buffers = request->next; -pthread_mutex_unlock (&slirp->write_buffer_lock); -if (NULL == request) - request = (struct slirp_write_request *)g_malloc(sizeof(*request)); - -/* Copy buffer contents */ -request->len = len; -memcpy(request->msg, msg, len); - -/* Insert buffer at the end of the write list (to make sure that */ -/* packets make it to the wire in the order they were presented here) */ -pthread_mutex_lock (&slirp->write_buffer_lock); -request->next = NULL; -if (slirp->write_requests) { - struct slirp_write_request *last_request = slirp->write_requests; - - while (last_request->next) { - last_request = last_request->next; - } - last_request->next = request; - } -else { - slirp->write_requests = request; - wake_needed = 1; - } -pthread_mutex_unlock (&slirp->write_buffer_lock); - -if (wake_needed) - sim_write_sock (slirp->db_chime, msg, 0); -return len; -} - -void slirp_output (void *opaque, const uint8_t *pkt, int pkt_len) -{ -SLIRP *slirp = (SLIRP *)opaque; - -slirp->callback (slirp->opaque, pkt, pkt_len); -} - -void sim_slirp_show (SLIRP *slirp, FILE *st) -{ -struct redir_tcp_udp *rtmp; - -if ((slirp == NULL) || (slirp->slirp == NULL)) - return; -fprintf (st, "NAT args: %s\n", slirp->args); -fprintf (st, "NAT network setup:\n"); -fprintf (st, " gateway =%s/%d", inet_ntoa(slirp->vgateway), slirp->maskbits); -fprintf (st, "(%s)\n", inet_ntoa(slirp->vnetmask)); -fprintf (st, " DNS =%s\n", inet_ntoa(slirp->vnameserver)); -if (slirp->vdhcp_start.s_addr != 0) - fprintf (st, " dhcp_start =%s\n", inet_ntoa(slirp->vdhcp_start)); -if (slirp->boot_file) - fprintf (st, " dhcp bootfile =%s\n", slirp->boot_file); -if (slirp->dns_search_domains) { - char **domains = slirp->dns_search_domains; - - fprintf (st, " DNS domains ="); - while (*domains) { - fprintf (st, "%s%s", (domains != slirp->dns_search_domains) ? ", " : "", *domains); - ++domains; - } - fprintf (st, "\n"); - } -if (slirp->tftp_path) - fprintf (st, " tftp prefix =%s\n", slirp->tftp_path); -rtmp = slirp->rtcp; -while (rtmp) { - fprintf (st, " redir %3s =%d:%s:%d\n", tcpudp[rtmp->is_udp], rtmp->lport, inet_ntoa(rtmp->inaddr), rtmp->port); - rtmp = rtmp->next; - } -slirp_connection_info (slirp->slirp, (Monitor *)st); -} - -#if !defined(MAX) -#define MAX(a,b) (((a)>(b)) ? (a) : (b)) -#endif - -static int pollfds_fill (GArray *pollfds, fd_set *rfds, fd_set *wfds, - fd_set *xfds) -{ -int nfds = -1; -guint i; - -for (i = 0; i < pollfds->len; i++) { - GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); - int fd = pfd->fd; - int events = pfd->events; - if (events & G_IO_IN) { - FD_SET(fd, rfds); - nfds = MAX(nfds, fd); - } - if (events & G_IO_OUT) { - FD_SET(fd, wfds); - nfds = MAX(nfds, fd); - } - if (events & (G_IO_PRI | G_IO_HUP | G_IO_ERR)) { - FD_SET(fd, xfds); - nfds = MAX(nfds, fd); - } - } -return nfds; -} - -static void pollfds_poll (GArray *pollfds, int nfds, fd_set *rfds, - fd_set *wfds, fd_set *xfds) -{ -guint i; - -for (i = 0; i < pollfds->len; i++) { - GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); - int fd = pfd->fd; - int revents = 0; - - if (FD_ISSET(fd, rfds)) { - revents |= G_IO_IN; - } - if (FD_ISSET(fd, wfds)) { - revents |= G_IO_OUT; - } - if (FD_ISSET(fd, xfds)) { - revents |= G_IO_PRI; - } - pfd->revents = revents & pfd->events; - } -} - -int sim_slirp_select (SLIRP *slirp, int ms_timeout) -{ -int select_ret = 0; -uint32 slirp_timeout = ms_timeout; -struct timeval timeout; -fd_set rfds, wfds, xfds; -fd_set save_rfds, save_wfds, save_xfds; -int nfds; - -if (!slirp) /* Not active? */ - return -1; /* That's an error */ -/* Populate the GPollFDs from slirp */ -g_array_set_size (slirp->gpollfds, 1); /* Leave the doorbell chime alone */ -slirp_pollfds_fill(slirp->gpollfds, &slirp_timeout); -timeout.tv_sec = slirp_timeout / 1000; -timeout.tv_usec = (slirp_timeout % 1000) * 1000; - -FD_ZERO(&rfds); -FD_ZERO(&wfds); -FD_ZERO(&xfds); -/* Extract the GPollFDs interest */ -nfds = pollfds_fill (slirp->gpollfds, &rfds, &wfds, &xfds); -save_rfds = rfds; -save_wfds = wfds; -save_xfds = xfds; -select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &timeout); -if (select_ret) { - int i; - /* Update the GPollFDs results */ - pollfds_poll (slirp->gpollfds, nfds, &rfds, &wfds, &xfds); - if (FD_ISSET (slirp->db_chime, &rfds)) { - char buf[32]; - /* consume the doorbell wakeup ring */ - (void)recv (slirp->db_chime, buf, sizeof (buf), 0); - } - sim_debug (slirp->dbit, slirp->dptr, "Select returned %d\r\n", select_ret); - for (i=0; idbit, slirp->dptr, "%d: save_rfd=%d, rfd=%d\r\n", i, FD_ISSET(i, &save_rfds), FD_ISSET(i, &rfds)); - if (FD_ISSET(i, &wfds) || FD_ISSET(i, &save_wfds)) - sim_debug (slirp->dbit, slirp->dptr, "%d: save_wfd=%d, wfd=%d\r\n", i, FD_ISSET(i, &save_wfds), FD_ISSET(i, &wfds)); - if (FD_ISSET(i, &xfds) || FD_ISSET(i, &save_xfds)) - sim_debug (slirp->dbit, slirp->dptr, "%d: save_xfd=%d, xfd=%d\r\n", i, FD_ISSET(i, &save_xfds), FD_ISSET(i, &xfds)); - } - } -return select_ret + 1; /* Force dispatch even on timeout */ -} - -void sim_slirp_dispatch (SLIRP *slirp) -{ -struct slirp_write_request *request; - -/* first deliver any transmit packets which are pending */ - -pthread_mutex_lock (&slirp->write_buffer_lock); -while (NULL != (request = slirp->write_requests)) { - /* Pull buffer off request list */ - slirp->write_requests = request->next; - pthread_mutex_unlock (&slirp->write_buffer_lock); - - slirp_input (slirp->slirp, (const uint8_t *)request->msg, (int)request->len); - - pthread_mutex_lock (&slirp->write_buffer_lock); - /* Put buffer on free buffer list */ - request->next = slirp->write_buffers; - slirp->write_buffers = request; - } -pthread_mutex_unlock (&slirp->write_buffer_lock); - -slirp_pollfds_poll(slirp->gpollfds, 0); - -} - diff --git a/slirp_glue/sim_slirp.h b/slirp_glue/sim_slirp.h deleted file mode 100644 index 0411e3fcf..000000000 --- a/slirp_glue/sim_slirp.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef SIM_SLIRP_H -#define SIM_SLIRP_H - -#if defined(HAVE_SLIRP_NETWORK) - -#include "sim_defs.h" -typedef struct sim_slirp SLIRP; - -typedef void (*packet_callback)(void *opaque, const unsigned char *buf, int len); - -SLIRP *sim_slirp_open (const char *args, void *opaque, packet_callback callback, DEVICE *dptr, uint32 dbit, char *errbuf, size_t errbuf_size); -void sim_slirp_close (SLIRP *slirp); -int sim_slirp_send (SLIRP *slirp, const char *msg, size_t len, int flags); -int sim_slirp_select (SLIRP *slirp, int ms_timeout); -void sim_slirp_dispatch (SLIRP *slirp); -t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); -void sim_slirp_show (SLIRP *slirp, FILE *st); - -#endif /* HAVE_SLIRP_NETWORK */ - -#endif