New upstream version 1.0.1
Reinhard Tartler
3 years ago
0 | # https://clang.llvm.org/docs/ClangFormat.html | |
1 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html | |
2 | --- | |
3 | Language: Cpp | |
4 | AlignAfterOpenBracket: Align | |
5 | AlignConsecutiveAssignments: false # although we like it, it creates churn | |
6 | AlignConsecutiveDeclarations: false | |
7 | AlignEscapedNewlinesLeft: true | |
8 | AlignOperands: true | |
9 | AlignTrailingComments: false # churn | |
10 | AllowAllParametersOfDeclarationOnNextLine: true | |
11 | AllowShortBlocksOnASingleLine: false | |
12 | AllowShortCaseLabelsOnASingleLine: false | |
13 | AllowShortFunctionsOnASingleLine: None | |
14 | AllowShortIfStatementsOnASingleLine: false | |
15 | AllowShortLoopsOnASingleLine: false | |
16 | AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account | |
17 | AlwaysBreakBeforeMultilineStrings: false | |
18 | BinPackArguments: true | |
19 | BinPackParameters: true | |
20 | BraceWrapping: | |
21 | AfterControlStatement: false | |
22 | AfterEnum: false | |
23 | AfterFunction: true | |
24 | AfterStruct: false | |
25 | AfterUnion: false | |
26 | BeforeElse: false | |
27 | IndentBraces: false | |
28 | BreakBeforeBinaryOperators: None | |
29 | BreakBeforeBraces: Custom | |
30 | BreakBeforeTernaryOperators: false | |
31 | BreakStringLiterals: true | |
32 | ColumnLimit: 80 | |
33 | ContinuationIndentWidth: 4 | |
34 | Cpp11BracedListStyle: false | |
35 | DerivePointerAlignment: false | |
36 | DisableFormat: false | |
37 | IndentCaseLabels: false | |
38 | IndentWidth: 4 | |
39 | IndentWrappedFunctionNames: false | |
40 | KeepEmptyLinesAtTheStartOfBlocks: false | |
41 | MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? | |
42 | MacroBlockEnd: '.*_END$' | |
43 | MaxEmptyLinesToKeep: 2 | |
44 | PointerAlignment: Right | |
45 | ReflowComments: true | |
46 | SortIncludes: false | |
47 | SpaceAfterCStyleCast: false | |
48 | SpaceBeforeAssignmentOperators: true | |
49 | SpaceBeforeParens: ControlStatements | |
50 | SpaceInEmptyParentheses: false | |
51 | SpacesBeforeTrailingComments: 1 | |
52 | SpacesInContainerLiterals: true | |
53 | SpacesInParentheses: false | |
54 | SpacesInSquareBrackets: false | |
55 | Standard: Auto | |
56 | UseTab: Never | |
57 | ... |
0 | name: Main | |
1 | on: [push, pull_request] | |
2 | jobs: | |
3 | test-main: | |
4 | runs-on: ubuntu-latest | |
5 | strategy: | |
6 | matrix: | |
7 | libslirp_commit: [master, v4.3.0, v4.2.0, v4.1.0] | |
8 | steps: | |
9 | - uses: actions/checkout@v1 | |
10 | - run: docker build -t slirp4netns-tests --build-arg LIBSLIRP_COMMIT -f Dockerfile.tests . | |
11 | env: | |
12 | LIBSLIRP_COMMIT: ${{ matrix.libslirp_commit }} | |
13 | - run: docker run --rm --privileged slirp4netns-tests | |
14 | test-build: | |
15 | runs-on: ubuntu-latest | |
16 | steps: | |
17 | - uses: actions/checkout@v1 | |
18 | - run: DOCKER_BUILDKIT=1 docker build -f Dockerfile.buildtests . | |
19 | artifact: | |
20 | runs-on: ubuntu-latest | |
21 | steps: | |
22 | - uses: actions/checkout@v1 | |
23 | - run: DOCKER_BUILDKIT=1 docker build -o /tmp/artifact --target artifact -f Dockerfile.buildtests . | |
24 | - run: (cd /tmp/artifact; sha256sum *) | |
25 | - uses: actions/upload-artifact@v1 | |
26 | with: | |
27 | name: slirp4netns-x86_64 | |
28 | path: /tmp/artifact/slirp4netns |
0 | name: Release | |
1 | on: | |
2 | push: | |
3 | tags: | |
4 | - 'v*' | |
5 | ||
6 | jobs: | |
7 | release: | |
8 | runs-on: ubuntu-latest | |
9 | steps: | |
10 | - uses: actions/checkout@v1 | |
11 | - run: DOCKER_BUILDKIT=1 docker build -o /tmp/artifact --target artifact -f Dockerfile.buildtests . | |
12 | - run: (cd /tmp/artifact; sha256sum *) | |
13 | - uses: actions/create-release@v1 | |
14 | id: create_release | |
15 | env: | |
16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
17 | with: | |
18 | tag_name: ${{ github.ref }} | |
19 | release_name: ${{ github.ref }} | |
20 | draft: true | |
21 | - uses: actions/upload-release-asset@v1.0.1 | |
22 | env: | |
23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
24 | with: | |
25 | upload_url: ${{ steps.create_release.outputs.upload_url }} | |
26 | asset_path: /tmp/artifact/slirp4netns | |
27 | asset_name: slirp4netns-x86_64 | |
28 | asset_content_type: application/octet-stream |
0 | dist: trusty | |
1 | sudo: required | |
2 | ||
3 | services: | |
4 | - docker | |
5 | ||
6 | script: | |
7 | - docker build -t slirp4netns-tests -f Dockerfile.tests . | |
8 | - docker run --security-opt seccomp="unconfined" -ti --rm -v /dev:/dev slirp4netns-tests | |
9 | - docker build -q -f Dockerfile.buildtests . |
0 | FROM alpine:3.8 AS buildtest-alpine38-static | |
1 | RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static | |
0 | ARG LIBSLIRP_COMMIT=v4.3.0 | |
1 | ||
2 | # Alpine | |
3 | FROM alpine:3 AS buildtest-alpine3-static | |
4 | RUN apk add --no-cache git build-base autoconf automake libtool linux-headers glib-dev glib-static libcap-static libcap-dev libseccomp-dev git meson | |
5 | RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp | |
6 | WORKDIR /libslirp | |
7 | ARG LIBSLIRP_COMMIT | |
8 | RUN git pull && git checkout ${LIBSLIRP_COMMIT} && meson setup --default-library=both build && ninja -C build install | |
2 | 9 | COPY . /src |
3 | 10 | WORKDIR /src |
4 | 11 | RUN ./autogen.sh && ./configure LDFLAGS="-static" && make && cp -f slirp4netns / |
5 | 12 | |
13 | # Ubuntu | |
6 | 14 | FROM ubuntu:18.04 AS buildtest-ubuntu1804-common |
7 | RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev | |
15 | RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev git ninja-build python3-pip | |
16 | RUN pip3 install meson | |
17 | RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp | |
18 | WORKDIR /libslirp | |
19 | ARG LIBSLIRP_COMMIT | |
20 | RUN git pull && git checkout ${LIBSLIRP_COMMIT} && meson setup build && ninja -C build install | |
8 | 21 | COPY . /src |
9 | 22 | WORKDIR /src |
10 | 23 | RUN ./autogen.sh |
12 | 25 | FROM buildtest-ubuntu1804-common AS buildtest-ubuntu1804-dynamic |
13 | 26 | RUN ./configure && make && cp -f slirp4netns / |
14 | 27 | |
15 | FROM buildtest-ubuntu1804-common AS buildtest-ubuntu1804-static | |
28 | # CentOS | |
29 | FROM centos:7 AS buildtest-centos7-common | |
30 | RUN yum install -y epel-release | |
31 | RUN yum install -y autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel ninja-build python3-pip | |
32 | RUN pip3 install meson | |
33 | RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp | |
34 | WORKDIR /libslirp | |
35 | ARG LIBSLIRP_COMMIT | |
36 | RUN git pull && git checkout ${LIBSLIRP_COMMIT} && meson setup --default-library=both --prefix=/usr build && ninja-build -C build install | |
37 | COPY . /src | |
38 | WORKDIR /src | |
39 | RUN ./autogen.sh | |
40 | ||
41 | FROM buildtest-centos7-common AS buildtest-centos7-dynamic | |
42 | RUN ./configure && make && cp -f slirp4netns / | |
43 | ||
44 | FROM buildtest-centos7-common AS buildtest-centos7-static | |
45 | RUN yum install -y glibc-static glib2-static | |
46 | RUN yum-config-manager --add-repo=https://buildlogs.centos.org/centos/7/virt/x86_64/container && \ | |
47 | yum install --nogpgcheck -y libseccomp-static | |
16 | 48 | RUN ./configure LDFLAGS="-static" && make && cp -f slirp4netns / |
17 | 49 | |
50 | # openSUSE (dynamic only) | |
51 | FROM opensuse/leap:15 AS buildtest-opensuse15-common | |
52 | RUN zypper install -y --no-recommends autoconf automake gcc glib2-devel git make libcap-devel libseccomp-devel ninja python3-pip | |
53 | RUN pip3 install meson | |
54 | RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp | |
55 | WORKDIR /libslirp | |
56 | ARG LIBSLIRP_COMMIT | |
57 | RUN git pull && git checkout ${LIBSLIRP_COMMIT} && meson setup --default-library=both build && ninja -C build install | |
58 | COPY . /src | |
59 | WORKDIR /src | |
60 | RUN ./autogen.sh | |
61 | ||
62 | FROM buildtest-opensuse15-common AS buildtest-opensuse15-dynamic | |
63 | RUN ./configure && make && cp -f slirp4netns / | |
64 | ||
65 | # artifact for GitHub actions | |
66 | FROM scratch AS artifact | |
67 | COPY --from=buildtest-centos7-static /slirp4netns /slirp4netns | |
68 | ||
18 | 69 | FROM scratch AS buildtest-final-stage |
19 | COPY --from=buildtest-alpine38-static /slirp4netns /buildtest-alpine38-static | |
70 | COPY --from=buildtest-alpine3-static /slirp4netns /buildtest-alpine3-static | |
20 | 71 | COPY --from=buildtest-ubuntu1804-dynamic /slirp4netns /buildtest-ubuntu1804-dynamic |
21 | COPY --from=buildtest-ubuntu1804-static /slirp4netns /buildtest-ubuntu1804-static | |
72 | COPY --from=buildtest-centos7-dynamic /slirp4netns /buildtest-centos7-dynamic | |
73 | COPY --from=buildtest-centos7-static /slirp4netns /buildtest-centos7-static | |
74 | COPY --from=buildtest-opensuse15-dynamic /slirp4netns /buildtest-opensuse15-dynamic |
0 | FROM ubuntu AS build | |
1 | RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev | |
0 | ARG LIBSLIRP_COMMIT=v4.3.0 | |
1 | ||
2 | FROM ubuntu:18.04 AS build | |
3 | RUN apt update && apt install -y automake autotools-dev make gcc libglib2.0-dev libcap-dev libseccomp-dev git ninja-build python3-pip | |
4 | RUN pip3 install meson | |
5 | RUN git clone https://gitlab.freedesktop.org/slirp/libslirp.git /libslirp | |
6 | WORKDIR /libslirp | |
7 | ARG LIBSLIRP_COMMIT | |
8 | RUN git pull && git checkout ${LIBSLIRP_COMMIT} && meson setup build && ninja -C build install | |
2 | 9 | COPY . /slirp4netns |
3 | 10 | WORKDIR /slirp4netns |
4 | 11 | RUN chown -R 1000:1000 /slirp4netns |
7 | 14 | |
8 | 15 | FROM build AS test |
9 | 16 | USER 0 |
10 | RUN apt update && apt install -y git indent libtool iproute2 clang clang-tidy iputils-ping iperf3 nmap jq | |
17 | RUN apt update && apt install -y git libtool iproute2 clang clang-format clang-tidy iputils-ping iperf3 nmap jq | |
11 | 18 | USER 1000:1000 |
12 | 19 | CMD ["make", "ci"] |
1 | 1 | |
2 | 2 | [Org] |
3 | 3 | |
4 | # Approvers (aka Core Maintainers) approve pull requests. | |
4 | # Approvers (aka Core Maintainers) approve pull requests and ship releases. | |
5 | 5 | # GitHub Team: rootless-containers/slirp4netns-approvers |
6 | # | |
7 | # An approver may approve PRs and ship releases without waiting for LGTMs from other approvers. | |
8 | # However, the approver should try to get LGTMs from other approvers for significant changes. | |
9 | # | |
10 | # Release guide (since v0.4.3): | |
11 | # 1. Bump up the version string to `vX.Y.Z` (or `vX.Y.Z-beta.W`) in `configure.ac`. | |
12 | # 2. `git commit -a -s -m vX.Y.Z` | |
13 | # 3. Bump up the version string to `vX.Y.Z+dev` (or `vX.Y.Z-beta.W`+dev) in `configure.ac`. | |
14 | # 4. `git commit -a -s -m vX.Y.Z+dev` | |
15 | # 5. Open a PR and merge it. | |
16 | # 6. Create a tag `v.X.Y.Z` for the `vX.Y.Z` commit, and push the tag to the upstream: `git push upstream vX.Y.Z` | |
17 | # 7. GitHub Actions automatically ships a draft release with a statically compiled binary: https://github.com/rootless-containers/slirp4netns/releases | |
18 | # If it fails, check the GitHub Actions log: https://github.com/rootless-containers/slirp4netns/actions?query=workflow%3ARelease | |
19 | # 8. Add release notes to the draft release and ship the release. | |
6 | 20 | [Org.Approvers] |
7 | 21 | people = [ |
8 | 22 | "akihirosuda", |
72 | 86 | [people] |
73 | 87 | [people.akihirosuda] |
74 | 88 | Name = "Akihiro Suda" |
75 | Email = "suda.akihiro@lab.ntt.co.jp" | |
89 | Email = "akihiro.suda.cz@hco.ntt.co.jp" | |
76 | 90 | GitHub = "AkihiroSuda" |
77 | 91 | |
78 | 92 | [people.barthalion] |
0 | 0 | bin_PROGRAMS = slirp4netns |
1 | 1 | |
2 | AM_CFLAGS = @GLIB_CFLAGS@ | |
2 | AM_CFLAGS = @GLIB_CFLAGS@ @SLIRP_CFLAGS@ @LIBCAP_CFLAGS@ @LIBSECCOMP_CFLAGS@ | |
3 | 3 | |
4 | noinst_LIBRARIES = libslirp.a libparson.a | |
4 | noinst_LIBRARIES = libparson.a | |
5 | 5 | |
6 | 6 | AM_TESTS_ENVIRONMENT = PATH="$(abs_top_builddir):$(PATH)" |
7 | 7 | TESTS = tests/test-slirp4netns.sh tests/test-slirp4netns-configure.sh tests/test-slirp4netns-exit-fd.sh tests/test-slirp4netns-ready-fd.sh tests/test-slirp4netns-api-socket.sh tests/test-slirp4netns-disable-host-loopback.sh tests/test-slirp4netns-cidr.sh |
13 | 13 | tests/common.sh \ |
14 | 14 | slirp4netns.h \ |
15 | 15 | api.h \ |
16 | vendor/libslirp/COPYRIGHT \ | |
17 | vendor/libslirp/README.md \ | |
18 | vendor/libslirp/src/bootp.h \ | |
19 | vendor/libslirp/src/debug.h \ | |
20 | vendor/libslirp/src/dhcpv6.h \ | |
21 | vendor/libslirp/src/if.h \ | |
22 | vendor/libslirp/src/ip6.h \ | |
23 | vendor/libslirp/src/ip6_icmp.h \ | |
24 | vendor/libslirp/src/ip.h \ | |
25 | vendor/libslirp/src/ip_icmp.h \ | |
26 | vendor/libslirp/src/libslirp.h \ | |
27 | vendor/libslirp/src/libslirp-version.h \ | |
28 | vendor/libslirp/src/main.h \ | |
29 | vendor/libslirp/src/mbuf.h \ | |
30 | vendor/libslirp/src/misc.h \ | |
31 | vendor/libslirp/src/ncsi-pkt.h \ | |
32 | vendor/libslirp/src/sbuf.h \ | |
33 | vendor/libslirp/src/slirp.h \ | |
34 | vendor/libslirp/src/socket.h \ | |
35 | vendor/libslirp/src/stream.h \ | |
36 | vendor/libslirp/src/tftp.h \ | |
37 | vendor/libslirp/src/tcp.h \ | |
38 | vendor/libslirp/src/tcpip.h \ | |
39 | vendor/libslirp/src/tcp_timer.h \ | |
40 | vendor/libslirp/src/tcp_var.h \ | |
41 | vendor/libslirp/src/udp.h \ | |
42 | vendor/libslirp/src/util.h \ | |
43 | vendor/libslirp/src/vmstate.h \ | |
16 | sandbox.h \ | |
17 | seccompfilter.h \ | |
18 | tests/slirp4netns-no-unmount.sh \ | |
44 | 19 | vendor/parson/LICENSE \ |
45 | 20 | vendor/parson/README.md \ |
46 | 21 | vendor/parson/parson.h |
47 | 22 | |
48 | libslirp_a_SOURCES = \ | |
49 | vendor/libslirp/src/arp_table.c \ | |
50 | vendor/libslirp/src/bootp.c \ | |
51 | vendor/libslirp/src/cksum.c \ | |
52 | vendor/libslirp/src/dhcpv6.c \ | |
53 | vendor/libslirp/src/dnssearch.c \ | |
54 | vendor/libslirp/src/if.c \ | |
55 | vendor/libslirp/src/ip6_icmp.c \ | |
56 | vendor/libslirp/src/ip6_input.c \ | |
57 | vendor/libslirp/src/ip6_output.c \ | |
58 | vendor/libslirp/src/ip_icmp.c \ | |
59 | vendor/libslirp/src/ip_input.c \ | |
60 | vendor/libslirp/src/ip_output.c \ | |
61 | vendor/libslirp/src/mbuf.c \ | |
62 | vendor/libslirp/src/misc.c \ | |
63 | vendor/libslirp/src/ncsi.c \ | |
64 | vendor/libslirp/src/ndp_table.c \ | |
65 | vendor/libslirp/src/sbuf.c \ | |
66 | vendor/libslirp/src/slirp.c \ | |
67 | vendor/libslirp/src/socket.c \ | |
68 | vendor/libslirp/src/state.c \ | |
69 | vendor/libslirp/src/stream.c \ | |
70 | vendor/libslirp/src/tcp_input.c \ | |
71 | vendor/libslirp/src/tcp_output.c \ | |
72 | vendor/libslirp/src/tcp_subr.c \ | |
73 | vendor/libslirp/src/tcp_timer.c \ | |
74 | vendor/libslirp/src/tftp.c \ | |
75 | vendor/libslirp/src/udp6.c \ | |
76 | vendor/libslirp/src/udp.c \ | |
77 | vendor/libslirp/src/util.c \ | |
78 | vendor/libslirp/src/version.c \ | |
79 | vendor/libslirp/src/vmstate.c | |
80 | ||
81 | 23 | # define specific commit if git available or it was replaced during git-archive creation |
82 | COMMIT := $(shell V=5e1639783df9bd7abbc46abc084a53c1093342d8 ; \ | |
24 | COMMIT := $(shell V=6a7b16babc95b6a3056b33fb45b74a6f62262dd4 ; \ | |
83 | 25 | expr match "$$V" ormat: >/dev/null \ |
84 | 26 | && (cd "$$abs_srcdir" && [ -d .git ] && git describe --always --abbrev=0 --dirty --exclude=\* || echo unknown) \ |
85 | 27 | || echo "$$V" ) |
86 | 28 | DEFINE_COMMIT = -DCOMMIT="\"$(COMMIT)\"" |
87 | 29 | |
88 | 30 | slirp4netns_CFLAGS = $(AM_CFLAGS) $(DEFINE_COMMIT) |
89 | libslirp_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/libslirp/src | |
90 | 31 | libparson_a_CFLAGS = $(AM_CFLAGS) -I$(abs_top_builddir)/vendor/parson |
91 | 32 | libparson_a_SOURCES = vendor/parson/parson.c |
92 | 33 | |
93 | slirp4netns_SOURCES = main.c slirp4netns.c api.c | |
94 | slirp4netns_LDADD = libslirp.a libparson.a @GLIB_LIBS@ -lpthread | |
34 | slirp4netns_SOURCES = main.c slirp4netns.c api.c sandbox.c seccompfilter.c | |
35 | slirp4netns_LDADD = libparson.a @GLIB_LIBS@ @SLIRP_LIBS@ @LIBSECCOMP_LIBS@ -lpthread | |
95 | 36 | man1_MANS = slirp4netns.1 |
96 | 37 | |
97 | 38 | generate-man: |
99 | 40 | |
100 | 41 | CLANGTIDY = clang-tidy -warnings-as-errors='*' |
101 | 42 | |
43 | CLANGFORMAT = clang-format | |
44 | ||
102 | 45 | lint: |
103 | 46 | $(CLANGTIDY) $(slirp4netns_SOURCES) -- $(AM_CFLAGS) |
104 | 47 | |
105 | 48 | lint-full: |
106 | $(CLANGTIDY) $(slirp4netns_SOURCES) $(libslirp_a_SOURCES) $(libparson_a_SOURCES) -- $(AM_CFLAGS) | |
49 | $(CLANGTIDY) $(slirp4netns_SOURCES) $(libparson_a_SOURCES) -- $(AM_CFLAGS) | |
107 | 50 | |
108 | 51 | indent: |
109 | # indent(1): "You must use the ‘-T’ option to tell indent the name of all the typenames in your program that are defined by typedef." | |
110 | indent -linux -l120 \ | |
111 | -T ssize_t -T pid_t \ | |
112 | -T GArray -T GList -T GSList -T GPollFD -T gpointer -T gconstpointer \ | |
113 | -T Slirp -T SlirpCb -T SlirpConfig -T SlirpTimerCb \ | |
114 | -T JSON_Object -T JSON_Value \ | |
115 | -T regex_t -T regmatch_t \ | |
116 | $(slirp4netns_SOURCES) | |
117 | $(RM) *.c~ | |
118 | ||
52 | $(CLANGFORMAT) -i $(slirp4netns_SOURCES) | |
119 | 53 | |
120 | 54 | benchmark: |
121 | 55 | benchmarks/benchmark-iperf3.sh |
123 | 57 | |
124 | 58 | ci: |
125 | 59 | $(MAKE) indent |
126 | test -z "$(git diff)" | |
60 | git diff --exit-code | |
127 | 61 | # TODO: make sure ./vendor is synced with ./vendor.sh |
128 | # (hard to verify during `make`, because sync.sh removes ./vendor/libslirp/src/.deps) | |
129 | 62 | $(MAKE) lint |
130 | 63 | $(MAKE) -j $(shell nproc) distcheck || ( find . -name test-suite.log | xargs cat; exit 1 ) |
131 | 64 | PATH=$(shell pwd):$$PATH $(MAKE) benchmark MTU=1500 |
0 | 0 | # slirp4netns: User-mode networking for unprivileged network namespaces |
1 | 1 | |
2 | 2 | slirp4netns provides user-mode networking ("slirp") for unprivileged network namespaces. |
3 | ||
4 | Latest stable release: [v0.3.X](https://github.com/rootless-containers/slirp4netns/releases) | |
5 | 3 | |
6 | 4 | ## Motivation |
7 | 5 | |
12 | 10 | |
13 | 11 | ## Projects using slirp4netns |
14 | 12 | |
13 | Kubernetes distributions: | |
15 | 14 | * [Usernetes](https://github.com/rootless-containers/usernetes) (via RootlessKit) |
15 | * [k3s](https://k3s.io) (via RootlessKit) | |
16 | ||
17 | Container engines: | |
16 | 18 | * [Podman](https://github.com/containers/libpod) |
17 | 19 | * [Buildah](https://github.com/containers/buildah) |
18 | 20 | * [ctnr](https://github.com/mgoltzsche/ctnr) (via slirp-cni-plugin) |
19 | 21 | * [Docker & Moby](https://get.docker.com/rootless) (optionally, via RootlessKit) |
20 | 22 | |
23 | Tools: | |
21 | 24 | * [RootlessKit](https://github.com/rootless-containers/rootlesskit) |
22 | 25 | * [become-root](https://github.com/giuseppe/become-root) |
23 | 26 | * [slirp-cni-plugin](https://github.com/mgoltzsche/slirp-cni-plugin) |
24 | 27 | |
28 | ## Maintenance policy | |
29 | ||
30 | Version | Status | |
31 | -------------------------------|------------------------------------------------------------------------ | |
32 | v1.0.x | :white_check_mark: Active | |
33 | v0.4.x | :white_check_mark: Active (EOL: Sep 30, 2020) | |
34 | v0.3.x | :warning: End of Life (Mar 31, 2020) | |
35 | v0.2.x | :warning: End of Life (Aug 30, 2019) | |
36 | Early versions prior to v0.2.x | :warning: End of Life (Jan 5, 2019) | |
37 | ||
38 | See https://github.com/rootless-containers/slirp4netns/releases for the releases. | |
39 | ||
40 | See https://github.com/rootless-containers/slirp4netns/security/advisories for the past security advisories. | |
41 | ||
25 | 42 | ## Quick start |
26 | 43 | |
27 | 44 | ### Install from source |
28 | 45 | |
29 | Build dependency: `glib2-devel` (`libglib2.0-dev`) | |
46 | Build dependencies: | |
47 | * `glib2-devel` (`libglib2.0-dev`) | |
48 | * `libslirp-devel` >= 4.1 (`libslirp-dev`) | |
49 | * `libcap-devel` (`libcap-dev`) | |
50 | * `libseccomp-devel` (`libseccomp-dev`) | |
51 | ||
52 | Install steps: | |
30 | 53 | |
31 | 54 | ```console |
32 | 55 | $ ./autogen.sh |
44 | 67 | |
45 | 68 | ```console |
46 | 69 | $ sudo dnf install slirp4netns |
70 | ``` | |
71 | ||
72 | #### RHEL/CentOS 7.7 | |
73 | ||
74 | ```console | |
75 | $ sudo yum install slirp4netns | |
47 | 76 | ``` |
48 | 77 | |
49 | 78 | #### [RHEL/CentOS 7.6](https://copr.fedorainfracloud.org/coprs/vbatts/shadow-utils-newxidmap/) |
170 | 199 | The latest revision of slirp4netns is regularly benchmarked (`make benchmark`) on Travis: https://travis-ci.org/rootless-containers/slirp4netns |
171 | 200 | |
172 | 201 | ## Acknowledgement |
173 | ||
174 | 202 | See [`vendor/README.md`](./vendor/README.md). |
203 | ||
204 | ## License | |
205 | [GPL-2.0-or-later](COPYING) |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include <stdio.h> |
2 | 3 | #include <stdlib.h> |
3 | 4 | #include <unistd.h> |
4 | 5 | #include <sys/un.h> |
5 | 6 | #include <glib.h> |
7 | #include <libslirp.h> | |
6 | 8 | #include "vendor/parson/parson.h" |
7 | #include "vendor/libslirp/src/libslirp.h" | |
8 | 9 | #include "api.h" |
9 | 10 | #include "slirp4netns.h" |
10 | 11 | |
11 | 12 | int api_bindlisten(const char *api_socket) |
12 | 13 | { |
13 | int fd; | |
14 | struct sockaddr_un addr; | |
15 | unlink(api_socket); /* avoid EADDRINUSE */ | |
16 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
17 | perror("api_bindlisten: socket"); | |
18 | return -1; | |
19 | } | |
20 | memset(&addr, 0, sizeof(addr)); | |
21 | addr.sun_family = AF_UNIX; | |
22 | strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1); | |
23 | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
24 | perror("api_bindlisten: bind"); | |
25 | return -1; | |
26 | } | |
27 | if (listen(fd, 0) < 0) { | |
28 | perror("api_bindlisten: listen"); | |
29 | return -1; | |
30 | } | |
31 | return fd; | |
14 | int fd; | |
15 | struct sockaddr_un addr; | |
16 | unlink(api_socket); /* avoid EADDRINUSE */ | |
17 | if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
18 | perror("api_bindlisten: socket"); | |
19 | return -1; | |
20 | } | |
21 | memset(&addr, 0, sizeof(addr)); | |
22 | addr.sun_family = AF_UNIX; | |
23 | if (strlen(api_socket) >= sizeof(addr.sun_path)) { | |
24 | fprintf(stderr, "the specified API socket path is too long (>= %lu)\n", | |
25 | sizeof(addr.sun_path)); | |
26 | return -1; | |
27 | } | |
28 | strncpy(addr.sun_path, api_socket, sizeof(addr.sun_path) - 1); | |
29 | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
30 | perror("api_bindlisten: bind"); | |
31 | return -1; | |
32 | } | |
33 | if (listen(fd, 0) < 0) { | |
34 | perror("api_bindlisten: listen"); | |
35 | return -1; | |
36 | } | |
37 | return fd; | |
32 | 38 | } |
33 | 39 | |
34 | 40 | struct api_hostfwd { |
35 | int id; | |
36 | int is_udp; | |
37 | struct in_addr host_addr; | |
38 | int host_port; | |
39 | struct in_addr guest_addr; | |
40 | int guest_port; | |
41 | int id; | |
42 | int is_udp; | |
43 | struct in_addr host_addr; | |
44 | int host_port; | |
45 | struct in_addr guest_addr; | |
46 | int guest_port; | |
41 | 47 | }; |
42 | 48 | |
43 | 49 | struct api_ctx { |
44 | uint8_t *buf; | |
45 | size_t buflen; | |
46 | GList *hostfwds; | |
47 | int hostfwds_nextid; | |
48 | struct slirp4netns_config *cfg; | |
50 | uint8_t *buf; | |
51 | size_t buflen; | |
52 | GList *hostfwds; | |
53 | int hostfwds_nextid; | |
54 | struct slirp4netns_config *cfg; | |
49 | 55 | }; |
50 | 56 | |
51 | 57 | struct api_ctx *api_ctx_alloc(struct slirp4netns_config *cfg) |
52 | 58 | { |
53 | struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx)); | |
54 | if (ctx == NULL) { | |
55 | return NULL; | |
56 | } | |
57 | ctx->buflen = 4096; | |
58 | ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */ | |
59 | if (ctx->buf == NULL) { | |
60 | free(ctx); | |
61 | return NULL; | |
62 | } | |
63 | ctx->cfg = cfg; | |
64 | ctx->hostfwds = NULL; | |
65 | ctx->hostfwds_nextid = 1; | |
66 | return ctx; | |
59 | struct api_ctx *ctx = (struct api_ctx *)g_malloc0(sizeof(*ctx)); | |
60 | if (ctx == NULL) { | |
61 | return NULL; | |
62 | } | |
63 | ctx->buflen = 4096; | |
64 | ctx->buf = malloc(ctx->buflen); /* FIXME: realloc */ | |
65 | if (ctx->buf == NULL) { | |
66 | free(ctx); | |
67 | return NULL; | |
68 | } | |
69 | ctx->cfg = cfg; | |
70 | ctx->hostfwds = NULL; | |
71 | ctx->hostfwds_nextid = 1; | |
72 | return ctx; | |
67 | 73 | } |
68 | 74 | |
69 | 75 | void api_ctx_free(struct api_ctx *ctx) |
70 | 76 | { |
71 | if (ctx != NULL) { | |
72 | if (ctx->buf != NULL) { | |
73 | free(ctx->buf); | |
74 | } | |
75 | g_list_free_full(ctx->hostfwds, g_free); | |
76 | free(ctx); | |
77 | } | |
77 | if (ctx != NULL) { | |
78 | if (ctx->buf != NULL) { | |
79 | free(ctx->buf); | |
80 | } | |
81 | g_list_free_full(ctx->hostfwds, g_free); | |
82 | free(ctx); | |
83 | } | |
78 | 84 | } |
79 | 85 | |
80 | 86 | /* |
81 | 87 | Handler for add_hostfwd. |
82 | e.g. {"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr": "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}} | |
83 | This function returns the return value of write(2), not the return value of slirp_add_hostfwd(). | |
88 | e.g. {"execute": "add_hostfwd", "arguments": {"proto": "tcp", "host_addr": | |
89 | "0.0.0.0", "host_port": 8080, "guest_addr": "10.0.2.100", "guest_port": 80}} | |
90 | This function returns the return value of write(2), not the return value of | |
91 | slirp_add_hostfwd(). | |
84 | 92 | */ |
85 | static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) | |
86 | { | |
87 | int wrc = 0, slirprc = 0; | |
88 | int id = -1; | |
89 | char idbuf[64]; | |
90 | const char *proto_s = json_object_dotget_string(jo, "arguments.proto"); | |
91 | const char *host_addr_s = json_object_dotget_string(jo, "arguments.host_addr"); | |
92 | const char *guest_addr_s = json_object_dotget_string(jo, "arguments.guest_addr"); | |
93 | struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd)); | |
94 | if (fwd == NULL) { | |
95 | perror("fatal: malloc"); | |
96 | exit(EXIT_FAILURE); | |
97 | } | |
98 | fwd->is_udp = -1; /* TODO: support SCTP */ | |
99 | if (strcmp(proto_s, "udp") == 0) { | |
100 | fwd->is_udp = 1; | |
101 | } else if (strcmp(proto_s, "tcp") == 0) { | |
102 | fwd->is_udp = 0; | |
103 | } | |
104 | if (fwd->is_udp == -1) { | |
105 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.proto\"}}"; | |
106 | wrc = write(fd, err, strlen(err)); | |
107 | free(fwd); | |
108 | goto finish; | |
109 | } | |
110 | if (host_addr_s == NULL || host_addr_s[0] == '\0') { | |
111 | host_addr_s = "0.0.0.0"; | |
112 | } | |
113 | if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) { | |
114 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_addr\"}}"; | |
115 | wrc = write(fd, err, strlen(err)); | |
116 | free(fwd); | |
117 | goto finish; | |
118 | } | |
119 | fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port"); | |
120 | if (fwd->host_port == 0) { | |
121 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.host_port\"}}"; | |
122 | wrc = write(fd, err, strlen(err)); | |
123 | free(fwd); | |
124 | goto finish; | |
125 | } | |
126 | ||
127 | if (guest_addr_s == NULL || guest_addr_s[0] == '\0') { | |
128 | fwd->guest_addr = ctx->cfg->recommended_vguest; | |
129 | } else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) { | |
130 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_addr\"}}"; | |
131 | wrc = write(fd, err, strlen(err)); | |
132 | free(fwd); | |
133 | goto finish; | |
134 | } | |
135 | fwd->guest_port = (int)json_object_dotget_number(jo, "arguments.guest_port"); | |
136 | if (fwd->guest_port == 0) { | |
137 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: bad arguments.guest_port\"}}"; | |
138 | wrc = write(fd, err, strlen(err)); | |
139 | free(fwd); | |
140 | goto finish; | |
141 | } | |
142 | if ((slirprc = | |
143 | slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port, fwd->guest_addr, | |
144 | fwd->guest_port)) < 0) { | |
145 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: slirp_add_hostfwd failed\"}}"; | |
146 | wrc = write(fd, err, strlen(err)); | |
147 | free(fwd); | |
148 | goto finish; | |
149 | } | |
150 | fwd->id = ctx->hostfwds_nextid; | |
151 | ctx->hostfwds_nextid++; | |
152 | ctx->hostfwds = g_list_append(ctx->hostfwds, fwd); | |
153 | if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) > sizeof(idbuf)) { | |
154 | fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id); | |
155 | exit(EXIT_FAILURE); | |
156 | } | |
157 | wrc = write(fd, idbuf, strlen(idbuf)); | |
158 | finish: | |
159 | return wrc; | |
160 | } | |
161 | ||
162 | static void api_handle_req_list_hostfwd_foreach(gpointer data, gpointer user_data) | |
163 | { | |
164 | struct api_hostfwd *fwd = data; | |
165 | JSON_Array *entries_array = (JSON_Array *) user_data; | |
166 | JSON_Value *entry_value = json_value_init_object(); | |
167 | JSON_Object *entry_object = json_value_get_object(entry_value); | |
168 | char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN]; | |
169 | if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) == NULL) { | |
170 | perror("fatal: inet_ntop"); | |
171 | exit(EXIT_FAILURE); | |
172 | } | |
173 | if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) == NULL) { | |
174 | perror("fatal: inet_ntop"); | |
175 | exit(EXIT_FAILURE); | |
176 | } | |
177 | json_object_set_number(entry_object, "id", fwd->id); | |
178 | json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp"); | |
179 | json_object_set_string(entry_object, "host_addr", host_addr); | |
180 | json_object_set_number(entry_object, "host_port", fwd->host_port); | |
181 | json_object_set_string(entry_object, "guest_addr", guest_addr); | |
182 | json_object_set_number(entry_object, "guest_port", fwd->guest_port); | |
183 | /* json_array_append_value does not copy passed value */ | |
184 | if (json_array_append_value(entries_array, entry_value) != JSONSuccess) { | |
185 | fprintf(stderr, "fatal: json_array_append_value\n"); | |
186 | exit(EXIT_FAILURE); | |
187 | } | |
93 | static int api_handle_req_add_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, | |
94 | JSON_Object *jo) | |
95 | { | |
96 | int wrc = 0, slirprc = 0; | |
97 | char idbuf[64]; | |
98 | const char *proto_s = json_object_dotget_string(jo, "arguments.proto"); | |
99 | const char *host_addr_s = | |
100 | json_object_dotget_string(jo, "arguments.host_addr"); | |
101 | const char *guest_addr_s = | |
102 | json_object_dotget_string(jo, "arguments.guest_addr"); | |
103 | struct api_hostfwd *fwd = g_malloc0(sizeof(*fwd)); | |
104 | if (fwd == NULL) { | |
105 | perror("fatal: malloc"); | |
106 | exit(EXIT_FAILURE); | |
107 | } | |
108 | fwd->is_udp = -1; /* TODO: support SCTP */ | |
109 | if (strcmp(proto_s, "udp") == 0) { | |
110 | fwd->is_udp = 1; | |
111 | } else if (strcmp(proto_s, "tcp") == 0) { | |
112 | fwd->is_udp = 0; | |
113 | } | |
114 | if (fwd->is_udp == -1) { | |
115 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
116 | "bad arguments.proto\"}}"; | |
117 | wrc = write(fd, err, strlen(err)); | |
118 | free(fwd); | |
119 | goto finish; | |
120 | } | |
121 | if (host_addr_s == NULL || host_addr_s[0] == '\0') { | |
122 | host_addr_s = "0.0.0.0"; | |
123 | } | |
124 | if (inet_pton(AF_INET, host_addr_s, &fwd->host_addr) != 1) { | |
125 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
126 | "bad arguments.host_addr\"}}"; | |
127 | wrc = write(fd, err, strlen(err)); | |
128 | free(fwd); | |
129 | goto finish; | |
130 | } | |
131 | fwd->host_port = (int)json_object_dotget_number(jo, "arguments.host_port"); | |
132 | if (fwd->host_port == 0) { | |
133 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
134 | "bad arguments.host_port\"}}"; | |
135 | wrc = write(fd, err, strlen(err)); | |
136 | free(fwd); | |
137 | goto finish; | |
138 | } | |
139 | ||
140 | if (guest_addr_s == NULL || guest_addr_s[0] == '\0') { | |
141 | fwd->guest_addr = ctx->cfg->recommended_vguest; | |
142 | } else if (inet_pton(AF_INET, guest_addr_s, &fwd->guest_addr) != 1) { | |
143 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
144 | "bad arguments.guest_addr\"}}"; | |
145 | wrc = write(fd, err, strlen(err)); | |
146 | free(fwd); | |
147 | goto finish; | |
148 | } | |
149 | fwd->guest_port = | |
150 | (int)json_object_dotget_number(jo, "arguments.guest_port"); | |
151 | if (fwd->guest_port == 0) { | |
152 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
153 | "bad arguments.guest_port\"}}"; | |
154 | wrc = write(fd, err, strlen(err)); | |
155 | free(fwd); | |
156 | goto finish; | |
157 | } | |
158 | if ((slirprc = slirp_add_hostfwd(slirp, fwd->is_udp, fwd->host_addr, | |
159 | fwd->host_port, fwd->guest_addr, | |
160 | fwd->guest_port)) < 0) { | |
161 | const char *err = "{\"error\":{\"desc\":\"bad request: add_hostfwd: " | |
162 | "slirp_add_hostfwd failed\"}}"; | |
163 | wrc = write(fd, err, strlen(err)); | |
164 | free(fwd); | |
165 | goto finish; | |
166 | } | |
167 | fwd->id = ctx->hostfwds_nextid; | |
168 | ctx->hostfwds_nextid++; | |
169 | ctx->hostfwds = g_list_append(ctx->hostfwds, fwd); | |
170 | if (snprintf(idbuf, sizeof(idbuf), "{\"return\":{\"id\":%d}}", fwd->id) > | |
171 | sizeof(idbuf)) { | |
172 | fprintf(stderr, "fatal: unexpected id=%d\n", fwd->id); | |
173 | exit(EXIT_FAILURE); | |
174 | } | |
175 | wrc = write(fd, idbuf, strlen(idbuf)); | |
176 | finish: | |
177 | return wrc; | |
178 | } | |
179 | ||
180 | static void api_handle_req_list_hostfwd_foreach(gpointer data, | |
181 | gpointer user_data) | |
182 | { | |
183 | struct api_hostfwd *fwd = data; | |
184 | JSON_Array *entries_array = (JSON_Array *)user_data; | |
185 | JSON_Value *entry_value = json_value_init_object(); | |
186 | JSON_Object *entry_object = json_value_get_object(entry_value); | |
187 | char host_addr[INET_ADDRSTRLEN], guest_addr[INET_ADDRSTRLEN]; | |
188 | if (inet_ntop(AF_INET, &fwd->host_addr, host_addr, sizeof(host_addr)) == | |
189 | NULL) { | |
190 | perror("fatal: inet_ntop"); | |
191 | exit(EXIT_FAILURE); | |
192 | } | |
193 | if (inet_ntop(AF_INET, &fwd->guest_addr, guest_addr, sizeof(guest_addr)) == | |
194 | NULL) { | |
195 | perror("fatal: inet_ntop"); | |
196 | exit(EXIT_FAILURE); | |
197 | } | |
198 | json_object_set_number(entry_object, "id", fwd->id); | |
199 | json_object_set_string(entry_object, "proto", fwd->is_udp ? "udp" : "tcp"); | |
200 | json_object_set_string(entry_object, "host_addr", host_addr); | |
201 | json_object_set_number(entry_object, "host_port", fwd->host_port); | |
202 | json_object_set_string(entry_object, "guest_addr", guest_addr); | |
203 | json_object_set_number(entry_object, "guest_port", fwd->guest_port); | |
204 | /* json_array_append_value does not copy passed value */ | |
205 | if (json_array_append_value(entries_array, entry_value) != JSONSuccess) { | |
206 | fprintf(stderr, "fatal: json_array_append_value\n"); | |
207 | exit(EXIT_FAILURE); | |
208 | } | |
188 | 209 | } |
189 | 210 | |
190 | 211 | /* |
191 | 212 | Handler for list_hostfwd. |
192 | 213 | e.g. {"execute": "list_hostfwd"} |
193 | 214 | */ |
194 | static int api_handle_req_list_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) | |
195 | { | |
196 | int wrc = 0; | |
197 | struct api_hostfwd *fwd = NULL; | |
198 | JSON_Value *root_value = json_value_init_object(), *entries_value = json_value_init_array(); | |
199 | JSON_Object *root_object = json_value_get_object(root_value); | |
200 | JSON_Array *entries_array = json_array(entries_value); | |
201 | char *serialized_string = NULL; | |
202 | g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach, entries_array); | |
203 | json_object_set_value(root_object, "entries", entries_value); | |
204 | serialized_string = json_serialize_to_string(root_value); | |
205 | wrc = write(fd, serialized_string, strlen(serialized_string)); | |
206 | finish: | |
207 | json_free_serialized_string(serialized_string); | |
208 | json_value_free(root_value); | |
209 | return wrc; | |
210 | } | |
211 | ||
212 | static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd, gconstpointer gcp_id) | |
213 | { | |
214 | struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd; | |
215 | int id = *(int *)gcp_id; | |
216 | return id == fwd->id ? 0 : 1; | |
215 | static int api_handle_req_list_hostfwd(Slirp *slirp, int fd, | |
216 | struct api_ctx *ctx, JSON_Object *jo) | |
217 | { | |
218 | int wrc = 0; | |
219 | JSON_Value *root_value = json_value_init_object(), | |
220 | *entries_value = json_value_init_array(); | |
221 | JSON_Object *root_object = json_value_get_object(root_value); | |
222 | JSON_Array *entries_array = json_array(entries_value); | |
223 | char *serialized_string = NULL; | |
224 | g_list_foreach(ctx->hostfwds, api_handle_req_list_hostfwd_foreach, | |
225 | entries_array); | |
226 | json_object_set_value(root_object, "entries", entries_value); | |
227 | serialized_string = json_serialize_to_string(root_value); | |
228 | wrc = write(fd, serialized_string, strlen(serialized_string)); | |
229 | json_free_serialized_string(serialized_string); | |
230 | json_value_free(root_value); | |
231 | return wrc; | |
232 | } | |
233 | ||
234 | static int api_handle_remove_list_hostfwd_find(gconstpointer gcp_fwd, | |
235 | gconstpointer gcp_id) | |
236 | { | |
237 | struct api_hostfwd *fwd = (struct api_hostfwd *)gcp_fwd; | |
238 | int id = *(int *)gcp_id; | |
239 | return id == fwd->id ? 0 : 1; | |
217 | 240 | } |
218 | 241 | |
219 | 242 | /* |
220 | 243 | Handler for remove_hostfwd. |
221 | 244 | e.g. {"execute": "remove_hostfwd", "arguments": {"id": 42}} |
222 | 245 | */ |
223 | static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd, struct api_ctx *ctx, JSON_Object *jo) | |
224 | { | |
225 | int wrc = 0; | |
226 | int id = (int)json_object_dotget_number(jo, "arguments.id"); | |
227 | GList *found = g_list_find_custom(ctx->hostfwds, &id, api_handle_remove_list_hostfwd_find); | |
228 | if (found == NULL) { | |
229 | const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: bad arguments.id\"}}"; | |
230 | wrc = write(fd, err, strlen(err)); | |
231 | } else { | |
232 | struct api_hostfwd *fwd = found->data; | |
233 | const char *api_ok = "{\"return\":{}}"; | |
234 | if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr, fwd->host_port) < 0) { | |
235 | const char *err = | |
236 | "{\"error\":{\"desc\":\"bad request: remove_hostfwd: slirp_remove_hostfwd failed\"}}"; | |
237 | wrc = write(fd, err, strlen(err)); | |
238 | } else { | |
239 | ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd); | |
240 | g_free(fwd); | |
241 | wrc = write(fd, api_ok, strlen(api_ok)); | |
242 | } | |
243 | } | |
244 | return wrc; | |
246 | static int api_handle_req_remove_hostfwd(Slirp *slirp, int fd, | |
247 | struct api_ctx *ctx, JSON_Object *jo) | |
248 | { | |
249 | int wrc = 0; | |
250 | int id = (int)json_object_dotget_number(jo, "arguments.id"); | |
251 | GList *found = g_list_find_custom(ctx->hostfwds, &id, | |
252 | api_handle_remove_list_hostfwd_find); | |
253 | if (found == NULL) { | |
254 | const char *err = "{\"error\":{\"desc\":\"bad request: remove_hostfwd: " | |
255 | "bad arguments.id\"}}"; | |
256 | wrc = write(fd, err, strlen(err)); | |
257 | } else { | |
258 | struct api_hostfwd *fwd = found->data; | |
259 | const char *api_ok = "{\"return\":{}}"; | |
260 | if (slirp_remove_hostfwd(slirp, fwd->is_udp, fwd->host_addr, | |
261 | fwd->host_port) < 0) { | |
262 | const char *err = "{\"error\":{\"desc\":\"bad request: " | |
263 | "remove_hostfwd: slirp_remove_hostfwd failed\"}}"; | |
264 | wrc = write(fd, err, strlen(err)); | |
265 | } else { | |
266 | ctx->hostfwds = g_list_remove(ctx->hostfwds, fwd); | |
267 | g_free(fwd); | |
268 | wrc = write(fd, api_ok, strlen(api_ok)); | |
269 | } | |
270 | } | |
271 | return wrc; | |
245 | 272 | } |
246 | 273 | |
247 | 274 | static int api_handle_req(Slirp *slirp, int fd, struct api_ctx *ctx) |
248 | 275 | { |
249 | JSON_Value *jv = NULL; | |
250 | JSON_Object *jo = NULL; | |
251 | const char *execute = NULL; | |
252 | int wrc = 0; | |
253 | if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) { | |
254 | const char *err = "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}"; | |
255 | wrc = write(fd, err, strlen(err)); | |
256 | goto finish; | |
257 | } | |
258 | if ((jo = json_object(jv)) == NULL) { | |
259 | const char *err = "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}"; | |
260 | wrc = write(fd, err, strlen(err)); | |
261 | goto finish; | |
262 | } | |
263 | /* TODO: json_validate */ | |
264 | if ((execute = json_object_get_string(jo, "execute")) == NULL) { | |
265 | const char *err = "{\"error\":{\"desc\":\"bad request: no execute found\"}}"; | |
266 | wrc = write(fd, err, strlen(err)); | |
267 | goto finish; | |
268 | } | |
269 | if ((strcmp(execute, "add_hostfwd")) == 0) { | |
270 | wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo); | |
271 | } else if ((strcmp(execute, "list_hostfwd")) == 0) { | |
272 | wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo); | |
273 | } else if ((strcmp(execute, "remove_hostfwd")) == 0) { | |
274 | wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo); | |
275 | } else { | |
276 | const char *err = "{\"error\":{\"desc\":\"bad request: unknown execute\"}}"; | |
277 | wrc = write(fd, err, strlen(err)); | |
278 | goto finish; | |
279 | } | |
280 | finish: | |
281 | if (jv != NULL) { | |
282 | json_value_free(jv); | |
283 | } | |
284 | return wrc; | |
276 | JSON_Value *jv = NULL; | |
277 | JSON_Object *jo = NULL; | |
278 | const char *execute = NULL; | |
279 | int wrc = 0; | |
280 | if ((jv = json_parse_string((const char *)ctx->buf)) == NULL) { | |
281 | const char *err = | |
282 | "{\"error\":{\"desc\":\"bad request: cannot parse JSON\"}}"; | |
283 | wrc = write(fd, err, strlen(err)); | |
284 | goto finish; | |
285 | } | |
286 | if ((jo = json_object(jv)) == NULL) { | |
287 | const char *err = | |
288 | "{\"error\":{\"desc\":\"bad request: json_object() failed\"}}"; | |
289 | wrc = write(fd, err, strlen(err)); | |
290 | goto finish; | |
291 | } | |
292 | /* TODO: json_validate */ | |
293 | if ((execute = json_object_get_string(jo, "execute")) == NULL) { | |
294 | const char *err = | |
295 | "{\"error\":{\"desc\":\"bad request: no execute found\"}}"; | |
296 | wrc = write(fd, err, strlen(err)); | |
297 | goto finish; | |
298 | } | |
299 | if ((strcmp(execute, "add_hostfwd")) == 0) { | |
300 | wrc = api_handle_req_add_hostfwd(slirp, fd, ctx, jo); | |
301 | } else if ((strcmp(execute, "list_hostfwd")) == 0) { | |
302 | wrc = api_handle_req_list_hostfwd(slirp, fd, ctx, jo); | |
303 | } else if ((strcmp(execute, "remove_hostfwd")) == 0) { | |
304 | wrc = api_handle_req_remove_hostfwd(slirp, fd, ctx, jo); | |
305 | } else { | |
306 | const char *err = | |
307 | "{\"error\":{\"desc\":\"bad request: unknown execute\"}}"; | |
308 | wrc = write(fd, err, strlen(err)); | |
309 | goto finish; | |
310 | } | |
311 | finish: | |
312 | if (jv != NULL) { | |
313 | json_value_free(jv); | |
314 | } | |
315 | return wrc; | |
285 | 316 | } |
286 | 317 | |
287 | 318 | /* |
290 | 321 | */ |
291 | 322 | int api_handler(Slirp *slirp, int listenfd, struct api_ctx *ctx) |
292 | 323 | { |
293 | struct sockaddr_un addr; | |
294 | socklen_t addrlen = sizeof(struct sockaddr_un); | |
295 | int fd; | |
296 | int rc = 0, wrc = 0; | |
297 | ssize_t len; | |
298 | memset(&addr, 0, sizeof(addr)); | |
299 | if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) { | |
300 | perror("api_handler: accept"); | |
301 | return -1; | |
302 | } | |
303 | if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) { | |
304 | perror("api_handler: read"); | |
305 | rc = len; | |
306 | goto finish; | |
307 | } | |
308 | if (len == ctx->buflen) { | |
309 | const char *err = "{\"error\":{\"desc\":\"bad request: too large message\"}}"; | |
310 | fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len); | |
311 | wrc = write(fd, err, strlen(err)); | |
312 | rc = -1; | |
313 | goto finish; | |
314 | } | |
315 | ctx->buf[len] = 0; | |
316 | fprintf(stderr, "api_handler: got request: %s\n", ctx->buf); | |
317 | wrc = api_handle_req(slirp, fd, ctx); | |
318 | finish: | |
319 | shutdown(fd, SHUT_RDWR); | |
320 | if (rc == 0 && wrc != 0) { | |
321 | rc = wrc; | |
322 | } | |
323 | close(fd); | |
324 | return rc; | |
325 | } | |
324 | struct sockaddr_un addr; | |
325 | socklen_t addrlen = sizeof(struct sockaddr_un); | |
326 | int fd; | |
327 | int rc = 0, wrc = 0; | |
328 | ssize_t len; | |
329 | memset(&addr, 0, sizeof(addr)); | |
330 | if ((fd = accept(listenfd, (struct sockaddr *)&addr, &addrlen)) < 0) { | |
331 | perror("api_handler: accept"); | |
332 | return -1; | |
333 | } | |
334 | if ((len = read(fd, ctx->buf, ctx->buflen)) < 0) { | |
335 | perror("api_handler: read"); | |
336 | rc = len; | |
337 | goto finish; | |
338 | } | |
339 | if (len == ctx->buflen) { | |
340 | const char *err = | |
341 | "{\"error\":{\"desc\":\"bad request: too large message\"}}"; | |
342 | fprintf(stderr, "api_handler: too large message (>= %ld bytes)\n", len); | |
343 | wrc = write(fd, err, strlen(err)); | |
344 | rc = -1; | |
345 | goto finish; | |
346 | } | |
347 | ctx->buf[len] = 0; | |
348 | fprintf(stderr, "api_handler: got request: %s\n", ctx->buf); | |
349 | wrc = api_handle_req(slirp, fd, ctx); | |
350 | finish: | |
351 | shutdown(fd, SHUT_RDWR); | |
352 | if (rc == 0 && wrc != 0) { | |
353 | rc = wrc; | |
354 | } | |
355 | close(fd); | |
356 | return rc; | |
357 | } |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #ifndef SLIRP4NETNS_API_H |
1 | 2 | # define SLIRP4NETNS_API_H |
2 | 3 | int api_bindlisten(const char *api_socket); |
0 | 0 | AC_PREREQ([2.69]) |
1 | AC_INIT([slirp4netns], [0.3.2], [https://github.com/rootless-containers/slirp4netns/issues]) | |
1 | AC_INIT([slirp4netns], [1.0.1], [https://github.com/rootless-containers/slirp4netns/issues]) | |
2 | 2 | AC_CONFIG_SRCDIR([main.c]) |
3 | 3 | AC_CONFIG_HEADERS([config.h]) |
4 | 4 | |
7 | 7 | |
8 | 8 | AM_INIT_AUTOMAKE([1.9 foreign subdir-objects]) |
9 | 9 | |
10 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/timeb.h unistd.h getopt.h]) | |
10 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/mount.h sys/socket.h sys/timeb.h unistd.h getopt.h]) | |
11 | 11 | |
12 | 12 | AC_CHECK_HEADER_STDBOOL |
13 | 13 | AC_C_INLINE |
24 | 24 | AC_TYPE_UINT8_T |
25 | 25 | |
26 | 26 | AC_FUNC_ALLOCA |
27 | AC_FUNC_STRTOD | |
27 | 28 | AC_FUNC_FORK |
28 | 29 | AC_FUNC_MALLOC |
29 | 30 | AC_FUNC_REALLOC |
30 | AC_CHECK_FUNCS([atexit clock_gettime inet_ntoa memmove memset socket strchr strdup strerror strstr strtol getopt_long]) | |
31 | AC_CHECK_FUNCS([atexit clock_gettime dup2 getopt_long inet_ntoa memmove memset mkdir regcomp rmdir socket strcasecmp strchr strdup strerror strstr strtol strtoul]) | |
31 | 32 | |
32 | 33 | PKG_CHECK_MODULES(GLIB, glib-2.0) |
34 | PKG_CHECK_MODULES(SLIRP, slirp >= 4.1.0) | |
35 | PKG_CHECK_MODULES(LIBCAP, libcap) | |
36 | PKG_CHECK_MODULES(LIBSECCOMP, libseccomp) | |
33 | 37 | |
34 | 38 | AC_CONFIG_FILES([Makefile]) |
35 | 39 | AC_OUTPUT |
0 | #!/bin/bash | |
1 | # release.sh: configurable signed-artefact release script | |
2 | # Copyright (C) 2016-2019 SUSE LLC. | |
3 | # | |
4 | # This Source Code Form is subject to the terms of the Mozilla Public | |
5 | # License, v2.0. If a copy of the MPL was not distributed with this | |
6 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
7 | ||
8 | set -Eeuo pipefail | |
9 | ||
10 | ## ---> | |
11 | # Project-specific options and functions. In *theory* you shouldn't need to | |
12 | # touch anything else in this script in order to use this elsewhere. | |
13 | project="slirp4netns" | |
14 | root="$(readlink -f "$(dirname "${BASH_SOURCE}")/..")" | |
15 | ||
16 | # Make pushd and popd silent. | |
17 | function pushd() { command pushd "$@" &>/dev/null ; } | |
18 | function popd() { command popd "$@" &>/dev/null ; } | |
19 | ||
20 | # These functions allow you to configure how the defaults are computed. | |
21 | function get_arch() { uname -m ; } | |
22 | function get_version() { echo '@VERSION@' | "$root/config.status" --file - ; } | |
23 | ||
24 | # Any pre-configuration steps should be done here -- for instance ./configure. | |
25 | function setup_project() { | |
26 | pushd "$root" | |
27 | [ -x ./configure ] || ./autogen.sh | |
28 | ./configure LDFLAGS="-static" --prefix=/ --bindir=/bin | |
29 | popd | |
30 | } | |
31 | ||
32 | # This function takes an output path as an argument, where the built | |
33 | # (preferably static) binary should be placed. | |
34 | function build_project() { | |
35 | tmprootfs="$(mktemp -d --tmpdir "$project-build.XXXXXX")" | |
36 | ||
37 | make -C "$root" clean all install DESTDIR="$tmprootfs" | |
38 | ||
39 | mv "$tmprootfs/bin/slirp4netns" "$1" | |
40 | rm -rf "$tmprootfs" | |
41 | } | |
42 | # End of the easy-to-configure portion. | |
43 | ## <--- | |
44 | ||
45 | # Print usage information. | |
46 | function usage() { | |
47 | echo "usage: release.sh [-h] [-v <version>] [-c <commit>] [-o <output-dir>]" >&2 | |
48 | echo " [-H <hashcmd>] [-S <gpg-key>]" >&2 | |
49 | } | |
50 | ||
51 | # Log something to stderr. | |
52 | function log() { | |
53 | echo "[*]" "$@" >&2 | |
54 | } | |
55 | ||
56 | # Log something to stderr and then exit with 0. | |
57 | function quit() { | |
58 | log "$@" | |
59 | exit 0 | |
60 | } | |
61 | ||
62 | # Conduct a sanity-check to make sure that GPG provided with the given | |
63 | # arguments can sign something. Inability to sign things is not a fatal error. | |
64 | function gpg_cansign() { | |
65 | gpg "$@" --clear-sign </dev/null >/dev/null | |
66 | } | |
67 | ||
68 | # When creating releases we need to build (ideally static) binaries, an archive | |
69 | # of the current commit, and generate detached signatures for both. | |
70 | keyid="" | |
71 | version="" | |
72 | arch="" | |
73 | commit="HEAD" | |
74 | hashcmd="sha256sum" | |
75 | while getopts ":h:v:c:o:S:H:" opt; do | |
76 | case "$opt" in | |
77 | S) | |
78 | keyid="$OPTARG" | |
79 | ;; | |
80 | c) | |
81 | commit="$OPTARG" | |
82 | ;; | |
83 | o) | |
84 | outputdir="$OPTARG" | |
85 | ;; | |
86 | v) | |
87 | version="$OPTARG" | |
88 | ;; | |
89 | H) | |
90 | hashcmd="$OPTARG" | |
91 | ;; | |
92 | h) | |
93 | usage ; exit 0 | |
94 | ;; | |
95 | \:) | |
96 | echo "Missing argument: -$OPTARG" >&2 | |
97 | usage ; exit 1 | |
98 | ;; | |
99 | \?) | |
100 | echo "Invalid option: -$OPTARG" >&2 | |
101 | usage ; exit 1 | |
102 | ;; | |
103 | esac | |
104 | done | |
105 | ||
106 | # Run project setup first... | |
107 | ( set -x ; setup_project ) | |
108 | ||
109 | # Generate the defaults for version and so on *after* argument parsing and | |
110 | # setup_project, to avoid calling get_version() needlessly. | |
111 | version="${version:-$(get_version)}" | |
112 | arch="${arch:-$(get_arch)}" | |
113 | outputdir="${outputdir:-release/$version}" | |
114 | ||
115 | log "[[ $project ]]" | |
116 | log "version: $version" | |
117 | log "commit: $commit" | |
118 | log "output_dir: $outputdir" | |
119 | log "key: ${keyid:-(default)}" | |
120 | log "hash_cmd: $hashcmd" | |
121 | ||
122 | # Make explicit what we're doing. | |
123 | set -x | |
124 | ||
125 | # Make the release directory. | |
126 | rm -rf "$outputdir" && mkdir -p "$outputdir" | |
127 | ||
128 | # Build project. | |
129 | build_project "$outputdir/$project.$arch" | |
130 | ||
131 | # Generate new archive. | |
132 | git archive --format=tar --prefix="$project-$version/" "$commit" | xz > "$outputdir/$project.tar.xz" | |
133 | ||
134 | # Generate sha256 checksums for both. | |
135 | ( cd "$outputdir" ; "$hashcmd" "$project".{"$arch",tar.xz} > "$project.$hashcmd" ; ) | |
136 | ||
137 | # Set up the gpgflags. | |
138 | gpgflags=() | |
139 | [[ -z "$keyid" ]] || gpgflags+=("--default-key=$keyid") | |
140 | gpg_cansign "${gpgflags[@]}" || quit "Could not find suitable GPG key, skipping signing step." | |
141 | ||
142 | # Sign everything. | |
143 | gpg "${gpgflags[@]}" --detach-sign --armor "$outputdir/$project.$arch" | |
144 | gpg "${gpgflags[@]}" --detach-sign --armor "$outputdir/$project.tar.xz" | |
145 | gpg "${gpgflags[@]}" --clear-sign --armor \ | |
146 | --output "$outputdir/$project.$hashcmd"{.tmp,} && \ | |
147 | mv "$outputdir/$project.$hashcmd"{.tmp,} |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include "config.h" |
2 | 3 | #include <stdio.h> |
17 | 18 | #include <getopt.h> |
18 | 19 | #include <stdbool.h> |
19 | 20 | #include <regex.h> |
21 | #include <libslirp.h> | |
20 | 22 | #include "slirp4netns.h" |
21 | 23 | |
22 | 24 | #define DEFAULT_MTU (1500) |
23 | 25 | #define DEFAULT_CIDR ("10.0.2.0/24") |
24 | #define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2 | |
25 | #define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15 | |
26 | #define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3 | |
27 | #define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100 | |
26 | #define DEFAULT_VHOST_OFFSET (2) // 10.0.2.2 | |
27 | #define DEFAULT_VDHCPSTART_OFFSET (15) // 10.0.2.15 | |
28 | #define DEFAULT_VNAMESERVER_OFFSET (3) // 10.0.2.3 | |
29 | #define DEFAULT_RECOMMENDED_VGUEST_OFFSET (100) // 10.0.2.100 | |
30 | #define DEFAULT_NETNS_TYPE ("pid") | |
28 | 31 | #define NETWORK_PREFIX_MIN (1) |
29 | // >=26 is not supported because the recommended guest IP is set to network addr + 100 . | |
32 | // >=26 is not supported because the recommended guest IP is set to network addr | |
33 | // + 100 . | |
30 | 34 | #define NETWORK_PREFIX_MAX (25) |
31 | 35 | |
32 | static int nsenter(pid_t target_pid) | |
33 | { | |
34 | char userns[32], netns[32]; | |
35 | int usernsfd, netnsfd; | |
36 | snprintf(userns, sizeof(userns), "/proc/%d/ns/user", target_pid); | |
37 | snprintf(netns, sizeof(netns), "/proc/%d/ns/net", target_pid); | |
38 | if ((usernsfd = open(userns, O_RDONLY)) < 0) { | |
39 | perror(userns); | |
40 | return usernsfd; | |
41 | } | |
42 | if ((netnsfd = open(netns, O_RDONLY)) < 0) { | |
43 | perror(netns); | |
44 | return netnsfd; | |
45 | } | |
46 | setns(usernsfd, CLONE_NEWUSER); | |
47 | if (setns(netnsfd, CLONE_NEWNET) < 0) { | |
48 | perror("setns(CLONE_NEWNET)"); | |
49 | return -1; | |
50 | } | |
51 | close(usernsfd); | |
52 | close(netnsfd); | |
53 | return 0; | |
36 | static int nsenter(pid_t target_pid, char *netns, char *userns, | |
37 | bool only_userns) | |
38 | { | |
39 | int usernsfd = -1, netnsfd = -1; | |
40 | if (!only_userns && !netns) { | |
41 | if (asprintf(&netns, "/proc/%d/ns/net", target_pid) < 0) { | |
42 | perror("cannot get netns path"); | |
43 | return -1; | |
44 | } | |
45 | } | |
46 | if (!userns && target_pid) { | |
47 | if (asprintf(&userns, "/proc/%d/ns/user", target_pid) < 0) { | |
48 | perror("cannot get userns path"); | |
49 | return -1; | |
50 | } | |
51 | } | |
52 | if (!only_userns && (netnsfd = open(netns, O_RDONLY)) < 0) { | |
53 | perror(netns); | |
54 | return netnsfd; | |
55 | } | |
56 | if (userns && (usernsfd = open(userns, O_RDONLY)) < 0) { | |
57 | perror(userns); | |
58 | return usernsfd; | |
59 | } | |
60 | ||
61 | if (usernsfd != -1) { | |
62 | int r = setns(usernsfd, CLONE_NEWUSER); | |
63 | if (only_userns && r < 0) { | |
64 | perror("setns(CLONE_NEWUSER)"); | |
65 | return -1; | |
66 | } | |
67 | close(usernsfd); | |
68 | } | |
69 | if (netnsfd != -1 && setns(netnsfd, CLONE_NEWNET) < 0) { | |
70 | perror("setns(CLONE_NEWNET)"); | |
71 | return -1; | |
72 | } | |
73 | close(netnsfd); | |
74 | return 0; | |
54 | 75 | } |
55 | 76 | |
56 | 77 | static int open_tap(const char *tapname) |
57 | 78 | { |
58 | int fd; | |
59 | struct ifreq ifr; | |
60 | if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { | |
61 | perror("open(\"/dev/net/tun\")"); | |
62 | return fd; | |
63 | } | |
64 | memset(&ifr, 0, sizeof(ifr)); | |
65 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | |
66 | strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); | |
67 | if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { | |
68 | perror("ioctl(TUNSETIFF)"); | |
69 | close(fd); | |
70 | return -1; | |
71 | } | |
72 | return fd; | |
79 | int fd; | |
80 | struct ifreq ifr; | |
81 | if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { | |
82 | perror("open(\"/dev/net/tun\")"); | |
83 | return fd; | |
84 | } | |
85 | memset(&ifr, 0, sizeof(ifr)); | |
86 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; | |
87 | strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); | |
88 | if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) { | |
89 | perror("ioctl(TUNSETIFF)"); | |
90 | close(fd); | |
91 | return -1; | |
92 | } | |
93 | return fd; | |
73 | 94 | } |
74 | 95 | |
75 | 96 | static int sendfd(int sock, int fd) |
76 | 97 | { |
77 | ssize_t rc; | |
78 | struct msghdr msg; | |
79 | struct cmsghdr *cmsg; | |
80 | char cmsgbuf[CMSG_SPACE(sizeof(fd))]; | |
81 | struct iovec iov; | |
82 | char dummy = '\0'; | |
83 | memset(&msg, 0, sizeof(msg)); | |
84 | iov.iov_base = &dummy; | |
85 | iov.iov_len = 1; | |
86 | msg.msg_iov = &iov; | |
87 | msg.msg_iovlen = 1; | |
88 | msg.msg_control = cmsgbuf; | |
89 | msg.msg_controllen = sizeof(cmsgbuf); | |
90 | cmsg = CMSG_FIRSTHDR(&msg); | |
91 | cmsg->cmsg_level = SOL_SOCKET; | |
92 | cmsg->cmsg_type = SCM_RIGHTS; | |
93 | cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); | |
94 | memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); | |
95 | msg.msg_controllen = cmsg->cmsg_len; | |
96 | if ((rc = sendmsg(sock, &msg, 0)) < 0) { | |
97 | perror("sendmsg"); | |
98 | } | |
99 | return rc; | |
100 | } | |
101 | ||
102 | static int configure_network(const char *tapname, struct slirp4netns_config *cfg) | |
103 | { | |
104 | struct rtentry route; | |
105 | struct ifreq ifr; | |
106 | struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr; | |
107 | int sockfd; | |
108 | ||
109 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); | |
110 | if (sockfd < 0) { | |
111 | perror("cannot create socket"); | |
112 | return -1; | |
113 | } | |
114 | ||
115 | memset(&ifr, 0, sizeof(ifr)); | |
116 | ifr.ifr_flags = IFF_UP | IFF_RUNNING; | |
117 | strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); | |
118 | ||
119 | if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { | |
120 | perror("cannot set device up"); | |
121 | return -1; | |
122 | } | |
123 | ||
124 | ifr.ifr_mtu = (int)cfg->mtu; | |
125 | if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { | |
126 | perror("cannot set MTU"); | |
127 | return -1; | |
128 | } | |
129 | ||
130 | sai->sin_family = AF_INET; | |
131 | sai->sin_port = 0; | |
132 | sai->sin_addr = cfg->recommended_vguest; | |
133 | ||
134 | if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { | |
135 | perror("cannot set device address"); | |
136 | return -1; | |
137 | } | |
138 | ||
139 | sai->sin_addr = cfg->vnetmask; | |
140 | if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { | |
141 | perror("cannot set device netmask"); | |
142 | return -1; | |
143 | } | |
144 | ||
145 | memset(&route, 0, sizeof(route)); | |
146 | sai = (struct sockaddr_in *)&route.rt_gateway; | |
147 | sai->sin_family = AF_INET; | |
148 | sai->sin_addr = cfg->vhost; | |
149 | sai = (struct sockaddr_in *)&route.rt_dst; | |
150 | sai->sin_family = AF_INET; | |
151 | sai->sin_addr.s_addr = INADDR_ANY; | |
152 | sai = (struct sockaddr_in *)&route.rt_genmask; | |
153 | sai->sin_family = AF_INET; | |
154 | sai->sin_addr.s_addr = INADDR_ANY; | |
155 | ||
156 | route.rt_flags = RTF_UP | RTF_GATEWAY; | |
157 | route.rt_metric = 0; | |
158 | route.rt_dev = (char *)tapname; | |
159 | ||
160 | if (ioctl(sockfd, SIOCADDRT, &route) < 0) { | |
161 | perror("set route"); | |
162 | return -1; | |
163 | } | |
164 | return 0; | |
165 | } | |
166 | ||
167 | static int child(int sock, pid_t target_pid, bool do_config_network, const char *tapname, int ready_fd, | |
168 | struct slirp4netns_config *cfg) | |
169 | { | |
170 | int rc, tapfd; | |
171 | if ((rc = nsenter(target_pid)) < 0) { | |
172 | return rc; | |
173 | } | |
174 | if ((tapfd = open_tap(tapname)) < 0) { | |
175 | return tapfd; | |
176 | } | |
177 | if (do_config_network && configure_network(tapname, cfg) < 0) { | |
178 | return -1; | |
179 | } | |
180 | if (ready_fd >= 0) { | |
181 | do | |
182 | rc = write(ready_fd, "1", 1); | |
183 | while (rc < 0 && errno == EINTR); | |
184 | close(ready_fd); | |
185 | } | |
186 | if (sendfd(sock, tapfd) < 0) { | |
187 | close(tapfd); | |
188 | close(sock); | |
189 | return -1; | |
190 | } | |
191 | fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname); | |
192 | close(sock); | |
193 | return 0; | |
98 | ssize_t rc; | |
99 | struct msghdr msg; | |
100 | struct cmsghdr *cmsg; | |
101 | char cmsgbuf[CMSG_SPACE(sizeof(fd))]; | |
102 | struct iovec iov; | |
103 | char dummy = '\0'; | |
104 | memset(&msg, 0, sizeof(msg)); | |
105 | iov.iov_base = &dummy; | |
106 | iov.iov_len = 1; | |
107 | msg.msg_iov = &iov; | |
108 | msg.msg_iovlen = 1; | |
109 | msg.msg_control = cmsgbuf; | |
110 | msg.msg_controllen = sizeof(cmsgbuf); | |
111 | cmsg = CMSG_FIRSTHDR(&msg); | |
112 | cmsg->cmsg_level = SOL_SOCKET; | |
113 | cmsg->cmsg_type = SCM_RIGHTS; | |
114 | cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); | |
115 | memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); | |
116 | msg.msg_controllen = cmsg->cmsg_len; | |
117 | if ((rc = sendmsg(sock, &msg, 0)) < 0) { | |
118 | perror("sendmsg"); | |
119 | } | |
120 | return rc; | |
121 | } | |
122 | ||
123 | static int configure_network(const char *tapname, | |
124 | struct slirp4netns_config *cfg) | |
125 | { | |
126 | struct rtentry route; | |
127 | struct ifreq ifr; | |
128 | struct sockaddr_in *sai = (struct sockaddr_in *)&ifr.ifr_addr; | |
129 | int sockfd; | |
130 | ||
131 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); | |
132 | if (sockfd < 0) { | |
133 | perror("cannot create socket"); | |
134 | return -1; | |
135 | } | |
136 | ||
137 | // set loopback device to UP | |
138 | struct ifreq ifr_lo = { .ifr_name = "lo", | |
139 | .ifr_flags = IFF_UP | IFF_RUNNING }; | |
140 | if (ioctl(sockfd, SIOCSIFFLAGS, &ifr_lo) < 0) { | |
141 | perror("cannot set device up"); | |
142 | return -1; | |
143 | } | |
144 | ||
145 | memset(&ifr, 0, sizeof(ifr)); | |
146 | ifr.ifr_flags = IFF_UP | IFF_RUNNING; | |
147 | strncpy(ifr.ifr_name, tapname, sizeof(ifr.ifr_name) - 1); | |
148 | ||
149 | if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) { | |
150 | perror("cannot set device up"); | |
151 | return -1; | |
152 | } | |
153 | ||
154 | ifr.ifr_mtu = (int)cfg->mtu; | |
155 | if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0) { | |
156 | perror("cannot set MTU"); | |
157 | return -1; | |
158 | } | |
159 | ||
160 | sai->sin_family = AF_INET; | |
161 | sai->sin_port = 0; | |
162 | sai->sin_addr = cfg->recommended_vguest; | |
163 | ||
164 | if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) { | |
165 | perror("cannot set device address"); | |
166 | return -1; | |
167 | } | |
168 | ||
169 | sai->sin_addr = cfg->vnetmask; | |
170 | if (ioctl(sockfd, SIOCSIFNETMASK, &ifr) < 0) { | |
171 | perror("cannot set device netmask"); | |
172 | return -1; | |
173 | } | |
174 | ||
175 | memset(&route, 0, sizeof(route)); | |
176 | sai = (struct sockaddr_in *)&route.rt_gateway; | |
177 | sai->sin_family = AF_INET; | |
178 | sai->sin_addr = cfg->vhost; | |
179 | sai = (struct sockaddr_in *)&route.rt_dst; | |
180 | sai->sin_family = AF_INET; | |
181 | sai->sin_addr.s_addr = INADDR_ANY; | |
182 | sai = (struct sockaddr_in *)&route.rt_genmask; | |
183 | sai->sin_family = AF_INET; | |
184 | sai->sin_addr.s_addr = INADDR_ANY; | |
185 | ||
186 | route.rt_flags = RTF_UP | RTF_GATEWAY; | |
187 | route.rt_metric = 0; | |
188 | route.rt_dev = (char *)tapname; | |
189 | ||
190 | if (ioctl(sockfd, SIOCADDRT, &route) < 0) { | |
191 | perror("set route"); | |
192 | return -1; | |
193 | } | |
194 | return 0; | |
195 | } | |
196 | ||
197 | static int child(int sock, pid_t target_pid, bool do_config_network, | |
198 | const char *tapname, char *netns_path, char *userns_path, | |
199 | struct slirp4netns_config *cfg) | |
200 | { | |
201 | int rc, tapfd; | |
202 | if ((rc = nsenter(target_pid, netns_path, userns_path, false)) < 0) { | |
203 | return rc; | |
204 | } | |
205 | if ((tapfd = open_tap(tapname)) < 0) { | |
206 | return tapfd; | |
207 | } | |
208 | if (do_config_network && configure_network(tapname, cfg) < 0) { | |
209 | return -1; | |
210 | } | |
211 | if (sendfd(sock, tapfd) < 0) { | |
212 | close(tapfd); | |
213 | close(sock); | |
214 | return -1; | |
215 | } | |
216 | fprintf(stderr, "sent tapfd=%d for %s\n", tapfd, tapname); | |
217 | close(sock); | |
218 | return 0; | |
194 | 219 | } |
195 | 220 | |
196 | 221 | static int recvfd(int sock) |
197 | 222 | { |
198 | int fd; | |
199 | ssize_t rc; | |
200 | struct msghdr msg; | |
201 | struct cmsghdr *cmsg; | |
202 | char cmsgbuf[CMSG_SPACE(sizeof(fd))]; | |
203 | struct iovec iov; | |
204 | char dummy = '\0'; | |
205 | memset(&msg, 0, sizeof(msg)); | |
206 | iov.iov_base = &dummy; | |
207 | iov.iov_len = 1; | |
208 | msg.msg_iov = &iov; | |
209 | msg.msg_iovlen = 1; | |
210 | msg.msg_control = cmsgbuf; | |
211 | msg.msg_controllen = sizeof(cmsgbuf); | |
212 | if ((rc = recvmsg(sock, &msg, 0)) < 0) { | |
213 | perror("recvmsg"); | |
214 | return (int)rc; | |
215 | } | |
216 | if (rc == 0) { | |
217 | fprintf(stderr, "the message is empty\n"); | |
218 | return -1; | |
219 | } | |
220 | cmsg = CMSG_FIRSTHDR(&msg); | |
221 | if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) { | |
222 | fprintf(stderr, "the message does not contain fd\n"); | |
223 | return -1; | |
224 | } | |
225 | memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); | |
226 | return fd; | |
227 | } | |
228 | ||
229 | static int parent(int sock, int exit_fd, const char *api_socket, struct slirp4netns_config *cfg) | |
230 | { | |
231 | int rc, tapfd; | |
232 | if ((tapfd = recvfd(sock)) < 0) { | |
233 | return tapfd; | |
234 | } | |
235 | fprintf(stderr, "received tapfd=%d\n", tapfd); | |
236 | close(sock); | |
237 | printf("Starting slirp\n"); | |
238 | printf("* MTU: %d\n", cfg->mtu); | |
239 | printf("* Network: %s\n", inet_ntoa(cfg->vnetwork)); | |
240 | printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask)); | |
241 | printf("* Gateway: %s\n", inet_ntoa(cfg->vhost)); | |
242 | printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver)); | |
243 | printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest)); | |
244 | if (api_socket != NULL) { | |
245 | printf("* API Socket: %s\n", api_socket); | |
246 | } | |
247 | if (!cfg->disable_host_loopback) { | |
248 | printf | |
249 | ("WARNING: 127.0.0.1:* on the host is accessible as %s (set --disable-host-loopback to prohibit connecting to 127.0.0.1:*)\n", | |
250 | inet_ntoa(cfg->vhost)); | |
251 | } | |
252 | if ((rc = do_slirp(tapfd, exit_fd, api_socket, cfg)) < 0) { | |
253 | fprintf(stderr, "do_slirp failed\n"); | |
254 | close(tapfd); | |
255 | return rc; | |
256 | } | |
257 | /* NOT REACHED */ | |
258 | return 0; | |
223 | int fd; | |
224 | ssize_t rc; | |
225 | struct msghdr msg; | |
226 | struct cmsghdr *cmsg; | |
227 | char cmsgbuf[CMSG_SPACE(sizeof(fd))]; | |
228 | struct iovec iov; | |
229 | char dummy = '\0'; | |
230 | memset(&msg, 0, sizeof(msg)); | |
231 | iov.iov_base = &dummy; | |
232 | iov.iov_len = 1; | |
233 | msg.msg_iov = &iov; | |
234 | msg.msg_iovlen = 1; | |
235 | msg.msg_control = cmsgbuf; | |
236 | msg.msg_controllen = sizeof(cmsgbuf); | |
237 | if ((rc = recvmsg(sock, &msg, 0)) < 0) { | |
238 | perror("recvmsg"); | |
239 | return (int)rc; | |
240 | } | |
241 | if (rc == 0) { | |
242 | fprintf(stderr, "the message is empty\n"); | |
243 | return -1; | |
244 | } | |
245 | cmsg = CMSG_FIRSTHDR(&msg); | |
246 | if (cmsg == NULL || cmsg->cmsg_type != SCM_RIGHTS) { | |
247 | fprintf(stderr, "the message does not contain fd\n"); | |
248 | return -1; | |
249 | } | |
250 | memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); | |
251 | return fd; | |
252 | } | |
253 | ||
254 | static int parent(int sock, int ready_fd, int exit_fd, const char *api_socket, | |
255 | struct slirp4netns_config *cfg, pid_t target_pid) | |
256 | { | |
257 | int rc, tapfd; | |
258 | if ((tapfd = recvfd(sock)) < 0) { | |
259 | return tapfd; | |
260 | } | |
261 | fprintf(stderr, "received tapfd=%d\n", tapfd); | |
262 | close(sock); | |
263 | printf("Starting slirp\n"); | |
264 | printf("* MTU: %d\n", cfg->mtu); | |
265 | printf("* Network: %s\n", inet_ntoa(cfg->vnetwork)); | |
266 | printf("* Netmask: %s\n", inet_ntoa(cfg->vnetmask)); | |
267 | printf("* Gateway: %s\n", inet_ntoa(cfg->vhost)); | |
268 | printf("* DNS: %s\n", inet_ntoa(cfg->vnameserver)); | |
269 | printf("* Recommended IP: %s\n", inet_ntoa(cfg->recommended_vguest)); | |
270 | if (api_socket != NULL) { | |
271 | printf("* API Socket: %s\n", api_socket); | |
272 | } | |
273 | if (!cfg->disable_host_loopback) { | |
274 | printf( | |
275 | "WARNING: 127.0.0.1:* on the host is accessible as %s (set " | |
276 | "--disable-host-loopback to prohibit connecting to 127.0.0.1:*)\n", | |
277 | inet_ntoa(cfg->vhost)); | |
278 | } | |
279 | if (cfg->enable_sandbox && geteuid() != 0) { | |
280 | if ((rc = nsenter(target_pid, NULL, NULL, true)) < 0) { | |
281 | close(tapfd); | |
282 | return rc; | |
283 | } | |
284 | if ((rc = setegid(0)) < 0) { | |
285 | fprintf(stderr, "setegid(0)\n"); | |
286 | close(tapfd); | |
287 | return rc; | |
288 | } | |
289 | if ((rc = seteuid(0)) < 0) { | |
290 | fprintf(stderr, "seteuid(0)\n"); | |
291 | close(tapfd); | |
292 | return rc; | |
293 | } | |
294 | } | |
295 | if ((rc = do_slirp(tapfd, ready_fd, exit_fd, api_socket, cfg)) < 0) { | |
296 | fprintf(stderr, "do_slirp failed\n"); | |
297 | close(tapfd); | |
298 | return rc; | |
299 | } | |
300 | /* NOT REACHED */ | |
301 | return 0; | |
259 | 302 | } |
260 | 303 | |
261 | 304 | static void usage(const char *argv0) |
262 | 305 | { |
263 | printf("Usage: %s [OPTION]... PID TAPNAME\n", argv0); | |
264 | printf("User-mode networking for unprivileged network namespaces.\n\n"); | |
265 | printf("-c, --configure bring up the interface\n"); | |
266 | printf("-e, --exit-fd=FD specify the FD for terminating slirp4netns\n"); | |
267 | printf("-r, --ready-fd=FD specify the FD to write to when the network is configured\n"); | |
268 | printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n", DEFAULT_MTU); | |
269 | printf("--cidr=CIDR specify network address CIDR (default=%s)\n", DEFAULT_CIDR); | |
270 | printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the host namespace\n"); | |
271 | printf("-a, --api-socket=PATH specify API socket path\n"); | |
272 | printf("-6, --enable-ipv6 enable IPv6 (experimental)\n"); | |
273 | printf("-h, --help show this help and exit\n"); | |
274 | printf("-v, --version show version and exit\n"); | |
306 | printf("Usage: %s [OPTION]... PID|PATH TAPNAME\n", argv0); | |
307 | printf("User-mode networking for unprivileged network namespaces.\n\n"); | |
308 | printf("-c, --configure bring up the interface\n"); | |
309 | printf("-e, --exit-fd=FD specify the FD for terminating " | |
310 | "slirp4netns\n"); | |
311 | printf("-r, --ready-fd=FD specify the FD to write to when the " | |
312 | "network is configured\n"); | |
313 | /* v0.2.0 */ | |
314 | printf("-m, --mtu=MTU specify MTU (default=%d, max=65521)\n", | |
315 | DEFAULT_MTU); | |
316 | printf("-6, --enable-ipv6 enable IPv6 (experimental)\n"); | |
317 | /* v0.3.0 */ | |
318 | printf("-a, --api-socket=PATH specify API socket path\n"); | |
319 | printf( | |
320 | "--cidr=CIDR specify network address CIDR (default=%s)\n", | |
321 | DEFAULT_CIDR); | |
322 | printf("--disable-host-loopback prohibit connecting to 127.0.0.1:* on the " | |
323 | "host namespace\n"); | |
324 | /* v0.4.0 */ | |
325 | printf("--netns-type=TYPE specify network namespace type ([path|pid], " | |
326 | "default=%s)\n", | |
327 | DEFAULT_NETNS_TYPE); | |
328 | printf("--userns-path=PATH specify user namespace path\n"); | |
329 | printf( | |
330 | "--enable-sandbox create a new mount namespace (and drop all " | |
331 | "caps except CAP_NET_BIND_SERVICE if running as the root)\n"); | |
332 | printf("--enable-seccomp enable seccomp to limit syscalls " | |
333 | "(experimental)\n"); | |
334 | /* others */ | |
335 | printf("-h, --help show this help and exit\n"); | |
336 | printf("-v, --version show version and exit\n"); | |
275 | 337 | } |
276 | 338 | |
277 | 339 | // version output is runc-compatible and machine-parsable |
278 | 340 | static void version() |
279 | 341 | { |
280 | printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); | |
342 | printf("slirp4netns version %s\n", VERSION ? VERSION : PACKAGE_VERSION); | |
281 | 343 | #ifdef COMMIT |
282 | printf("commit: %s\n", COMMIT); | |
344 | printf("commit: %s\n", COMMIT); | |
283 | 345 | #endif |
346 | printf("libslirp: %s\n", slirp_version_string()); | |
284 | 347 | } |
285 | 348 | |
286 | 349 | struct options { |
287 | pid_t target_pid; // argv[1] | |
288 | char *tapname; // argv[2] | |
289 | bool do_config_network; // -c | |
290 | int exit_fd; // -e | |
291 | int ready_fd; // -r | |
292 | unsigned int mtu; // -m | |
293 | bool disable_host_loopback; // --disable-host-loopback | |
294 | char *cidr; // --cidr | |
295 | bool enable_ipv6; // -6 | |
296 | char *api_socket; // -a | |
350 | pid_t target_pid; // argv[1] | |
351 | char *tapname; // argv[2] | |
352 | bool do_config_network; // -c | |
353 | int exit_fd; // -e | |
354 | int ready_fd; // -r | |
355 | unsigned int mtu; // -m | |
356 | bool disable_host_loopback; // --disable-host-loopback | |
357 | char *cidr; // --cidr | |
358 | bool enable_ipv6; // -6 | |
359 | char *api_socket; // -a | |
360 | char *netns_type; // argv[1] | |
361 | char *netns_path; // --netns-path | |
362 | char *userns_path; // --userns-path | |
363 | bool enable_sandbox; // --enable-sandbox | |
364 | bool enable_seccomp; // --enable-seccomp | |
297 | 365 | }; |
298 | 366 | |
299 | 367 | static void options_init(struct options *options) |
300 | 368 | { |
301 | memset(options, 0, sizeof(*options)); | |
302 | options->exit_fd = options->ready_fd = -1; | |
303 | options->mtu = DEFAULT_MTU; | |
369 | memset(options, 0, sizeof(*options)); | |
370 | options->exit_fd = options->ready_fd = -1; | |
371 | options->mtu = DEFAULT_MTU; | |
304 | 372 | } |
305 | 373 | |
306 | 374 | static void options_destroy(struct options *options) |
307 | 375 | { |
308 | if (options->tapname != NULL) { | |
309 | free(options->tapname); | |
310 | options->tapname = NULL; | |
311 | } | |
312 | if (options->cidr != NULL) { | |
313 | free(options->cidr); | |
314 | options->cidr = NULL; | |
315 | } | |
316 | if (options->api_socket != NULL) { | |
317 | free(options->api_socket); | |
318 | options->api_socket = NULL; | |
319 | } | |
320 | // options itself is not freed, because it can be on the stack. | |
376 | if (options->tapname != NULL) { | |
377 | free(options->tapname); | |
378 | options->tapname = NULL; | |
379 | } | |
380 | if (options->cidr != NULL) { | |
381 | free(options->cidr); | |
382 | options->cidr = NULL; | |
383 | } | |
384 | if (options->api_socket != NULL) { | |
385 | free(options->api_socket); | |
386 | options->api_socket = NULL; | |
387 | } | |
388 | if (options->netns_type != NULL) { | |
389 | free(options->netns_type); | |
390 | options->netns_type = NULL; | |
391 | } | |
392 | if (options->netns_path != NULL) { | |
393 | free(options->netns_path); | |
394 | options->netns_path = NULL; | |
395 | } | |
396 | if (options->userns_path != NULL) { | |
397 | free(options->userns_path); | |
398 | options->userns_path = NULL; | |
399 | } | |
321 | 400 | } |
322 | 401 | |
323 | 402 | // * caller does not need to call options_init() |
325 | 404 | // * this function calls exit() on an error. |
326 | 405 | static void parse_args(int argc, char *const argv[], struct options *options) |
327 | 406 | { |
328 | int opt; | |
407 | int opt; | |
408 | char *strtol_e = NULL; | |
409 | char *optarg_cidr = NULL; | |
410 | char *optarg_netns_type = NULL; | |
411 | char *optarg_userns_path = NULL; | |
412 | char *optarg_api_socket = NULL; | |
329 | 413 | #define CIDR -42 |
330 | 414 | #define DISABLE_HOST_LOOPBACK -43 |
331 | #define _DEPRECATED_NO_HOST_LOOPBACK -10043 // deprecated in favor of disable-host-loopback | |
332 | const struct option longopts[] = { | |
333 | {"configure", no_argument, NULL, 'c'}, | |
334 | {"exit-fd", required_argument, NULL, 'e'}, | |
335 | {"ready-fd", required_argument, NULL, 'r'}, | |
336 | {"mtu", required_argument, NULL, 'm'}, | |
337 | {"cidr", required_argument, NULL, CIDR}, | |
338 | {"disable-host-loopback", no_argument, NULL, DISABLE_HOST_LOOPBACK}, | |
339 | {"no-host-loopback", no_argument, NULL, _DEPRECATED_NO_HOST_LOOPBACK}, | |
340 | {"api-socket", required_argument, NULL, 'a'}, | |
341 | {"enable-ipv6", no_argument, NULL, '6'}, | |
342 | {"help", no_argument, NULL, 'h'}, | |
343 | {"version", no_argument, NULL, 'v'}, | |
344 | {0, 0, 0, 0}, | |
345 | }; | |
346 | options_init(options); | |
347 | while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) != -1) { | |
348 | switch (opt) { | |
349 | case 'c': | |
350 | options->do_config_network = true; | |
351 | break; | |
352 | case 'e': | |
353 | errno = 0; | |
354 | options->exit_fd = strtol(optarg, NULL, 10); | |
355 | if (errno) { | |
356 | perror("strtol"); | |
357 | usage(argv[0]); | |
358 | exit(EXIT_FAILURE); | |
359 | } | |
360 | break; | |
361 | case 'r': | |
362 | errno = 0; | |
363 | options->ready_fd = strtol(optarg, NULL, 10); | |
364 | if (errno) { | |
365 | perror("strtol"); | |
366 | usage(argv[0]); | |
367 | exit(EXIT_FAILURE); | |
368 | } | |
369 | break; | |
370 | case 'm': | |
371 | errno = 0; | |
372 | options->mtu = strtol(optarg, NULL, 10); | |
373 | if (errno) { | |
374 | perror("strtol"); | |
375 | usage(argv[0]); | |
376 | exit(EXIT_FAILURE); | |
377 | } | |
378 | break; | |
379 | case CIDR: | |
380 | options->cidr = strdup(optarg); | |
381 | break; | |
382 | case _DEPRECATED_NO_HOST_LOOPBACK: | |
383 | // There was no tagged release with support for --no-host-loopback. | |
384 | // So no one will be affected by removal of --no-host-loopback. | |
385 | printf | |
386 | ("WARNING: --no-host-loopback is deprecated and will be removed in future releases, please use --disable-host-loopback instead.\n"); | |
387 | /* FALLTHROUGH */ | |
388 | case DISABLE_HOST_LOOPBACK: | |
389 | options->disable_host_loopback = true; | |
390 | break; | |
391 | case 'a': | |
392 | options->api_socket = strdup(optarg); | |
393 | break; | |
394 | case '6': | |
395 | options->enable_ipv6 = true; | |
396 | printf("WARNING: Support for IPv6 is experimental\n"); | |
397 | break; | |
398 | case 'h': | |
399 | usage(argv[0]); | |
400 | exit(EXIT_SUCCESS); | |
401 | case 'v': | |
402 | version(); | |
403 | exit(EXIT_SUCCESS); | |
404 | default: | |
405 | usage(argv[0]); | |
406 | exit(EXIT_FAILURE); | |
407 | } | |
408 | } | |
415 | #define NETNS_TYPE -44 | |
416 | #define USERNS_PATH -45 | |
417 | #define ENABLE_SANDBOX -46 | |
418 | #define ENABLE_SECCOMP -47 | |
419 | #define _DEPRECATED_NO_HOST_LOOPBACK \ | |
420 | -10043 // deprecated in favor of disable-host-loopback | |
421 | #define _DEPRECATED_CREATE_SANDBOX \ | |
422 | -10044 // deprecated in favor of enable-sandbox | |
423 | const struct option longopts[] = { | |
424 | { "configure", no_argument, NULL, 'c' }, | |
425 | { "exit-fd", required_argument, NULL, 'e' }, | |
426 | { "ready-fd", required_argument, NULL, 'r' }, | |
427 | { "mtu", required_argument, NULL, 'm' }, | |
428 | { "cidr", required_argument, NULL, CIDR }, | |
429 | { "disable-host-loopback", no_argument, NULL, DISABLE_HOST_LOOPBACK }, | |
430 | { "no-host-loopback", no_argument, NULL, _DEPRECATED_NO_HOST_LOOPBACK }, | |
431 | { "netns-type", required_argument, NULL, NETNS_TYPE }, | |
432 | { "userns-path", required_argument, NULL, USERNS_PATH }, | |
433 | { "api-socket", required_argument, NULL, 'a' }, | |
434 | { "enable-ipv6", no_argument, NULL, '6' }, | |
435 | { "enable-sandbox", no_argument, NULL, ENABLE_SANDBOX }, | |
436 | { "create-sandbox", no_argument, NULL, _DEPRECATED_CREATE_SANDBOX }, | |
437 | { "enable-seccomp", no_argument, NULL, ENABLE_SECCOMP }, | |
438 | { "help", no_argument, NULL, 'h' }, | |
439 | { "version", no_argument, NULL, 'v' }, | |
440 | { 0, 0, 0, 0 }, | |
441 | }; | |
442 | options_init(options); | |
443 | /* NOTE: clang-tidy hates strdup(optarg) in the while loop (#112) */ | |
444 | while ((opt = getopt_long(argc, argv, "ce:r:m:a:6hv", longopts, NULL)) != | |
445 | -1) { | |
446 | switch (opt) { | |
447 | case 'c': | |
448 | options->do_config_network = true; | |
449 | break; | |
450 | case 'e': | |
451 | errno = 0; | |
452 | options->exit_fd = strtol(optarg, &strtol_e, 10); | |
453 | if (errno || *strtol_e != '\0' || options->exit_fd < 0) { | |
454 | fprintf(stderr, "exit-fd must be a non-negative integer\n"); | |
455 | goto error; | |
456 | } | |
457 | break; | |
458 | case 'r': | |
459 | errno = 0; | |
460 | options->ready_fd = strtol(optarg, &strtol_e, 10); | |
461 | if (errno || *strtol_e != '\0' || options->ready_fd < 0) { | |
462 | fprintf(stderr, "ready-fd must be a non-negative integer\n"); | |
463 | goto error; | |
464 | } | |
465 | break; | |
466 | case 'm': | |
467 | errno = 0; | |
468 | options->mtu = strtol(optarg, &strtol_e, 10); | |
469 | if (errno || *strtol_e != '\0' || options->mtu <= 0 || | |
470 | options->mtu > 65521) { | |
471 | fprintf(stderr, "MTU must be a positive integer (< 65522)\n"); | |
472 | goto error; | |
473 | } | |
474 | break; | |
475 | case CIDR: | |
476 | optarg_cidr = optarg; | |
477 | break; | |
478 | case _DEPRECATED_NO_HOST_LOOPBACK: | |
479 | // There was no tagged release with support for --no-host-loopback. | |
480 | // So no one will be affected by removal of --no-host-loopback. | |
481 | printf("WARNING: --no-host-loopback is deprecated and will be " | |
482 | "removed in future releases, please use " | |
483 | "--disable-host-loopback instead.\n"); | |
484 | /* FALLTHROUGH */ | |
485 | case DISABLE_HOST_LOOPBACK: | |
486 | options->disable_host_loopback = true; | |
487 | break; | |
488 | case _DEPRECATED_CREATE_SANDBOX: | |
489 | // There was no tagged release with support for --create-sandbox. | |
490 | // So no one will be affected by removal of --create-sandbox. | |
491 | printf("WARNING: --create-sandbox is deprecated and will be " | |
492 | "removed in future releases, please use " | |
493 | "--enable-sandbox instead.\n"); | |
494 | /* FALLTHROUGH */ | |
495 | case ENABLE_SANDBOX: | |
496 | options->enable_sandbox = true; | |
497 | break; | |
498 | case ENABLE_SECCOMP: | |
499 | printf("WARNING: Support for seccomp is experimental\n"); | |
500 | options->enable_seccomp = true; | |
501 | break; | |
502 | case NETNS_TYPE: | |
503 | optarg_netns_type = optarg; | |
504 | break; | |
505 | case USERNS_PATH: | |
506 | optarg_userns_path = optarg; | |
507 | if (access(optarg_userns_path, F_OK) == -1) { | |
508 | fprintf(stderr, "userns path doesn't exist: %s\n", | |
509 | optarg_userns_path); | |
510 | goto error; | |
511 | } | |
512 | break; | |
513 | case 'a': | |
514 | optarg_api_socket = optarg; | |
515 | break; | |
516 | case '6': | |
517 | options->enable_ipv6 = true; | |
518 | printf("WARNING: Support for IPv6 is experimental\n"); | |
519 | break; | |
520 | case 'h': | |
521 | usage(argv[0]); | |
522 | exit(EXIT_SUCCESS); | |
523 | break; | |
524 | case 'v': | |
525 | version(); | |
526 | exit(EXIT_SUCCESS); | |
527 | break; | |
528 | default: | |
529 | goto error; | |
530 | break; | |
531 | } | |
532 | } | |
533 | if (optarg_cidr != NULL) { | |
534 | options->cidr = strdup(optarg_cidr); | |
535 | } | |
536 | if (optarg_netns_type != NULL) { | |
537 | options->netns_type = strdup(optarg_netns_type); | |
538 | } | |
539 | if (optarg_userns_path != NULL) { | |
540 | options->userns_path = strdup(optarg_userns_path); | |
541 | } | |
542 | if (optarg_api_socket != NULL) { | |
543 | options->api_socket = strdup(optarg_api_socket); | |
544 | } | |
409 | 545 | #undef CIDR |
410 | 546 | #undef DISABLE_HOST_LOOPBACK |
547 | #undef NETNS_TYPE | |
548 | #undef USERNS_PATH | |
411 | 549 | #undef _DEPRECATED_NO_HOST_LOOPBACK |
412 | if (options->ready_fd >= 0 && !options->do_config_network) { | |
413 | fprintf(stderr, "the option -r FD requires -c\n"); | |
414 | exit(EXIT_FAILURE); | |
415 | } | |
416 | if (options->mtu == 0 || options->mtu > 65521) { | |
417 | fprintf(stderr, "MTU must be a positive integer (< 65522)\n"); | |
418 | exit(EXIT_FAILURE); | |
419 | } | |
420 | if (argc - optind < 2) { | |
421 | usage(argv[0]); | |
422 | exit(EXIT_FAILURE); | |
423 | } | |
424 | errno = 0; | |
425 | options->target_pid = strtol(argv[optind], NULL, 10); | |
426 | if (errno != 0) { | |
427 | perror("strtol"); | |
428 | usage(argv[0]); | |
429 | exit(EXIT_FAILURE); | |
430 | } | |
431 | options->tapname = strdup(argv[optind + 1]); | |
432 | } | |
433 | ||
434 | static int from_regmatch(char *buf, size_t buf_len, regmatch_t match, const char *orig) | |
435 | { | |
436 | size_t len = match.rm_eo - match.rm_so; | |
437 | if (len > buf_len - 1) { | |
438 | return -1; | |
439 | } | |
440 | memset(buf, 0, buf_len); | |
441 | strncpy(buf, &orig[match.rm_so], len); | |
442 | return 0; | |
443 | } | |
444 | ||
445 | static int parse_cidr(struct in_addr *network, struct in_addr *netmask, const char *cidr) | |
446 | { | |
447 | int rc = 0; | |
448 | regex_t r; | |
449 | regmatch_t matches[4]; | |
450 | size_t nmatch = sizeof(matches) / sizeof(matches[0]); | |
451 | const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$"; | |
452 | char snetwork[16], sprefix[16]; | |
453 | int prefix; | |
454 | rc = regcomp(&r, cidr_regex, REG_EXTENDED); | |
455 | if (rc != 0) { | |
456 | fprintf(stderr, "internal regex error\n"); | |
457 | rc = -1; | |
458 | goto finish; | |
459 | } | |
460 | rc = regexec(&r, cidr, nmatch, matches, 0); | |
461 | if (rc != 0) { | |
462 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
463 | rc = -1; | |
464 | goto finish; | |
465 | } | |
466 | rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr); | |
467 | if (rc < 0) { | |
468 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
469 | goto finish; | |
470 | } | |
471 | rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr); | |
472 | if (rc < 0) { | |
473 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
474 | goto finish; | |
475 | } | |
476 | if (inet_pton(AF_INET, snetwork, network) != 1) { | |
477 | fprintf(stderr, "invalid network address: %s\n", snetwork); | |
478 | rc = -1; | |
479 | goto finish; | |
480 | } | |
481 | errno = 0; | |
482 | prefix = strtoul(sprefix, NULL, 10); | |
483 | if (errno) { | |
484 | fprintf(stderr, "invalid prefix length: %s\n", sprefix); | |
485 | rc = -1; | |
486 | goto finish; | |
487 | } | |
488 | if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) { | |
489 | fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN, NETWORK_PREFIX_MAX); | |
490 | rc = -1; | |
491 | goto finish; | |
492 | } | |
493 | netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1)); | |
494 | if ((network->s_addr & netmask->s_addr) != network->s_addr) { | |
495 | fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, not like 10.0.2.100/24\n"); | |
496 | rc = -1; | |
497 | goto finish; | |
498 | } | |
499 | finish: | |
500 | regfree(&r); | |
501 | return rc; | |
502 | } | |
503 | ||
504 | static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg, const char *cidr) | |
505 | { | |
506 | int rc; | |
507 | rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr); | |
508 | if (rc < 0) { | |
509 | goto finish; | |
510 | } | |
511 | cfg->vhost.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET); | |
512 | cfg->vdhcp_start.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET); | |
513 | cfg->vnameserver.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET); | |
514 | cfg->recommended_vguest.s_addr = htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET); | |
515 | finish: | |
516 | return rc; | |
517 | } | |
518 | ||
519 | static int slirp4netns_config_from_options(struct slirp4netns_config *cfg, struct options *opt) | |
520 | { | |
521 | int rc = 0; | |
522 | cfg->mtu = opt->mtu; | |
523 | rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR : opt->cidr); | |
524 | if (rc < 0) { | |
525 | goto finish; | |
526 | } | |
527 | cfg->enable_ipv6 = cfg->enable_ipv6; | |
528 | cfg->disable_host_loopback = opt->disable_host_loopback; | |
529 | finish: | |
530 | return rc; | |
550 | #undef ENABLE_SANDBOX | |
551 | #undef ENABLE_SECCOMP | |
552 | if (argc - optind < 2) { | |
553 | goto error; | |
554 | } | |
555 | if (!options->netns_type || | |
556 | strcmp(options->netns_type, DEFAULT_NETNS_TYPE) == 0) { | |
557 | errno = 0; | |
558 | options->target_pid = strtol(argv[optind], &strtol_e, 10); | |
559 | if (errno || *strtol_e != '\0' || options->target_pid <= 0) { | |
560 | fprintf(stderr, "PID must be a positive integer\n"); | |
561 | goto error; | |
562 | } | |
563 | } else { | |
564 | options->netns_path = strdup(argv[optind]); | |
565 | if (access(options->netns_path, F_OK) == -1) { | |
566 | perror("existing path expected when --netns-type=path"); | |
567 | goto error; | |
568 | } | |
569 | } | |
570 | options->tapname = strdup(argv[optind + 1]); | |
571 | return; | |
572 | error: | |
573 | usage(argv[0]); | |
574 | options_destroy(options); | |
575 | exit(EXIT_FAILURE); | |
576 | } | |
577 | ||
578 | static int from_regmatch(char *buf, size_t buf_len, regmatch_t match, | |
579 | const char *orig) | |
580 | { | |
581 | size_t len = match.rm_eo - match.rm_so; | |
582 | if (len > buf_len - 1) { | |
583 | return -1; | |
584 | } | |
585 | memset(buf, 0, buf_len); | |
586 | strncpy(buf, &orig[match.rm_so], len); | |
587 | return 0; | |
588 | } | |
589 | ||
590 | static int parse_cidr(struct in_addr *network, struct in_addr *netmask, | |
591 | const char *cidr) | |
592 | { | |
593 | int rc = 0; | |
594 | regex_t r; | |
595 | regmatch_t matches[4]; | |
596 | size_t nmatch = sizeof(matches) / sizeof(matches[0]); | |
597 | const char *cidr_regex = "^(([0-9]{1,3}\\.){3}[0-9]{1,3})/([0-9]{1,2})$"; | |
598 | char snetwork[16], sprefix[16]; | |
599 | int prefix; | |
600 | rc = regcomp(&r, cidr_regex, REG_EXTENDED); | |
601 | if (rc != 0) { | |
602 | fprintf(stderr, "internal regex error\n"); | |
603 | rc = -1; | |
604 | goto finish; | |
605 | } | |
606 | rc = regexec(&r, cidr, nmatch, matches, 0); | |
607 | if (rc != 0) { | |
608 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
609 | rc = -1; | |
610 | goto finish; | |
611 | } | |
612 | rc = from_regmatch(snetwork, sizeof(snetwork), matches[1], cidr); | |
613 | if (rc < 0) { | |
614 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
615 | goto finish; | |
616 | } | |
617 | rc = from_regmatch(sprefix, sizeof(sprefix), matches[3], cidr); | |
618 | if (rc < 0) { | |
619 | fprintf(stderr, "invalid CIDR: %s\n", cidr); | |
620 | goto finish; | |
621 | } | |
622 | if (inet_pton(AF_INET, snetwork, network) != 1) { | |
623 | fprintf(stderr, "invalid network address: %s\n", snetwork); | |
624 | rc = -1; | |
625 | goto finish; | |
626 | } | |
627 | errno = 0; | |
628 | prefix = strtoul(sprefix, NULL, 10); | |
629 | if (errno) { | |
630 | fprintf(stderr, "invalid prefix length: %s\n", sprefix); | |
631 | rc = -1; | |
632 | goto finish; | |
633 | } | |
634 | if (prefix < NETWORK_PREFIX_MIN || prefix > NETWORK_PREFIX_MAX) { | |
635 | fprintf(stderr, "prefix length needs to be %d-%d\n", NETWORK_PREFIX_MIN, | |
636 | NETWORK_PREFIX_MAX); | |
637 | rc = -1; | |
638 | goto finish; | |
639 | } | |
640 | netmask->s_addr = htonl(~((1 << (32 - prefix)) - 1)); | |
641 | if ((network->s_addr & netmask->s_addr) != network->s_addr) { | |
642 | fprintf(stderr, "CIDR needs to be a network address like 10.0.2.0/24, " | |
643 | "not like 10.0.2.100/24\n"); | |
644 | rc = -1; | |
645 | goto finish; | |
646 | } | |
647 | finish: | |
648 | regfree(&r); | |
649 | return rc; | |
650 | } | |
651 | ||
652 | static int slirp4netns_config_from_cidr(struct slirp4netns_config *cfg, | |
653 | const char *cidr) | |
654 | { | |
655 | int rc; | |
656 | rc = parse_cidr(&cfg->vnetwork, &cfg->vnetmask, cidr); | |
657 | if (rc < 0) { | |
658 | goto finish; | |
659 | } | |
660 | cfg->vhost.s_addr = | |
661 | htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VHOST_OFFSET); | |
662 | cfg->vdhcp_start.s_addr = | |
663 | htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VDHCPSTART_OFFSET); | |
664 | cfg->vnameserver.s_addr = | |
665 | htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_VNAMESERVER_OFFSET); | |
666 | cfg->recommended_vguest.s_addr = | |
667 | htonl(ntohl(cfg->vnetwork.s_addr) + DEFAULT_RECOMMENDED_VGUEST_OFFSET); | |
668 | finish: | |
669 | return rc; | |
670 | } | |
671 | ||
672 | static int slirp4netns_config_from_options(struct slirp4netns_config *cfg, | |
673 | struct options *opt) | |
674 | { | |
675 | int rc = 0; | |
676 | cfg->mtu = opt->mtu; | |
677 | rc = slirp4netns_config_from_cidr(cfg, opt->cidr == NULL ? DEFAULT_CIDR : | |
678 | opt->cidr); | |
679 | if (rc < 0) { | |
680 | goto finish; | |
681 | } | |
682 | cfg->enable_ipv6 = cfg->enable_ipv6; | |
683 | cfg->disable_host_loopback = opt->disable_host_loopback; | |
684 | cfg->enable_sandbox = opt->enable_sandbox; | |
685 | cfg->enable_seccomp = opt->enable_seccomp; | |
686 | finish: | |
687 | return rc; | |
531 | 688 | } |
532 | 689 | |
533 | 690 | int main(int argc, char *const argv[]) |
534 | 691 | { |
535 | int sv[2]; | |
536 | pid_t child_pid; | |
537 | struct options options; | |
538 | struct slirp4netns_config slirp4netns_config; | |
539 | int exit_status = 0; | |
540 | ||
541 | parse_args(argc, argv, &options); | |
542 | if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) { | |
543 | exit_status = EXIT_FAILURE; | |
544 | goto finish; | |
545 | } | |
546 | if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) { | |
547 | perror("socketpair"); | |
548 | exit_status = EXIT_FAILURE; | |
549 | goto finish; | |
550 | } | |
551 | if ((child_pid = fork()) < 0) { | |
552 | perror("fork"); | |
553 | exit_status = EXIT_FAILURE; | |
554 | goto finish; | |
555 | } | |
556 | if (child_pid == 0) { | |
557 | if (child(sv[1], options.target_pid, options.do_config_network, options.tapname, options.ready_fd, | |
558 | &slirp4netns_config) < 0) { | |
559 | exit_status = EXIT_FAILURE; | |
560 | goto finish; | |
561 | } | |
562 | } else { | |
563 | int child_wstatus, child_status; | |
564 | waitpid(child_pid, &child_wstatus, 0); | |
565 | if (!WIFEXITED(child_wstatus)) { | |
566 | fprintf(stderr, "child failed\n"); | |
567 | exit_status = EXIT_FAILURE; | |
568 | goto finish; | |
569 | } | |
570 | child_status = WEXITSTATUS(child_wstatus); | |
571 | if (child_status != 0) { | |
572 | fprintf(stderr, "child failed(%d)\n", child_status); | |
573 | exit_status = child_status; | |
574 | goto finish; | |
575 | } | |
576 | if (parent(sv[0], options.exit_fd, options.api_socket, &slirp4netns_config) < 0) { | |
577 | fprintf(stderr, "parent failed\n"); | |
578 | exit_status = EXIT_FAILURE; | |
579 | goto finish; | |
580 | } | |
581 | } | |
582 | finish: | |
583 | options_destroy(&options); | |
584 | exit(exit_status); | |
585 | return 0; | |
586 | } | |
692 | int sv[2]; | |
693 | pid_t child_pid; | |
694 | struct options options; | |
695 | struct slirp4netns_config slirp4netns_config; | |
696 | int exit_status = 0; | |
697 | ||
698 | parse_args(argc, argv, &options); | |
699 | if (slirp4netns_config_from_options(&slirp4netns_config, &options) < 0) { | |
700 | exit_status = EXIT_FAILURE; | |
701 | goto finish; | |
702 | } | |
703 | if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv) < 0) { | |
704 | perror("socketpair"); | |
705 | exit_status = EXIT_FAILURE; | |
706 | goto finish; | |
707 | } | |
708 | if ((child_pid = fork()) < 0) { | |
709 | perror("fork"); | |
710 | exit_status = EXIT_FAILURE; | |
711 | goto finish; | |
712 | } | |
713 | if (child_pid == 0) { | |
714 | if (child(sv[1], options.target_pid, options.do_config_network, | |
715 | options.tapname, options.netns_path, options.userns_path, | |
716 | &slirp4netns_config) < 0) { | |
717 | exit_status = EXIT_FAILURE; | |
718 | goto finish; | |
719 | } | |
720 | } else { | |
721 | int ret, child_wstatus, child_status; | |
722 | do | |
723 | ret = waitpid(child_pid, &child_wstatus, 0); | |
724 | while (ret < 0 && errno == EINTR); | |
725 | if (ret < 0) { | |
726 | perror("waitpid"); | |
727 | exit_status = EXIT_FAILURE; | |
728 | goto finish; | |
729 | } | |
730 | if (!WIFEXITED(child_wstatus)) { | |
731 | fprintf(stderr, "child failed(wstatus=%d, !WIFEXITED)\n", | |
732 | child_wstatus); | |
733 | exit_status = EXIT_FAILURE; | |
734 | goto finish; | |
735 | } | |
736 | child_status = WEXITSTATUS(child_wstatus); | |
737 | if (child_status != 0) { | |
738 | fprintf(stderr, "child failed(%d)\n", child_status); | |
739 | exit_status = child_status; | |
740 | goto finish; | |
741 | } | |
742 | if (parent(sv[0], options.ready_fd, options.exit_fd, options.api_socket, | |
743 | &slirp4netns_config, options.target_pid) < 0) { | |
744 | fprintf(stderr, "parent failed\n"); | |
745 | exit_status = EXIT_FAILURE; | |
746 | goto finish; | |
747 | } | |
748 | } | |
749 | finish: | |
750 | options_destroy(&options); | |
751 | exit(exit_status); | |
752 | return 0; | |
753 | } |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
1 | #define _GNU_SOURCE | |
2 | #include <errno.h> | |
3 | #include <sched.h> | |
4 | #include <stdio.h> | |
5 | #include <stdlib.h> | |
6 | #include <string.h> | |
7 | #include <unistd.h> | |
8 | #include <sys/prctl.h> | |
9 | #include <sys/mount.h> | |
10 | #include <sys/syscall.h> | |
11 | #include <sys/capability.h> | |
12 | #include <sys/stat.h> | |
13 | ||
14 | #include <glib.h> | |
15 | ||
16 | static int add_mount(const char *from, const char *to) | |
17 | { | |
18 | int ret; | |
19 | ||
20 | ret = mount("", from, "", MS_SLAVE | MS_REC, NULL); | |
21 | if (ret < 0 && errno != EINVAL) { | |
22 | fprintf(stderr, "cannot make mount propagation slave %s\n", from); | |
23 | return ret; | |
24 | } | |
25 | ret = mount(from, to, "", | |
26 | MS_BIND | MS_REC | MS_SLAVE | MS_NOSUID | MS_NODEV | MS_NOEXEC, | |
27 | NULL); | |
28 | if (ret < 0) { | |
29 | fprintf(stderr, "cannot bind mount %s to %s\n", from, to); | |
30 | return ret; | |
31 | } | |
32 | ret = mount("", to, "", MS_SLAVE | MS_REC, NULL); | |
33 | if (ret < 0) { | |
34 | fprintf(stderr, "cannot make mount propagation slave %s\n", to); | |
35 | return ret; | |
36 | } | |
37 | ret = mount(from, to, "", | |
38 | MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NODEV | | |
39 | MS_NOEXEC, | |
40 | NULL); | |
41 | if (ret < 0) { | |
42 | fprintf(stderr, "cannot remount ro %s\n", to); | |
43 | return ret; | |
44 | } | |
45 | return 0; | |
46 | } | |
47 | ||
48 | /* Warn (not error) if /etc/resolv.conf is a symlink to a file outside /etc or | |
49 | * /run. */ | |
50 | static void validate_etc_resolv_conf() | |
51 | { | |
52 | char *p = realpath("/etc/resolv.conf", NULL); | |
53 | if (p == NULL) { | |
54 | return; | |
55 | } | |
56 | if (!g_str_has_prefix(p, "/etc") && !g_str_has_prefix(p, "/run")) { | |
57 | fprintf(stderr, | |
58 | "sandbox: /etc/resolv.conf (-> %s) seems a symlink to a file " | |
59 | "outside {/etc, /run}. DNS will not work.\n", | |
60 | p); | |
61 | } | |
62 | free(p); | |
63 | } | |
64 | ||
65 | /* lock down the process doing the following: | |
66 | - create a new mount namespace | |
67 | - bind mount /etc and /run from the host | |
68 | - pivot_root in the new tmpfs. | |
69 | - drop all capabilities. | |
70 | */ | |
71 | int create_sandbox() | |
72 | { | |
73 | int ret, i; | |
74 | struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 }; | |
75 | struct __user_cap_data_struct data[2] = { { 0 } }; | |
76 | ||
77 | validate_etc_resolv_conf(); | |
78 | ||
79 | ret = unshare(CLONE_NEWNS); | |
80 | if (ret < 0) { | |
81 | fprintf(stderr, "cannot unshare new mount namespace\n"); | |
82 | return ret; | |
83 | } | |
84 | ret = mount("", "/", "", MS_PRIVATE, NULL); | |
85 | if (ret < 0) { | |
86 | fprintf(stderr, "cannot remount / private\n"); | |
87 | return ret; | |
88 | } | |
89 | ||
90 | ret = mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC, | |
91 | "size=1k"); | |
92 | if (ret < 0) { | |
93 | fprintf(stderr, "cannot mount tmpfs on /tmp\n"); | |
94 | return ret; | |
95 | } | |
96 | ||
97 | ret = mkdir("/tmp/etc", 0755); | |
98 | if (ret < 0) { | |
99 | fprintf(stderr, "cannot mkdir /etc\n"); | |
100 | return ret; | |
101 | } | |
102 | ||
103 | ret = mkdir("/tmp/old", 0755); | |
104 | if (ret < 0) { | |
105 | fprintf(stderr, "cannot mkdir /old\n"); | |
106 | return ret; | |
107 | } | |
108 | ||
109 | ret = mkdir("/tmp/run", 0755); | |
110 | if (ret < 0) { | |
111 | fprintf(stderr, "cannot mkdir /run\n"); | |
112 | return ret; | |
113 | } | |
114 | ||
115 | ret = add_mount("/etc", "/tmp/etc"); | |
116 | if (ret < 0) { | |
117 | return ret; | |
118 | } | |
119 | ||
120 | ret = add_mount("/run", "/tmp/run"); | |
121 | if (ret < 0) { | |
122 | return ret; | |
123 | } | |
124 | ||
125 | ret = chdir("/tmp"); | |
126 | if (ret < 0) { | |
127 | fprintf(stderr, "cannot chdir to /tmp\n"); | |
128 | return ret; | |
129 | } | |
130 | ||
131 | ret = syscall(__NR_pivot_root, ".", "old"); | |
132 | if (ret < 0) { | |
133 | fprintf(stderr, "cannot pivot_root to /tmp\n"); | |
134 | return ret; | |
135 | } | |
136 | ||
137 | ret = chdir("/"); | |
138 | if (ret < 0) { | |
139 | fprintf(stderr, "cannot chdir to /\n"); | |
140 | return ret; | |
141 | } | |
142 | ||
143 | ret = umount2("/old", MNT_DETACH); | |
144 | if (ret < 0) { | |
145 | fprintf(stderr, "cannot umount /old\n"); | |
146 | return ret; | |
147 | } | |
148 | ||
149 | ret = rmdir("/old"); | |
150 | if (ret < 0) { | |
151 | fprintf(stderr, "cannot rmdir /old\n"); | |
152 | return ret; | |
153 | } | |
154 | ||
155 | ret = mount("tmpfs", "/", "tmpfs", MS_REMOUNT | MS_RDONLY, "size=0k"); | |
156 | if (ret < 0) { | |
157 | fprintf(stderr, "cannot remount / as read-only\n"); | |
158 | /* error is negligible (#163) */ | |
159 | } | |
160 | ||
161 | ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); | |
162 | if (ret < 0) { | |
163 | fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS)\n"); | |
164 | return ret; | |
165 | } | |
166 | ||
167 | ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); | |
168 | if (ret < 0) { | |
169 | fprintf(stderr, "prctl(PR_CAP_AMBIENT_CLEAR_ALL)\n"); | |
170 | return ret; | |
171 | } | |
172 | for (i = 0;; i++) { | |
173 | if (i == CAP_NET_BIND_SERVICE) | |
174 | continue; | |
175 | ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); | |
176 | if (ret < 0) { | |
177 | if (errno == EINVAL) | |
178 | break; | |
179 | fprintf(stderr, "prctl(PR_CAPBSET_DROP)\n"); | |
180 | return ret; | |
181 | } | |
182 | } | |
183 | ||
184 | memset(&data, 0, sizeof(data)); | |
185 | data[0].effective |= 1 << CAP_NET_BIND_SERVICE; | |
186 | data[0].permitted |= 1 << CAP_NET_BIND_SERVICE; | |
187 | data[0].inheritable |= 1 << CAP_NET_BIND_SERVICE; | |
188 | ret = capset(&hdr, data); | |
189 | if (ret < 0) { | |
190 | fprintf(stderr, "capset(0)\n"); | |
191 | return ret; | |
192 | } | |
193 | ||
194 | return 0; | |
195 | } |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
1 | #ifndef SLIRP4NETNS_SANDBOX_H | |
2 | # define SLIRP4NETNS_SANDBOX_H | |
3 | int create_sandbox(); | |
4 | #endif |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
1 | #define _GNU_SOURCE | |
2 | #include <stdio.h> | |
3 | #include <seccomp.h> | |
4 | ||
5 | int enable_seccomp() | |
6 | { | |
7 | int rc = -1; | |
8 | /* Allow everything by default and block dangerous syscalls explicitly, | |
9 | * as it is hard to find the correct set of required syscalls */ | |
10 | scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW); | |
11 | if (ctx == NULL) | |
12 | goto ret; | |
13 | printf("seccomp: The following syscalls will be blocked by seccomp:"); | |
14 | #ifdef SCMP_ACT_KILL_PROCESS | |
15 | #define BLOCK_ACTION SCMP_ACT_KILL_PROCESS | |
16 | #else | |
17 | #define BLOCK_ACTION SCMP_ACT_KILL | |
18 | #endif | |
19 | #define BLOCK(x) \ | |
20 | { \ | |
21 | rc = seccomp_rule_add(ctx, BLOCK_ACTION, SCMP_SYS(x), 0); \ | |
22 | if (rc < 0) \ | |
23 | goto ret; \ | |
24 | printf(" %s", #x); \ | |
25 | } | |
26 | BLOCK(execve); | |
27 | #ifdef __NR_execveat | |
28 | BLOCK(execveat); | |
29 | #else | |
30 | fprintf(stderr, | |
31 | "seccomp: can't block execevat because __NR_execveat was not " | |
32 | "defined in the build environment\n"); | |
33 | #endif | |
34 | /* ideally we should also block open() and openat() but required for | |
35 | * resolv.conf */ | |
36 | BLOCK(open_by_handle_at); | |
37 | BLOCK(ptrace); | |
38 | BLOCK(prctl); | |
39 | BLOCK(process_vm_readv); | |
40 | BLOCK(process_vm_writev); | |
41 | BLOCK(mount); | |
42 | BLOCK(name_to_handle_at); | |
43 | BLOCK(setns); | |
44 | BLOCK(umount); | |
45 | BLOCK(umount2); | |
46 | BLOCK(unshare); | |
47 | #undef BLOCK | |
48 | #undef BLOCK_ACTION | |
49 | printf(".\n"); | |
50 | rc = seccomp_load(ctx); | |
51 | ret: | |
52 | seccomp_release(ctx); | |
53 | return rc; | |
54 | } |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
1 | #ifndef SLIRP4NETNS_SECCOMPFILTER_H | |
2 | # define SLIRP4NETNS_SECCOMPFILTER_H | |
3 | int enable_seccomp(); | |
4 | #endif |
0 | 0 | .nh |
1 | .TH SLIRP4NETNS 1 "July 2018" "Rootless Containers" "User Commands" | |
1 | .TH SLIRP4NETNS 1 "March 2020" "Rootless Containers" "User Commands" | |
2 | 2 | |
3 | 3 | .SH NAME |
4 | 4 | .PP |
7 | 7 | |
8 | 8 | .SH SYNOPSIS |
9 | 9 | .PP |
10 | slirp4netns [OPTION]... PID TAPNAME | |
10 | slirp4netns [OPTION]... PID|PATH TAPNAME | |
11 | 11 | |
12 | 12 | |
13 | 13 | .SH DESCRIPTION |
42 | 42 | .SH OPTIONS |
43 | 43 | .PP |
44 | 44 | \fB\-c\fP, \fB\-\-configure\fP |
45 | bring up the interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address. | |
45 | bring up the TAP interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address. | |
46 | Starting with v0.4.0, the loopback interface (\fBlo\fP) is brought up as well. | |
46 | 47 | |
47 | 48 | .PP |
48 | 49 | \fB\-e\fP, \fB\-\-exit\-fd=FD\fP |
49 | 50 | specify the FD for terminating slirp4netns. |
51 | When the FD is specified, slirp4netns exits when a \fBpoll(2)\fP event happens on the FD. | |
50 | 52 | |
51 | 53 | .PP |
52 | 54 | \fB\-r\fP, \fB\-\-ready\-fd=FD\fP |
53 | specify the FD to write to when the network is configured. | |
54 | ||
55 | .PP | |
56 | \fB\-m\fP, \fB\-\-mtu=MTU\fP | |
55 | specify the FD to write to when the initialization steps are finished. | |
56 | When the FD is specified, slirp4netns writes \fB"1"\fP to the FD and close the FD. | |
57 | Prior to v0.4.0, the FD was written after the network configuration (\fB\-c\fP) | |
58 | but before the API socket configuration (\fB\-a\fP). | |
59 | ||
60 | .PP | |
61 | \fB\-m\fP, \fB\-\-mtu=MTU\fP (since v0.2.0) | |
57 | 62 | specify MTU (max=65521). |
63 | ||
64 | .PP | |
65 | \fB\-6\fP, \fB\-\-enable\-ipv6\fP (since v0.2.0, EXPERIMENTAL) | |
66 | enable IPv6 | |
67 | ||
68 | .PP | |
69 | \fB\-a\fP, \fB\-\-api\-socket\fP (since v0.3.0) | |
70 | API socket path | |
58 | 71 | |
59 | 72 | .PP |
60 | 73 | \fB\-\-cidr\fP (since v0.3.0) |
65 | 78 | prohibit connecting to 127.0.0.1:* on the host namespace |
66 | 79 | |
67 | 80 | .PP |
68 | \fB\-a\fP, \fB\-\-api\-socket\fP (since v0.3.0) | |
69 | API socket path | |
70 | ||
71 | .PP | |
72 | \fB\-6\fP, \fB\-\-enable\-ipv6\fP | |
73 | enable IPv6 (experimental). | |
74 | ||
75 | .PP | |
76 | \fB\-h\fP, \fB\-\-help\fP | |
81 | \fB\-\-netns\-type=TYPE\fP (since v0.4.0) | |
82 | specify network namespace type ([path|pid], default=pid) | |
83 | ||
84 | .PP | |
85 | \fB\-\-userns\-path=PATH\fP (since v0.4.0) | |
86 | specify user namespace path | |
87 | ||
88 | .PP | |
89 | \fB\-\-enable\-sandbox\fP (since v0.4.0) | |
90 | enter the user namespace and create a new mount namespace where only /etc and | |
91 | /run are mounted from the host. | |
92 | ||
93 | .PP | |
94 | Requires \fB/etc/resolv.conf\fP not to be a symlink to a file outside /etc and /run. | |
95 | ||
96 | .PP | |
97 | When running as the root, the process does not enter the user namespace but all | |
98 | the capabilities except \fB\fCCAP\_NET\_BIND\_SERVICE\fR are dropped. | |
99 | ||
100 | .PP | |
101 | \fB\-\-enable\-seccomp\fP (since v0.4.0, EXPERIMENTAL) | |
102 | enable \fBseccomp(2)\fP to limit syscalls. | |
103 | Typically used in conjunction with \fB\-\-enable\-sandbox\fP\&. | |
104 | ||
105 | .PP | |
106 | \fB\-h\fP, \fB\-\-help\fP (since v0.2.0) | |
77 | 107 | show help and exit |
78 | 108 | |
79 | 109 | .PP |
80 | \fB\-v\fP, \fB\-\-version\fP | |
110 | \fB\-v\fP, \fB\-\-version\fP (since v0.2.0) | |
81 | 111 | show version and exit |
82 | 112 | |
83 | 113 | |
267 | 297 | |
268 | 298 | .RS |
269 | 299 | .IP \(bu 2 |
270 | Client needs to \fBshutdown\fP the socket with \fBSHUT\_WR\fP after sending every request. | |
300 | Client needs to \fBshutdown(2)\fP the socket with \fBSHUT\_WR\fP after sending every request. | |
271 | 301 | i.e. No support for keep\-alive and timeout. |
272 | 302 | .IP \(bu 2 |
273 | 303 | slirp4netns "stops the world" during processing API requests. |
274 | 304 | .IP \(bu 2 |
275 | A request must be less than 4095 bytes. | |
305 | A request must be less than 4096 bytes. | |
276 | 306 | .IP \(bu 2 |
277 | 307 | JSON responses may contain \fBerror\fP instead of \fBreturn\fP\&. |
278 | 308 | |
279 | 309 | .RE |
280 | 310 | |
281 | 311 | |
312 | .SH DEFINED NAMESPACE PATHS | |
313 | .PP | |
314 | A user can define a network namespace path as opposed to the default process ID: | |
315 | ||
316 | .PP | |
317 | .RS | |
318 | ||
319 | .nf | |
320 | $ slirp4netns \-\-netns\-type=path ... /path/to/netns tap0 | |
321 | ||
322 | .fi | |
323 | .RE | |
324 | ||
325 | .PP | |
326 | Currently, the \fBnetns\-type=TYPE\fP argument supports \fBpath\fP or \fBpid\fP args with the default being \fBpid\fP\&. | |
327 | ||
328 | .PP | |
329 | Additionally, a \fB\-\-userns\-path=PATH\fP argument can be included to override any user namespace path defaults | |
330 | ||
331 | .PP | |
332 | .RS | |
333 | ||
334 | .nf | |
335 | $ slirp4netns \-\-netns\-type=path \-\-userns\-path=/path/to/userns /path/to/netns tap0 | |
336 | ||
337 | .fi | |
338 | .RE | |
339 | ||
340 | ||
341 | .SH BUGS | |
342 | .PP | |
343 | Kernel 4.20 bumped up the default value of \fB/proc/sys/net/ipv4/tcp\_rmem\fP from 87380 to 131072. | |
344 | This is known to slow down slirp4netns port forwarding: \fBhttps://github.com/rootless\-containers/slirp4netns/issues/128\fP\&. | |
345 | ||
346 | .PP | |
347 | As a workaround, you can adjust the value of \fB/proc/sys/net/ipv4/tcp\_rmem\fP inside the namespace. | |
348 | No real root privilege is needed to modify the file since kernel 4.15. | |
349 | ||
350 | .PP | |
351 | .RS | |
352 | ||
353 | .nf | |
354 | unshared$ c=$(cat /proc/sys/net/ipv4/tcp\_rmem); echo $c | sed \-e s/131072/87380/g > /proc/sys/net/ipv4/tcp\_rmem | |
355 | ||
356 | .fi | |
357 | .RE | |
358 | ||
359 | ||
282 | 360 | .SH SEE ALSO |
283 | 361 | .PP |
284 | 362 | \fBnetwork\_namespaces\fP(7), \fBuser\_namespaces\fP(7), \fBveth\fP(4) |
286 | 364 | |
287 | 365 | .SH AVAILABILITY |
288 | 366 | .PP |
289 | The slirp4netns command is available from \fBhttps://github.com/rootless\-containers/slirp4netns\fP under GNU GENERAL PUBLIC LICENSE Version 2. | |
367 | The slirp4netns command is available from \fBhttps://github.com/rootless\-containers/slirp4netns\fP under GNU GENERAL PUBLIC LICENSE Version 2 (or later). |
0 | SLIRP4NETNS 1 "July 2018" "Rootless Containers" "User Commands" | |
0 | SLIRP4NETNS 1 "March 2020" "Rootless Containers" "User Commands" | |
1 | 1 | ================================================== |
2 | 2 | |
3 | 3 | # NAME |
6 | 6 | |
7 | 7 | # SYNOPSIS |
8 | 8 | |
9 | slirp4netns [OPTION]... PID TAPNAME | |
9 | slirp4netns [OPTION]... PID|PATH TAPNAME | |
10 | 10 | |
11 | 11 | # DESCRIPTION |
12 | 12 | |
27 | 27 | # OPTIONS |
28 | 28 | |
29 | 29 | **-c**, **--configure** |
30 | bring up the interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address. | |
30 | bring up the TAP interface. IP will be set to 10.0.2.100 (network address + 100) by default. IPv6 will be set to a random address. | |
31 | Starting with v0.4.0, the loopback interface (**lo**) is brought up as well. | |
31 | 32 | |
32 | 33 | **-e**, **--exit-fd=FD** |
33 | 34 | specify the FD for terminating slirp4netns. |
35 | When the FD is specified, slirp4netns exits when a **poll(2)** event happens on the FD. | |
34 | 36 | |
35 | 37 | **-r**, **--ready-fd=FD** |
36 | specify the FD to write to when the network is configured. | |
37 | ||
38 | **-m**, **--mtu=MTU** | |
38 | specify the FD to write to when the initialization steps are finished. | |
39 | When the FD is specified, slirp4netns writes **"1"** to the FD and close the FD. | |
40 | Prior to v0.4.0, the FD was written after the network configuration (**-c**) | |
41 | but before the API socket configuration (**-a**). | |
42 | ||
43 | **-m**, **--mtu=MTU** (since v0.2.0) | |
39 | 44 | specify MTU (max=65521). |
45 | ||
46 | **-6**, **--enable-ipv6** (since v0.2.0, EXPERIMENTAL) | |
47 | enable IPv6 | |
48 | ||
49 | **-a**, **--api-socket** (since v0.3.0) | |
50 | API socket path | |
40 | 51 | |
41 | 52 | **--cidr** (since v0.3.0) |
42 | 53 | specify CIDR, e.g. 10.0.2.0/24 |
44 | 55 | **--disable-host-loopback** (since v0.3.0) |
45 | 56 | prohibit connecting to 127.0.0.1:\* on the host namespace |
46 | 57 | |
47 | **-a**, **--api-socket** (since v0.3.0) | |
48 | API socket path | |
49 | ||
50 | **-6**, **--enable-ipv6** | |
51 | enable IPv6 (experimental). | |
52 | ||
53 | **-h**, **--help** | |
58 | **--netns-type=TYPE** (since v0.4.0) | |
59 | specify network namespace type ([path|pid], default=pid) | |
60 | ||
61 | **--userns-path=PATH** (since v0.4.0) | |
62 | specify user namespace path | |
63 | ||
64 | **--enable-sandbox** (since v0.4.0) | |
65 | enter the user namespace and create a new mount namespace where only /etc and | |
66 | /run are mounted from the host. | |
67 | ||
68 | Requires **/etc/resolv.conf** not to be a symlink to a file outside /etc and /run. | |
69 | ||
70 | When running as the root, the process does not enter the user namespace but all | |
71 | the capabilities except `CAP_NET_BIND_SERVICE` are dropped. | |
72 | ||
73 | **--enable-seccomp** (since v0.4.0, EXPERIMENTAL) | |
74 | enable **seccomp(2)** to limit syscalls. | |
75 | Typically used in conjunction with **--enable-sandbox**. | |
76 | ||
77 | **-h**, **--help** (since v0.2.0) | |
54 | 78 | show help and exit |
55 | 79 | |
56 | **-v**, **--version** | |
80 | **-v**, **--version** (since v0.2.0) | |
57 | 81 | show version and exit |
58 | 82 | |
59 | 83 | # EXAMPLE |
171 | 195 | |
172 | 196 | Remarks: |
173 | 197 | |
174 | * Client needs to **shutdown** the socket with **SHUT_WR** after sending every request. | |
198 | * Client needs to **shutdown(2)** the socket with **SHUT_WR** after sending every request. | |
175 | 199 | i.e. No support for keep-alive and timeout. |
176 | 200 | * slirp4netns "stops the world" during processing API requests. |
177 | * A request must be less than 4095 bytes. | |
201 | * A request must be less than 4096 bytes. | |
178 | 202 | * JSON responses may contain **error** instead of **return**. |
179 | 203 | |
204 | # DEFINED NAMESPACE PATHS | |
205 | A user can define a network namespace path as opposed to the default process ID: | |
206 | ||
207 | ```console | |
208 | $ slirp4netns --netns-type=path ... /path/to/netns tap0 | |
209 | ``` | |
210 | Currently, the **netns-type=TYPE** argument supports **path** or **pid** args with the default being **pid**. | |
211 | ||
212 | Additionally, a **--userns-path=PATH** argument can be included to override any user namespace path defaults | |
213 | ```console | |
214 | $ slirp4netns --netns-type=path --userns-path=/path/to/userns /path/to/netns tap0 | |
215 | ``` | |
216 | ||
217 | # BUGS | |
218 | ||
219 | Kernel 4.20 bumped up the default value of **/proc/sys/net/ipv4/tcp_rmem** from 87380 to 131072. | |
220 | This is known to slow down slirp4netns port forwarding: **https://github.com/rootless-containers/slirp4netns/issues/128**. | |
221 | ||
222 | As a workaround, you can adjust the value of **/proc/sys/net/ipv4/tcp_rmem** inside the namespace. | |
223 | No real root privilege is needed to modify the file since kernel 4.15. | |
224 | ||
225 | ```console | |
226 | unshared$ c=$(cat /proc/sys/net/ipv4/tcp_rmem); echo $c | sed -e s/131072/87380/g > /proc/sys/net/ipv4/tcp_rmem | |
227 | ``` | |
228 | ||
180 | 229 | # SEE ALSO |
181 | 230 | |
182 | 231 | **network_namespaces**(7), **user_namespaces**(7), **veth**(4) |
183 | 232 | |
184 | 233 | # AVAILABILITY |
185 | 234 | |
186 | The slirp4netns command is available from **https://github.com/rootless-containers/slirp4netns** under GNU GENERAL PUBLIC LICENSE Version 2. | |
235 | The slirp4netns command is available from **https://github.com/rootless-containers/slirp4netns** under GNU GENERAL PUBLIC LICENSE Version 2 (or later). |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #define _GNU_SOURCE |
1 | 2 | #include <errno.h> |
2 | 3 | #include <signal.h> |
5 | 6 | #include <unistd.h> |
6 | 7 | |
7 | 8 | #include <glib.h> |
8 | ||
9 | #include "vendor/libslirp/src/libslirp.h" | |
9 | #include <libslirp.h> | |
10 | ||
10 | 11 | #include "api.h" |
12 | #include "sandbox.h" | |
13 | #include "seccompfilter.h" | |
11 | 14 | #include "slirp4netns.h" |
12 | 15 | |
13 | 16 | /* opaque for SlirpCb */ |
14 | 17 | struct libslirp_data { |
15 | int tapfd; | |
16 | GSList *timers; | |
18 | int tapfd; | |
19 | GSList *timers; | |
17 | 20 | }; |
18 | 21 | |
19 | 22 | /* implements SlirpCb.send_packet */ |
20 | static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, void *opaque) | |
21 | { | |
22 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
23 | return write(data->tapfd, pkt, pkt_len); | |
23 | static ssize_t libslirp_send_packet(const void *pkt, size_t pkt_len, | |
24 | void *opaque) | |
25 | { | |
26 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
27 | return write(data->tapfd, pkt, pkt_len); | |
24 | 28 | } |
25 | 29 | |
26 | 30 | /* implements SlirpCb.guest_error */ |
27 | 31 | static void libslirp_guest_error(const char *msg, void *opaque) |
28 | 32 | { |
29 | fprintf(stderr, "libslirp: %s\n", msg); | |
33 | fprintf(stderr, "libslirp: %s\n", msg); | |
30 | 34 | } |
31 | 35 | |
32 | 36 | /* implements SlirpCb.clock_get_ns */ |
33 | 37 | static int64_t libslirp_clock_get_ns(void *opaque) |
34 | 38 | { |
35 | struct timespec ts; | |
36 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
37 | return ts.tv_sec * 1000000000LL + ts.tv_nsec; | |
39 | struct timespec ts; | |
40 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
41 | return ts.tv_sec * 1000000000LL + ts.tv_nsec; | |
38 | 42 | } |
39 | 43 | |
40 | 44 | /* timer for SlirpCb */ |
41 | 45 | struct timer { |
42 | SlirpTimerCb cb; | |
43 | void *cb_opaque; | |
44 | int64_t expire_timer_msec; | |
46 | SlirpTimerCb cb; | |
47 | void *cb_opaque; | |
48 | int64_t expire_timer_msec; | |
45 | 49 | }; |
46 | 50 | |
47 | 51 | /* implements SlirpCb.timer_new */ |
48 | 52 | static void *libslirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) |
49 | 53 | { |
50 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
51 | struct timer *t = g_malloc0(sizeof(*t)); | |
52 | t->cb = cb; | |
53 | t->cb_opaque = cb_opaque; | |
54 | t->expire_timer_msec = -1; | |
55 | data->timers = g_slist_append(data->timers, t); | |
56 | return t; | |
54 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
55 | struct timer *t = g_malloc0(sizeof(*t)); | |
56 | t->cb = cb; | |
57 | t->cb_opaque = cb_opaque; | |
58 | t->expire_timer_msec = -1; | |
59 | data->timers = g_slist_append(data->timers, t); | |
60 | return t; | |
57 | 61 | } |
58 | 62 | |
59 | 63 | /* implements SlirpCb.timer_free */ |
60 | 64 | static void libslirp_timer_free(void *timer, void *opaque) |
61 | 65 | { |
62 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
63 | data->timers = g_slist_remove(data->timers, timer); | |
64 | g_free(timer); | |
66 | struct libslirp_data *data = (struct libslirp_data *)opaque; | |
67 | data->timers = g_slist_remove(data->timers, timer); | |
68 | g_free(timer); | |
65 | 69 | } |
66 | 70 | |
67 | 71 | /* implements SlirpCb.timer_mod */ |
68 | static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, void *opaque) | |
69 | { | |
70 | struct timer *t = (struct timer *)timer; | |
71 | t->expire_timer_msec = expire_timer_msec; | |
72 | static void libslirp_timer_mod(void *timer, int64_t expire_timer_msec, | |
73 | void *opaque) | |
74 | { | |
75 | struct timer *t = (struct timer *)timer; | |
76 | t->expire_timer_msec = expire_timer_msec; | |
72 | 77 | } |
73 | 78 | |
74 | 79 | /* implements SlirpCb.register_poll_fd */ |
75 | 80 | static void libslirp_register_poll_fd(int fd, void *opaque) |
76 | 81 | { |
77 | /* | |
78 | * NOP | |
79 | * | |
80 | * This is NOP on QEMU upstream on Linux as well, see: | |
81 | * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls qemu_fd_register) | |
82 | * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux) | |
83 | * | |
84 | * See also: | |
85 | * * qemu/util/main-loop.c: qemu_fd_register (Win32 only) | |
86 | */ | |
82 | /* | |
83 | * NOP | |
84 | * | |
85 | * This is NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 on Linux as | |
86 | * well, see: | |
87 | * * qemu/net/slirp.c: net_slirp_register_poll_fd (calls | |
88 | * qemu_fd_register) | |
89 | * * qemu/stubs/fd-register.c: qemu_fd_register (NOP on Linux) | |
90 | * | |
91 | * See also: | |
92 | * * qemu/util/main-loop.c: qemu_fd_register (Win32 only) | |
93 | */ | |
87 | 94 | } |
88 | 95 | |
89 | 96 | /* implements SlirpCb.unregister_poll_fd */ |
90 | 97 | static void libslirp_unregister_poll_fd(int fd, void *opaque) |
91 | 98 | { |
92 | /* | |
93 | * NOP | |
94 | * | |
95 | * This is NOP on QEMU upstream as well, see: | |
96 | * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP) | |
97 | */ | |
99 | /* | |
100 | * NOP | |
101 | * | |
102 | * This is NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 as well, | |
103 | * see: | |
104 | * * qemu/net/slirp.c: net_slirp_unregister_poll_fd (NOP) | |
105 | */ | |
98 | 106 | } |
99 | 107 | |
100 | 108 | /* implements SlirpCb.notify */ |
101 | 109 | static void libslirp_notify(void *opaque) |
102 | 110 | { |
103 | /* | |
104 | * NOP | |
105 | * | |
106 | * This can be NOP on QEMU upstream as well, see: | |
107 | * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event) | |
108 | * * qemu/stubs/notify-event.c: qemu_notify_event (NOP) | |
109 | * | |
110 | * See also: | |
111 | * * qemu/util/main-loop.c: qemu_notify_event (NOP if !qemu_aio_context) | |
112 | */ | |
111 | /* | |
112 | * NOP | |
113 | * | |
114 | * This can be NOP on QEMU@4c76137484878f42a2ce1ae1b888b6a7f66b4053 as well, | |
115 | * see: | |
116 | * * qemu/net/slirp.c: net_slirp_notify (calls qemu_notify_event) | |
117 | * * qemu/stubs/notify-event.c: qemu_notify_event (NOP) | |
118 | * | |
119 | * See also: | |
120 | * * qemu/util/main-loop.c: qemu_notify_event (NOP if | |
121 | * !qemu_aio_context) | |
122 | */ | |
113 | 123 | } |
114 | 124 | |
115 | 125 | static int libslirp_poll_to_gio(int events) |
116 | 126 | { |
117 | int ret = 0; | |
118 | if (events & SLIRP_POLL_IN) { | |
119 | ret |= G_IO_IN; | |
120 | } | |
121 | if (events & SLIRP_POLL_OUT) { | |
122 | ret |= G_IO_OUT; | |
123 | } | |
124 | if (events & SLIRP_POLL_PRI) { | |
125 | ret |= G_IO_PRI; | |
126 | } | |
127 | if (events & SLIRP_POLL_ERR) { | |
128 | ret |= G_IO_ERR; | |
129 | } | |
130 | if (events & SLIRP_POLL_HUP) { | |
131 | ret |= G_IO_HUP; | |
132 | } | |
133 | return ret; | |
127 | int ret = 0; | |
128 | if (events & SLIRP_POLL_IN) { | |
129 | ret |= G_IO_IN; | |
130 | } | |
131 | if (events & SLIRP_POLL_OUT) { | |
132 | ret |= G_IO_OUT; | |
133 | } | |
134 | if (events & SLIRP_POLL_PRI) { | |
135 | ret |= G_IO_PRI; | |
136 | } | |
137 | if (events & SLIRP_POLL_ERR) { | |
138 | ret |= G_IO_ERR; | |
139 | } | |
140 | if (events & SLIRP_POLL_HUP) { | |
141 | ret |= G_IO_HUP; | |
142 | } | |
143 | return ret; | |
134 | 144 | } |
135 | 145 | |
136 | 146 | /* |
137 | 147 | * implements SlirpAddPollCb used in slirp_pollfds_fill. |
138 | * originally from qemu/net/slirp.c:net_slirp_add_poll | |
148 | * originally from qemu/net/slirp.c:net_slirp_add_poll | |
149 | * (4c76137484878f42a2ce1ae1b888b6a7f66b4053) | |
139 | 150 | */ |
140 | 151 | static int libslirp_add_poll(int fd, int events, void *opaque) |
141 | 152 | { |
142 | GArray *pollfds = opaque; | |
143 | GPollFD pfd = { | |
144 | .fd = fd, | |
145 | .events = libslirp_poll_to_gio(events), | |
146 | }; | |
147 | int idx = pollfds->len; | |
148 | g_array_append_val(pollfds, pfd); | |
149 | return idx; | |
153 | GArray *pollfds = opaque; | |
154 | GPollFD pfd = { | |
155 | .fd = fd, | |
156 | .events = libslirp_poll_to_gio(events), | |
157 | }; | |
158 | int idx = pollfds->len; | |
159 | g_array_append_val(pollfds, pfd); | |
160 | return idx; | |
150 | 161 | } |
151 | 162 | |
152 | 163 | static int libslirp_gio_to_poll(int events) |
153 | 164 | { |
154 | int ret = 0; | |
155 | if (events & G_IO_IN) { | |
156 | ret |= SLIRP_POLL_IN; | |
157 | } | |
158 | if (events & G_IO_OUT) { | |
159 | ret |= SLIRP_POLL_OUT; | |
160 | } | |
161 | if (events & G_IO_PRI) { | |
162 | ret |= SLIRP_POLL_PRI; | |
163 | } | |
164 | if (events & G_IO_ERR) { | |
165 | ret |= SLIRP_POLL_ERR; | |
166 | } | |
167 | if (events & G_IO_HUP) { | |
168 | ret |= SLIRP_POLL_HUP; | |
169 | } | |
170 | return ret; | |
165 | int ret = 0; | |
166 | if (events & G_IO_IN) { | |
167 | ret |= SLIRP_POLL_IN; | |
168 | } | |
169 | if (events & G_IO_OUT) { | |
170 | ret |= SLIRP_POLL_OUT; | |
171 | } | |
172 | if (events & G_IO_PRI) { | |
173 | ret |= SLIRP_POLL_PRI; | |
174 | } | |
175 | if (events & G_IO_ERR) { | |
176 | ret |= SLIRP_POLL_ERR; | |
177 | } | |
178 | if (events & G_IO_HUP) { | |
179 | ret |= SLIRP_POLL_HUP; | |
180 | } | |
181 | return ret; | |
171 | 182 | } |
172 | 183 | |
173 | 184 | /* |
174 | 185 | * implements SlirpGetREventsCB used in slirp_pollfds_poll |
175 | 186 | * originally from qemu/net/slirp.c:net_slirp_get_revents |
187 | * (4c76137484878f42a2ce1ae1b888b6a7f66b4053) | |
176 | 188 | */ |
177 | 189 | static int libslirp_get_revents(int idx, void *opaque) |
178 | 190 | { |
179 | GArray *pollfds = opaque; | |
180 | return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); | |
191 | GArray *pollfds = opaque; | |
192 | return libslirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); | |
181 | 193 | } |
182 | 194 | |
183 | 195 | /* |
184 | 196 | * updates timeout_msec for data->timers |
185 | * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66 | |
197 | * originally from | |
198 | * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L66 | |
186 | 199 | */ |
187 | static void update_ra_timeout(uint32_t * timeout_msec, struct libslirp_data *data) | |
188 | { | |
189 | int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; | |
190 | GSList *f; | |
191 | for (f = data->timers; f != NULL; f = f->next) { | |
192 | struct timer *t = f->data; | |
193 | if (t->expire_timer_msec != -1) { | |
194 | int64_t diff = t->expire_timer_msec - now_msec; | |
195 | if (diff < 0) | |
196 | diff = 0; | |
197 | if (diff < *timeout_msec) | |
198 | *timeout_msec = diff; | |
199 | } | |
200 | } | |
200 | static void update_ra_timeout(uint32_t *timeout_msec, | |
201 | struct libslirp_data *data) | |
202 | { | |
203 | int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; | |
204 | GSList *f; | |
205 | for (f = data->timers; f != NULL; f = f->next) { | |
206 | struct timer *t = f->data; | |
207 | if (t->expire_timer_msec != -1) { | |
208 | int64_t diff = t->expire_timer_msec - now_msec; | |
209 | if (diff < 0) | |
210 | diff = 0; | |
211 | if (diff < *timeout_msec) | |
212 | *timeout_msec = diff; | |
213 | } | |
214 | } | |
201 | 215 | } |
202 | 216 | |
203 | 217 | /* |
204 | 218 | * calls SlirpTimerCb if timed out |
205 | * originally from https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78 | |
219 | * originally from | |
220 | * https://github.com/rd235/libslirp/blob/d2b7032e29f3ba98e17414b32c9effffc90f2bb0/src/qemu2libslirp.c#L78 | |
206 | 221 | */ |
207 | 222 | static void check_ra_timeout(struct libslirp_data *data) |
208 | 223 | { |
209 | int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; | |
210 | GSList *f; | |
211 | for (f = data->timers; f != NULL; f = f->next) { | |
212 | struct timer *t = f->data; | |
213 | if (t->expire_timer_msec != -1) { | |
214 | int64_t diff = t->expire_timer_msec - now_msec; | |
215 | if (diff <= 0) { | |
216 | t->expire_timer_msec = -1; | |
217 | t->cb(t->cb_opaque); | |
218 | } | |
219 | } | |
220 | } | |
224 | int64_t now_msec = libslirp_clock_get_ns(data) / 1000000; | |
225 | GSList *f; | |
226 | for (f = data->timers; f != NULL; f = f->next) { | |
227 | struct timer *t = f->data; | |
228 | if (t->expire_timer_msec != -1) { | |
229 | int64_t diff = t->expire_timer_msec - now_msec; | |
230 | if (diff <= 0) { | |
231 | t->expire_timer_msec = -1; | |
232 | t->cb(t->cb_opaque); | |
233 | } | |
234 | } | |
235 | } | |
221 | 236 | } |
222 | 237 | |
223 | 238 | static const SlirpCb libslirp_cb = { |
224 | .send_packet = libslirp_send_packet, | |
225 | .guest_error = libslirp_guest_error, | |
226 | .clock_get_ns = libslirp_clock_get_ns, | |
227 | .timer_new = libslirp_timer_new, | |
228 | .timer_free = libslirp_timer_free, | |
229 | .timer_mod = libslirp_timer_mod, | |
230 | .register_poll_fd = libslirp_register_poll_fd, | |
231 | .unregister_poll_fd = libslirp_unregister_poll_fd, | |
232 | .notify = libslirp_notify, | |
239 | .send_packet = libslirp_send_packet, | |
240 | .guest_error = libslirp_guest_error, | |
241 | .clock_get_ns = libslirp_clock_get_ns, | |
242 | .timer_new = libslirp_timer_new, | |
243 | .timer_free = libslirp_timer_free, | |
244 | .timer_mod = libslirp_timer_mod, | |
245 | .register_poll_fd = libslirp_register_poll_fd, | |
246 | .unregister_poll_fd = libslirp_unregister_poll_fd, | |
247 | .notify = libslirp_notify, | |
233 | 248 | }; |
234 | 249 | |
235 | 250 | Slirp *create_slirp(void *opaque, struct slirp4netns_config *s4nn) |
236 | 251 | { |
237 | Slirp *slirp = NULL; | |
238 | SlirpConfig cfg; | |
239 | memset(&cfg, 0, sizeof(cfg)); | |
240 | cfg.version = 1; | |
241 | cfg.restricted = 0; | |
242 | cfg.in_enabled = 1; | |
243 | cfg.vnetwork = s4nn->vnetwork; | |
244 | cfg.vnetmask = s4nn->vnetmask; | |
245 | cfg.vhost = s4nn->vhost; | |
246 | cfg.in6_enabled = (int)(s4nn->enable_ipv6); | |
247 | inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6); | |
248 | cfg.vprefix_len = 64; | |
249 | inet_pton(AF_INET6, "fd00::2", &cfg.vhost6); | |
250 | cfg.vhostname = NULL; | |
251 | cfg.tftp_server_name = NULL; | |
252 | cfg.tftp_path = NULL; | |
253 | cfg.bootfile = NULL; | |
254 | cfg.vdhcp_start = s4nn->vdhcp_start; | |
255 | cfg.vnameserver = s4nn->vnameserver; | |
256 | inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6); | |
257 | cfg.vdnssearch = NULL; | |
258 | cfg.vdomainname = NULL; | |
259 | cfg.if_mtu = s4nn->mtu; | |
260 | cfg.if_mru = s4nn->mtu; | |
261 | cfg.disable_host_loopback = s4nn->disable_host_loopback; | |
262 | slirp = slirp_new(&cfg, &libslirp_cb, opaque); | |
263 | if (slirp == NULL) { | |
264 | fprintf(stderr, "slirp_initx failed\n"); | |
265 | } | |
266 | return slirp; | |
252 | Slirp *slirp = NULL; | |
253 | SlirpConfig cfg; | |
254 | memset(&cfg, 0, sizeof(cfg)); | |
255 | cfg.version = 1; | |
256 | cfg.restricted = 0; | |
257 | cfg.in_enabled = 1; | |
258 | cfg.vnetwork = s4nn->vnetwork; | |
259 | cfg.vnetmask = s4nn->vnetmask; | |
260 | cfg.vhost = s4nn->vhost; | |
261 | cfg.in6_enabled = (int)(s4nn->enable_ipv6); | |
262 | inet_pton(AF_INET6, "fd00::", &cfg.vprefix_addr6); | |
263 | cfg.vprefix_len = 64; | |
264 | inet_pton(AF_INET6, "fd00::2", &cfg.vhost6); | |
265 | cfg.vhostname = NULL; | |
266 | cfg.tftp_server_name = NULL; | |
267 | cfg.tftp_path = NULL; | |
268 | cfg.bootfile = NULL; | |
269 | cfg.vdhcp_start = s4nn->vdhcp_start; | |
270 | cfg.vnameserver = s4nn->vnameserver; | |
271 | inet_pton(AF_INET6, "fd00::3", &cfg.vnameserver6); | |
272 | cfg.vdnssearch = NULL; | |
273 | cfg.vdomainname = NULL; | |
274 | cfg.if_mtu = s4nn->mtu; | |
275 | cfg.if_mru = s4nn->mtu; | |
276 | cfg.disable_host_loopback = s4nn->disable_host_loopback; | |
277 | slirp = slirp_new(&cfg, &libslirp_cb, opaque); | |
278 | if (slirp == NULL) { | |
279 | fprintf(stderr, "slirp_new failed\n"); | |
280 | } | |
281 | return slirp; | |
267 | 282 | } |
268 | 283 | |
269 | 284 | #define ETH_BUF_SIZE (65536) |
270 | 285 | |
271 | int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg) | |
272 | { | |
273 | int ret = -1; | |
274 | Slirp *slirp = NULL; | |
275 | uint8_t *buf = NULL; | |
276 | struct libslirp_data opaque = {.tapfd = tapfd,.timers = NULL }; | |
277 | int apifd = -1; | |
278 | struct api_ctx *apictx = NULL; | |
279 | GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); | |
280 | int pollfds_exitfd_idx = -1; | |
281 | int pollfds_apifd_idx = -1; | |
282 | size_t n_fds = 1; | |
283 | GPollFD tap_pollfd = {.fd = tapfd,.events = G_IO_IN | G_IO_HUP,.revents = 0 | |
284 | }; | |
285 | GPollFD exit_pollfd = {.fd = exitfd,.events = G_IO_HUP,.revents = 0 | |
286 | }; | |
287 | GPollFD api_pollfd = {.fd = -1,.events = G_IO_IN | G_IO_HUP,.revents = 0 | |
288 | }; | |
289 | ||
290 | slirp = create_slirp((void *)&opaque, cfg); | |
291 | if (slirp == NULL) { | |
292 | fprintf(stderr, "create_slirp failed\n"); | |
293 | goto err; | |
294 | } | |
295 | buf = malloc(ETH_BUF_SIZE); | |
296 | if (buf == NULL) { | |
297 | goto err; | |
298 | } | |
299 | g_array_append_val(pollfds, tap_pollfd); | |
300 | if (exitfd >= 0) { | |
301 | n_fds++; | |
302 | g_array_append_val(pollfds, exit_pollfd); | |
303 | pollfds_exitfd_idx = n_fds - 1; | |
304 | } | |
305 | if (api_socket != NULL) { | |
306 | if ((apifd = api_bindlisten(api_socket)) < 0) { | |
307 | goto err; | |
308 | } | |
309 | if ((apictx = api_ctx_alloc(cfg)) == NULL) { | |
310 | fprintf(stderr, "api_ctx_alloc failed\n"); | |
311 | goto err; | |
312 | } | |
313 | api_pollfd.fd = apifd; | |
314 | n_fds++; | |
315 | g_array_append_val(pollfds, api_pollfd); | |
316 | pollfds_apifd_idx = n_fds - 1; | |
317 | } | |
318 | signal(SIGPIPE, SIG_IGN); | |
319 | while (1) { | |
320 | int pollout; | |
321 | GPollFD *pollfds_data; | |
322 | uint32_t timeout = -1; /* msec */ | |
323 | g_array_set_size(pollfds, n_fds); | |
324 | slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds); | |
325 | update_ra_timeout(&timeout, &opaque); | |
326 | pollfds_data = (GPollFD *)pollfds->data; | |
327 | do { | |
328 | pollout = g_poll(pollfds_data, pollfds->len, timeout); | |
329 | } while (pollout < 0 && errno == EINTR); | |
330 | if (pollout < 0) { | |
331 | goto err; | |
332 | } | |
333 | ||
334 | if (pollfds_data[0].revents) { | |
335 | ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE); | |
336 | if (rc < 0) { | |
337 | perror("do_slirp: read"); | |
338 | goto after_slirp_input; | |
339 | } | |
340 | slirp_input(slirp, buf, (int)rc); | |
341 | after_slirp_input: | |
342 | pollout = -1; | |
343 | } | |
344 | ||
345 | /* The exitfd is closed. */ | |
346 | if (pollfds_exitfd_idx >= 0 && pollfds_data[pollfds_exitfd_idx].revents) { | |
347 | fprintf(stderr, "exitfd event\n"); | |
348 | goto success; | |
349 | } | |
350 | ||
351 | if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) { | |
352 | int rc; | |
353 | fprintf(stderr, "apifd event\n"); | |
354 | if ((rc = api_handler(slirp, apifd, apictx)) < 0) { | |
355 | fprintf(stderr, "api_handler: rc=%d\n", rc); | |
356 | } | |
357 | } | |
358 | ||
359 | slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, pollfds); | |
360 | check_ra_timeout(&opaque); | |
361 | } | |
362 | success: | |
363 | ret = 0; | |
364 | err: | |
365 | fprintf(stderr, "do_slirp is exiting\n"); | |
366 | if (buf != NULL) { | |
367 | free(buf); | |
368 | } | |
369 | if (apictx != NULL) { | |
370 | api_ctx_free(apictx); | |
371 | unlink(api_socket); | |
372 | } | |
373 | g_array_free(pollfds, TRUE); | |
374 | return ret; | |
375 | } | |
286 | int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket, | |
287 | struct slirp4netns_config *cfg) | |
288 | { | |
289 | int ret = -1; | |
290 | Slirp *slirp = NULL; | |
291 | uint8_t *buf = NULL; | |
292 | struct libslirp_data opaque = { .tapfd = tapfd, .timers = NULL }; | |
293 | int apifd = -1; | |
294 | struct api_ctx *apictx = NULL; | |
295 | GArray *pollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); | |
296 | int pollfds_exitfd_idx = -1; | |
297 | int pollfds_apifd_idx = -1; | |
298 | size_t n_fds = 1; | |
299 | GPollFD tap_pollfd = { .fd = tapfd, | |
300 | .events = G_IO_IN | G_IO_HUP, | |
301 | .revents = 0 }; | |
302 | GPollFD exit_pollfd = { .fd = exitfd, .events = G_IO_HUP, .revents = 0 }; | |
303 | GPollFD api_pollfd = { .fd = -1, | |
304 | .events = G_IO_IN | G_IO_HUP, | |
305 | .revents = 0 }; | |
306 | ||
307 | slirp = create_slirp((void *)&opaque, cfg); | |
308 | if (slirp == NULL) { | |
309 | fprintf(stderr, "create_slirp failed\n"); | |
310 | goto err; | |
311 | } | |
312 | buf = malloc(ETH_BUF_SIZE); | |
313 | if (buf == NULL) { | |
314 | goto err; | |
315 | } | |
316 | g_array_append_val(pollfds, tap_pollfd); | |
317 | if (exitfd >= 0) { | |
318 | n_fds++; | |
319 | g_array_append_val(pollfds, exit_pollfd); | |
320 | pollfds_exitfd_idx = n_fds - 1; | |
321 | } | |
322 | if (api_socket != NULL) { | |
323 | if ((apifd = api_bindlisten(api_socket)) < 0) { | |
324 | goto err; | |
325 | } | |
326 | if ((apictx = api_ctx_alloc(cfg)) == NULL) { | |
327 | fprintf(stderr, "api_ctx_alloc failed\n"); | |
328 | goto err; | |
329 | } | |
330 | api_pollfd.fd = apifd; | |
331 | n_fds++; | |
332 | g_array_append_val(pollfds, api_pollfd); | |
333 | pollfds_apifd_idx = n_fds - 1; | |
334 | } | |
335 | signal(SIGPIPE, SIG_IGN); | |
336 | if (cfg->enable_sandbox && create_sandbox() < 0) { | |
337 | fprintf(stderr, "create_sandbox failed\n"); | |
338 | goto err; | |
339 | } | |
340 | if (cfg->enable_seccomp && enable_seccomp() < 0) { | |
341 | fprintf(stderr, "enable_seccomp failed\n"); | |
342 | goto err; | |
343 | } | |
344 | if (readyfd >= 0) { | |
345 | int rc = -1; | |
346 | do | |
347 | rc = write(readyfd, "1", 1); | |
348 | while (rc < 0 && errno == EINTR); | |
349 | close(readyfd); | |
350 | } | |
351 | while (1) { | |
352 | int pollout; | |
353 | GPollFD *pollfds_data; | |
354 | uint32_t timeout = -1; /* msec */ | |
355 | g_array_set_size(pollfds, n_fds); | |
356 | slirp_pollfds_fill(slirp, &timeout, libslirp_add_poll, pollfds); | |
357 | update_ra_timeout(&timeout, &opaque); | |
358 | pollfds_data = (GPollFD *)pollfds->data; | |
359 | do { | |
360 | pollout = g_poll(pollfds_data, pollfds->len, timeout); | |
361 | } while (pollout < 0 && errno == EINTR); | |
362 | if (pollout < 0) { | |
363 | goto err; | |
364 | } | |
365 | ||
366 | if (pollfds_data[0].revents) { | |
367 | ssize_t rc = read(tapfd, buf, ETH_BUF_SIZE); | |
368 | if (rc < 0) { | |
369 | perror("do_slirp: read"); | |
370 | goto after_slirp_input; | |
371 | } | |
372 | slirp_input(slirp, buf, (int)rc); | |
373 | after_slirp_input: | |
374 | pollout = -1; | |
375 | } | |
376 | ||
377 | /* The exitfd is closed. */ | |
378 | if (pollfds_exitfd_idx >= 0 && | |
379 | pollfds_data[pollfds_exitfd_idx].revents) { | |
380 | fprintf(stderr, "exitfd event\n"); | |
381 | goto success; | |
382 | } | |
383 | ||
384 | if (pollfds_apifd_idx >= 0 && pollfds_data[pollfds_apifd_idx].revents) { | |
385 | int rc; | |
386 | fprintf(stderr, "apifd event\n"); | |
387 | if ((rc = api_handler(slirp, apifd, apictx)) < 0) { | |
388 | fprintf(stderr, "api_handler: rc=%d\n", rc); | |
389 | } | |
390 | } | |
391 | ||
392 | slirp_pollfds_poll(slirp, (pollout <= 0), libslirp_get_revents, | |
393 | pollfds); | |
394 | check_ra_timeout(&opaque); | |
395 | } | |
396 | success: | |
397 | ret = 0; | |
398 | err: | |
399 | fprintf(stderr, "do_slirp is exiting\n"); | |
400 | if (buf != NULL) { | |
401 | free(buf); | |
402 | } | |
403 | if (apictx != NULL) { | |
404 | api_ctx_free(apictx); | |
405 | unlink(api_socket); | |
406 | } | |
407 | g_array_free(pollfds, TRUE); | |
408 | return ret; | |
409 | } |
0 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | |
0 | 1 | #ifndef SLIRP4NETNS_H |
1 | 2 | # define SLIRP4NETNS_H |
2 | 3 | #include <arpa/inet.h> |
11 | 12 | struct in_addr recommended_vguest; // 10.0.2.100 (slirp itself is unaware of vguest) |
12 | 13 | bool enable_ipv6; |
13 | 14 | bool disable_host_loopback; |
15 | bool enable_sandbox; | |
16 | bool enable_seccomp; | |
14 | 17 | }; |
15 | int do_slirp(int tapfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg); | |
18 | int do_slirp(int tapfd, int readyfd, int exitfd, const char *api_socket, struct slirp4netns_config *cfg); | |
16 | 19 | |
17 | 20 | #endif |
0 | #!/bin/bash | |
1 | set -xeuo pipefail | |
2 | ||
3 | . $(dirname $0)/common.sh | |
4 | ||
5 | # it is a part of test-slirp4netns.sh | |
6 | # must run in a new mount namespace | |
7 | ||
8 | mount -t tmpfs tmpfs /run | |
9 | mkdir /run/foo | |
10 | mount -t tmpfs tmpfs /run/foo | |
11 | mount --make-rshared /run | |
12 | ||
13 | unshare -n sleep infinity & | |
14 | child=$! | |
15 | ||
16 | wait_for_network_namespace $child | |
17 | ||
18 | ./slirp4netns --enable-sandbox --netns-type=path /proc/$child/ns/net tun11 & | |
19 | slirp_pid=$! | |
20 | ||
21 | function cleanup { | |
22 | kill -9 $child $slirp_pid | |
23 | } | |
24 | trap cleanup EXIT | |
25 | ||
26 | wait_for_network_device $child tun11 | |
27 | ||
28 | findmnt /run/foo |
9 | 9 | |
10 | 10 | tmpdir=$(mktemp -d /tmp/slirp4netns-bench.XXXXXXXXXX) |
11 | 11 | apisocket=${tmpdir}/slirp4netns.sock |
12 | apisocketlongpath=${tmpdir}/slirp4netns-TOO-LONG-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.sock | |
13 | ||
14 | if slirp4netns -c $child --api-socket $apisocketlongpath tun11; then | |
15 | echo "expected failure with apisocket path too long" >&2 | |
16 | kill -9 $child | |
17 | rm -rf $tmpdir | |
18 | exit 1 | |
19 | fi | |
20 | ||
12 | 21 | slirp4netns -c $child --api-socket $apisocket tun11 & |
13 | 22 | slirp_pid=$! |
14 | 23 |
2 | 2 | |
3 | 3 | . $(dirname $0)/common.sh |
4 | 4 | |
5 | ||
6 | # Test --netns-type=pid | |
5 | 7 | unshare -r -n sleep infinity & |
6 | 8 | child=$! |
7 | 9 | |
8 | 10 | wait_for_network_namespace $child |
9 | 11 | |
10 | slirp4netns $child tun11 & | |
12 | slirp4netns --ready-fd=3 --enable-sandbox $child tun11 3>ready.file & | |
11 | 13 | slirp_pid=$! |
12 | 14 | |
13 | wait_for_network_device $child tun11 | |
15 | # Wait that the sandbox is created | |
16 | wait_for_file_content 1 ready.file | |
17 | rm ready.file | |
18 | ||
19 | # Check there are no capabilities left in slirp4netns | |
20 | getpcaps $slirp_pid 2>&1 | tail -n1 > slirp.caps | |
21 | grep cap_net_bind_service slirp.caps | |
22 | grep -v cap_sys_admin slirp.caps | |
23 | rm slirp.caps | |
24 | test -e /proc/$slirp_pid/root/etc | |
25 | test -e /proc/$slirp_pid/root/run | |
26 | test \! -e /proc/$slirp_pid/root/home | |
27 | test \! -e /proc/$slirp_pid/root/root | |
28 | test \! -e /proc/$slirp_pid/root/var | |
14 | 29 | |
15 | 30 | function cleanup { |
16 | 31 | kill -9 $child $slirp_pid |
18 | 33 | trap cleanup EXIT |
19 | 34 | |
20 | 35 | nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11 |
36 | nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet | |
21 | 37 | |
38 | kill -9 $child $slirp_pid | |
39 | ||
40 | # Test --userns-path= --netns-type=path | |
41 | unshare -r -n sleep infinity & | |
42 | child=$! | |
43 | ||
44 | wait_for_network_namespace $child | |
45 | ||
46 | slirp4netns --userns-path=/proc/$child/ns/user --netns-type=path /proc/$child/ns/net tun11 & | |
47 | slirp_pid=$! | |
48 | ||
49 | wait_for_network_device $child tun11 | |
50 | ||
51 | nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11 | |
22 | 52 | nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet |
53 | ||
54 | kill -9 $child $slirp_pid | |
55 | ||
56 | # Test --netns-type=path | |
57 | unshare -r -n sleep infinity & | |
58 | child=$! | |
59 | ||
60 | wait_for_network_namespace $child | |
61 | ||
62 | nsenter --preserve-credentials -U --target=$child slirp4netns --netns-type=path /proc/$child/ns/net tun11 & | |
63 | slirp_pid=$! | |
64 | ||
65 | wait_for_network_device $child tun11 | |
66 | ||
67 | nsenter --preserve-credentials -U -n --target=$child ip -a netconf | grep tun11 | |
68 | nsenter --preserve-credentials -U -n --target=$child ip addr show tun11 | grep -v inet | |
69 | ||
70 | unshare -rm $(readlink -f $(dirname $0)/slirp4netns-no-unmount.sh) |
0 | 0 | # DO NOT EDIT MANUALLY |
1 | 1 | |
2 | 2 | Vendored components: |
3 | * libslirp: https://gitlab.freedesktop.org/slirp/libslirp.git (`76462e2f16c6fce6856fb914cbef6207d0be4bc5`) | |
4 | * parson: https://github.com/kgabis/parson.git (`c5bb9557fe98367aa8e041c65863909f12ee76b2`) | |
3 | * parson: https://github.com/kgabis/parson.git (`70dc239f8f54c80bf58477b25435fd3dd3102804`) | |
5 | 4 | |
6 | 5 | Please do not edit the contents under this directory manually. |
7 | 6 | |
8 | See also [`../vendor.md`](../vendor.md). | |
7 | Use [`../vendor.sh`](../vendor.sh) to update the contents. |
0 | # https://clang.llvm.org/docs/ClangFormat.html | |
1 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html | |
2 | --- | |
3 | Language: Cpp | |
4 | AlignAfterOpenBracket: Align | |
5 | AlignConsecutiveAssignments: false # although we like it, it creates churn | |
6 | AlignConsecutiveDeclarations: false | |
7 | AlignEscapedNewlinesLeft: true | |
8 | AlignOperands: true | |
9 | AlignTrailingComments: false # churn | |
10 | AllowAllParametersOfDeclarationOnNextLine: true | |
11 | AllowShortBlocksOnASingleLine: false | |
12 | AllowShortCaseLabelsOnASingleLine: false | |
13 | AllowShortFunctionsOnASingleLine: None | |
14 | AllowShortIfStatementsOnASingleLine: false | |
15 | AllowShortLoopsOnASingleLine: false | |
16 | AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account | |
17 | AlwaysBreakBeforeMultilineStrings: false | |
18 | BinPackArguments: true | |
19 | BinPackParameters: true | |
20 | BraceWrapping: | |
21 | AfterControlStatement: false | |
22 | AfterEnum: false | |
23 | AfterFunction: true | |
24 | AfterStruct: false | |
25 | AfterUnion: false | |
26 | BeforeElse: false | |
27 | IndentBraces: false | |
28 | BreakBeforeBinaryOperators: None | |
29 | BreakBeforeBraces: Custom | |
30 | BreakBeforeTernaryOperators: false | |
31 | BreakStringLiterals: true | |
32 | ColumnLimit: 80 | |
33 | ContinuationIndentWidth: 4 | |
34 | Cpp11BracedListStyle: false | |
35 | DerivePointerAlignment: false | |
36 | DisableFormat: false | |
37 | IndentCaseLabels: false | |
38 | IndentWidth: 4 | |
39 | IndentWrappedFunctionNames: false | |
40 | KeepEmptyLinesAtTheStartOfBlocks: false | |
41 | MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? | |
42 | MacroBlockEnd: '.*_END$' | |
43 | MaxEmptyLinesToKeep: 2 | |
44 | PointerAlignment: Right | |
45 | ReflowComments: true | |
46 | SortIncludes: false | |
47 | SpaceAfterCStyleCast: false | |
48 | SpaceBeforeAssignmentOperators: true | |
49 | SpaceBeforeParens: ControlStatements | |
50 | SpaceInEmptyParentheses: false | |
51 | SpacesBeforeTrailingComments: 1 | |
52 | SpacesInContainerLiterals: true | |
53 | SpacesInParentheses: false | |
54 | SpacesInSquareBrackets: false | |
55 | Standard: Auto | |
56 | UseTab: Never | |
57 | ... |
0 | Slirp was written by Danny Gasparovski. | |
1 | Copyright (c), 1995,1996 All Rights Reserved. | |
2 | ||
3 | Slirp is free software; "free" as in you don't have to pay for it, and you | |
4 | are free to do whatever you want with it. I do not accept any donations, | |
5 | monetary or otherwise, for Slirp. Instead, I would ask you to pass this | |
6 | potential donation to your favorite charity. In fact, I encourage | |
7 | *everyone* who finds Slirp useful to make a small donation to their | |
8 | favorite charity (for example, GreenPeace). This is not a requirement, but | |
9 | a suggestion from someone who highly values the service they provide. | |
10 | ||
11 | The copyright terms and conditions: | |
12 | ||
13 | ---BEGIN--- | |
14 | ||
15 | Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved. | |
16 | ||
17 | Redistribution and use in source and binary forms, with or without | |
18 | modification, are permitted provided that the following conditions | |
19 | are met: | |
20 | 1. Redistributions of source code must retain the above copyright | |
21 | notice, this list of conditions and the following disclaimer. | |
22 | 2. Redistributions in binary form must reproduce the above copyright | |
23 | notice, this list of conditions and the following disclaimer in the | |
24 | documentation and/or other materials provided with the distribution. | |
25 | 3. Neither the name of the copyright holder nor the names of its | |
26 | contributors may be used to endorse or promote products derived | |
27 | from this software without specific prior written permission. | |
28 | ||
29 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
30 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |
31 | AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | |
32 | DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
33 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
34 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
35 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
36 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
37 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
38 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
39 | ||
40 | ---END--- | |
41 | ||
42 | This basically means you can do anything you want with the software, except | |
43 | 1) call it your own, and 2) claim warranty on it. There is no warranty for | |
44 | this software. None. Nada. If you lose a million dollars while using | |
45 | Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***. | |
46 | ||
47 | If these conditions cannot be met due to legal restrictions (E.g. where it | |
48 | is against the law to give out Software without warranty), you must cease | |
49 | using the software and delete all copies you have. | |
50 | ||
51 | Slirp uses code that is copyrighted by the following people/organizations: | |
52 | ||
53 | Juha Pirkola. | |
54 | Gregory M. Christy. | |
55 | The Regents of the University of California. | |
56 | Carnegie Mellon University. | |
57 | The Australian National University. | |
58 | RSA Data Security, Inc. | |
59 | ||
60 | Please read the top of each source file for the details on the various | |
61 | copyrights. |
0 | # libslirp | |
1 | ||
2 | libslirp is a user-mode networking library used by virtual machines, | |
3 | containers or various tools. | |
4 | ||
5 | ## Getting Started | |
6 | ||
7 | ### Prerequisites | |
8 | ||
9 | A C compiler, make/meson and glib2 development libraries. | |
10 | ||
11 | (see also [.gitlab-ci.yml](.gitlab-ci.yml) DEPS variable for the list | |
12 | of dependencies on Fedora) | |
13 | ||
14 | ### Building | |
15 | ||
16 | You may build and install the shared library with meson: | |
17 | ||
18 | ``` sh | |
19 | meson build | |
20 | ninja -C build install | |
21 | ``` | |
22 | And configure QEMU with --enable-slirp=system to link against it. | |
23 | ||
24 | (QEMU may build with the submodule static library using --enable-slirp=git) | |
25 | ||
26 | ### Testing | |
27 | ||
28 | Unfortunately, there are no automated tests available. | |
29 | ||
30 | You may run QEMU ``-net user`` linked with your development version. | |
31 | ||
32 | ## Contributing | |
33 | ||
34 | Feel free to open issues on the [project | |
35 | issues](https://gitlab.freedesktop.org/slirp/libslirp/issues) page. | |
36 | ||
37 | You may clone the [gitlab | |
38 | project](https://gitlab.freedesktop.org/slirp/libslirp) and create a | |
39 | merge request. | |
40 | ||
41 | Contributing with gitlab allows gitlab workflow, tracking issues, | |
42 | running CI etc. | |
43 | ||
44 | Alternatively, you may send patches to slirp@lists.freedesktop.org | |
45 | mailing list. | |
46 | ||
47 | ## Versioning | |
48 | ||
49 | We intend to use [libtool's | |
50 | versioning](https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html) | |
51 | for the shared libraries and use [SemVer](http://semver.org/) for | |
52 | project versions. | |
53 | ||
54 | For the versions available, see the [tags on this | |
55 | repository](https://gitlab.freedesktop.org/slirp/libslirp/releases). | |
56 | ||
57 | ## License | |
58 | ||
59 | See the [COPYRIGHT](COPYRIGHT) file for details. |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * ARP table | |
3 | * | |
4 | * Copyright (c) 2011 AdaCore | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "slirp.h" | |
26 | ||
27 | #include <string.h> | |
28 | ||
29 | void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) | |
30 | { | |
31 | const uint32_t broadcast_addr = | |
32 | ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; | |
33 | ArpTable *arptbl = &slirp->arp_table; | |
34 | int i; | |
35 | ||
36 | DEBUG_CALL("arp_table_add"); | |
37 | DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr })); | |
38 | DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", ethaddr[0], ethaddr[1], | |
39 | ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5]); | |
40 | ||
41 | if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { | |
42 | /* Do not register broadcast addresses */ | |
43 | return; | |
44 | } | |
45 | ||
46 | /* Search for an entry */ | |
47 | for (i = 0; i < ARP_TABLE_SIZE; i++) { | |
48 | if (arptbl->table[i].ar_sip == ip_addr) { | |
49 | /* Update the entry */ | |
50 | memcpy(arptbl->table[i].ar_sha, ethaddr, ETH_ALEN); | |
51 | return; | |
52 | } | |
53 | } | |
54 | ||
55 | /* No entry found, create a new one */ | |
56 | arptbl->table[arptbl->next_victim].ar_sip = ip_addr; | |
57 | memcpy(arptbl->table[arptbl->next_victim].ar_sha, ethaddr, ETH_ALEN); | |
58 | arptbl->next_victim = (arptbl->next_victim + 1) % ARP_TABLE_SIZE; | |
59 | } | |
60 | ||
61 | bool arp_table_search(Slirp *slirp, uint32_t ip_addr, | |
62 | uint8_t out_ethaddr[ETH_ALEN]) | |
63 | { | |
64 | const uint32_t broadcast_addr = | |
65 | ~slirp->vnetwork_mask.s_addr | slirp->vnetwork_addr.s_addr; | |
66 | ArpTable *arptbl = &slirp->arp_table; | |
67 | int i; | |
68 | ||
69 | DEBUG_CALL("arp_table_search"); | |
70 | DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr })); | |
71 | ||
72 | /* If broadcast address */ | |
73 | if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { | |
74 | /* return Ethernet broadcast address */ | |
75 | memset(out_ethaddr, 0xff, ETH_ALEN); | |
76 | return 1; | |
77 | } | |
78 | ||
79 | for (i = 0; i < ARP_TABLE_SIZE; i++) { | |
80 | if (arptbl->table[i].ar_sip == ip_addr) { | |
81 | memcpy(out_ethaddr, arptbl->table[i].ar_sha, ETH_ALEN); | |
82 | DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x", | |
83 | out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], | |
84 | out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); | |
85 | return 1; | |
86 | } | |
87 | } | |
88 | ||
89 | return 0; | |
90 | } |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * QEMU BOOTP/DHCP server | |
3 | * | |
4 | * Copyright (c) 2004 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "slirp.h" | |
25 | ||
26 | #if defined(_WIN32) | |
27 | /* Windows ntohl() returns an u_long value. | |
28 | * Add a type cast to match the format strings. */ | |
29 | #define ntohl(n) ((uint32_t)ntohl(n)) | |
30 | #endif | |
31 | ||
32 | /* XXX: only DHCP is supported */ | |
33 | ||
34 | #define LEASE_TIME (24 * 3600) | |
35 | ||
36 | static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; | |
37 | ||
38 | #define DPRINTF(fmt, ...) DEBUG_CALL(fmt, ##__VA_ARGS__) | |
39 | ||
40 | static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr, | |
41 | const uint8_t *macaddr) | |
42 | { | |
43 | BOOTPClient *bc; | |
44 | int i; | |
45 | ||
46 | for (i = 0; i < NB_BOOTP_CLIENTS; i++) { | |
47 | bc = &slirp->bootp_clients[i]; | |
48 | if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) | |
49 | goto found; | |
50 | } | |
51 | return NULL; | |
52 | found: | |
53 | bc = &slirp->bootp_clients[i]; | |
54 | bc->allocated = 1; | |
55 | paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); | |
56 | return bc; | |
57 | } | |
58 | ||
59 | static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr, | |
60 | const uint8_t *macaddr) | |
61 | { | |
62 | uint32_t req_addr = ntohl(paddr->s_addr); | |
63 | uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr); | |
64 | BOOTPClient *bc; | |
65 | ||
66 | if (req_addr >= dhcp_addr && req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) { | |
67 | bc = &slirp->bootp_clients[req_addr - dhcp_addr]; | |
68 | if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) { | |
69 | bc->allocated = 1; | |
70 | return bc; | |
71 | } | |
72 | } | |
73 | return NULL; | |
74 | } | |
75 | ||
76 | static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr, | |
77 | const uint8_t *macaddr) | |
78 | { | |
79 | BOOTPClient *bc; | |
80 | int i; | |
81 | ||
82 | for (i = 0; i < NB_BOOTP_CLIENTS; i++) { | |
83 | if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6)) | |
84 | goto found; | |
85 | } | |
86 | return NULL; | |
87 | found: | |
88 | bc = &slirp->bootp_clients[i]; | |
89 | bc->allocated = 1; | |
90 | paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i); | |
91 | return bc; | |
92 | } | |
93 | ||
94 | static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type, | |
95 | struct in_addr *preq_addr) | |
96 | { | |
97 | const uint8_t *p, *p_end; | |
98 | int len, tag; | |
99 | ||
100 | *pmsg_type = 0; | |
101 | preq_addr->s_addr = htonl(0L); | |
102 | ||
103 | p = bp->bp_vend; | |
104 | p_end = p + DHCP_OPT_LEN; | |
105 | if (memcmp(p, rfc1533_cookie, 4) != 0) | |
106 | return; | |
107 | p += 4; | |
108 | while (p < p_end) { | |
109 | tag = p[0]; | |
110 | if (tag == RFC1533_PAD) { | |
111 | p++; | |
112 | } else if (tag == RFC1533_END) { | |
113 | break; | |
114 | } else { | |
115 | p++; | |
116 | if (p >= p_end) | |
117 | break; | |
118 | len = *p++; | |
119 | if (p + len > p_end) { | |
120 | break; | |
121 | } | |
122 | DPRINTF("dhcp: tag=%d len=%d\n", tag, len); | |
123 | ||
124 | switch (tag) { | |
125 | case RFC2132_MSG_TYPE: | |
126 | if (len >= 1) | |
127 | *pmsg_type = p[0]; | |
128 | break; | |
129 | case RFC2132_REQ_ADDR: | |
130 | if (len >= 4) { | |
131 | memcpy(&(preq_addr->s_addr), p, 4); | |
132 | } | |
133 | break; | |
134 | default: | |
135 | break; | |
136 | } | |
137 | p += len; | |
138 | } | |
139 | } | |
140 | if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) && | |
141 | bp->bp_ciaddr.s_addr) { | |
142 | memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4); | |
143 | } | |
144 | } | |
145 | ||
146 | static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) | |
147 | { | |
148 | BOOTPClient *bc = NULL; | |
149 | struct mbuf *m; | |
150 | struct bootp_t *rbp; | |
151 | struct sockaddr_in saddr, daddr; | |
152 | struct in_addr preq_addr; | |
153 | int dhcp_msg_type, val; | |
154 | uint8_t *q; | |
155 | uint8_t *end; | |
156 | uint8_t client_ethaddr[ETH_ALEN]; | |
157 | ||
158 | /* extract exact DHCP msg type */ | |
159 | dhcp_decode(bp, &dhcp_msg_type, &preq_addr); | |
160 | DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); | |
161 | if (preq_addr.s_addr != htonl(0L)) | |
162 | DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); | |
163 | else { | |
164 | DPRINTF("\n"); | |
165 | } | |
166 | ||
167 | if (dhcp_msg_type == 0) | |
168 | dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ | |
169 | ||
170 | if (dhcp_msg_type != DHCPDISCOVER && dhcp_msg_type != DHCPREQUEST) | |
171 | return; | |
172 | ||
173 | /* Get client's hardware address from bootp request */ | |
174 | memcpy(client_ethaddr, bp->bp_hwaddr, ETH_ALEN); | |
175 | ||
176 | m = m_get(slirp); | |
177 | if (!m) { | |
178 | return; | |
179 | } | |
180 | m->m_data += IF_MAXLINKHDR; | |
181 | rbp = (struct bootp_t *)m->m_data; | |
182 | m->m_data += sizeof(struct udpiphdr); | |
183 | memset(rbp, 0, sizeof(struct bootp_t)); | |
184 | ||
185 | if (dhcp_msg_type == DHCPDISCOVER) { | |
186 | if (preq_addr.s_addr != htonl(0L)) { | |
187 | bc = request_addr(slirp, &preq_addr, client_ethaddr); | |
188 | if (bc) { | |
189 | daddr.sin_addr = preq_addr; | |
190 | } | |
191 | } | |
192 | if (!bc) { | |
193 | new_addr: | |
194 | bc = get_new_addr(slirp, &daddr.sin_addr, client_ethaddr); | |
195 | if (!bc) { | |
196 | DPRINTF("no address left\n"); | |
197 | return; | |
198 | } | |
199 | } | |
200 | memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); | |
201 | } else if (preq_addr.s_addr != htonl(0L)) { | |
202 | bc = request_addr(slirp, &preq_addr, client_ethaddr); | |
203 | if (bc) { | |
204 | daddr.sin_addr = preq_addr; | |
205 | memcpy(bc->macaddr, client_ethaddr, ETH_ALEN); | |
206 | } else { | |
207 | /* DHCPNAKs should be sent to broadcast */ | |
208 | daddr.sin_addr.s_addr = 0xffffffff; | |
209 | } | |
210 | } else { | |
211 | bc = find_addr(slirp, &daddr.sin_addr, bp->bp_hwaddr); | |
212 | if (!bc) { | |
213 | /* if never assigned, behaves as if it was already | |
214 | assigned (windows fix because it remembers its address) */ | |
215 | goto new_addr; | |
216 | } | |
217 | } | |
218 | ||
219 | /* Update ARP table for this IP address */ | |
220 | arp_table_add(slirp, daddr.sin_addr.s_addr, client_ethaddr); | |
221 | ||
222 | saddr.sin_addr = slirp->vhost_addr; | |
223 | saddr.sin_port = htons(BOOTP_SERVER); | |
224 | ||
225 | daddr.sin_port = htons(BOOTP_CLIENT); | |
226 | ||
227 | rbp->bp_op = BOOTP_REPLY; | |
228 | rbp->bp_xid = bp->bp_xid; | |
229 | rbp->bp_htype = 1; | |
230 | rbp->bp_hlen = 6; | |
231 | memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, ETH_ALEN); | |
232 | ||
233 | rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */ | |
234 | rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ | |
235 | ||
236 | q = rbp->bp_vend; | |
237 | end = (uint8_t *)&rbp[1]; | |
238 | memcpy(q, rfc1533_cookie, 4); | |
239 | q += 4; | |
240 | ||
241 | if (bc) { | |
242 | DPRINTF("%s addr=%08" PRIx32 "\n", | |
243 | (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", | |
244 | ntohl(daddr.sin_addr.s_addr)); | |
245 | ||
246 | if (dhcp_msg_type == DHCPDISCOVER) { | |
247 | *q++ = RFC2132_MSG_TYPE; | |
248 | *q++ = 1; | |
249 | *q++ = DHCPOFFER; | |
250 | } else /* DHCPREQUEST */ { | |
251 | *q++ = RFC2132_MSG_TYPE; | |
252 | *q++ = 1; | |
253 | *q++ = DHCPACK; | |
254 | } | |
255 | ||
256 | if (slirp->bootp_filename) | |
257 | snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s", | |
258 | slirp->bootp_filename); | |
259 | ||
260 | *q++ = RFC2132_SRV_ID; | |
261 | *q++ = 4; | |
262 | memcpy(q, &saddr.sin_addr, 4); | |
263 | q += 4; | |
264 | ||
265 | *q++ = RFC1533_NETMASK; | |
266 | *q++ = 4; | |
267 | memcpy(q, &slirp->vnetwork_mask, 4); | |
268 | q += 4; | |
269 | ||
270 | if (!slirp->restricted) { | |
271 | *q++ = RFC1533_GATEWAY; | |
272 | *q++ = 4; | |
273 | memcpy(q, &saddr.sin_addr, 4); | |
274 | q += 4; | |
275 | ||
276 | *q++ = RFC1533_DNS; | |
277 | *q++ = 4; | |
278 | memcpy(q, &slirp->vnameserver_addr, 4); | |
279 | q += 4; | |
280 | } | |
281 | ||
282 | *q++ = RFC2132_LEASE_TIME; | |
283 | *q++ = 4; | |
284 | val = htonl(LEASE_TIME); | |
285 | memcpy(q, &val, 4); | |
286 | q += 4; | |
287 | ||
288 | if (*slirp->client_hostname) { | |
289 | val = strlen(slirp->client_hostname); | |
290 | if (q + val + 2 >= end) { | |
291 | g_warning("DHCP packet size exceeded, " | |
292 | "omitting host name option."); | |
293 | } else { | |
294 | *q++ = RFC1533_HOSTNAME; | |
295 | *q++ = val; | |
296 | memcpy(q, slirp->client_hostname, val); | |
297 | q += val; | |
298 | } | |
299 | } | |
300 | ||
301 | if (slirp->vdomainname) { | |
302 | val = strlen(slirp->vdomainname); | |
303 | if (q + val + 2 >= end) { | |
304 | g_warning("DHCP packet size exceeded, " | |
305 | "omitting domain name option."); | |
306 | } else { | |
307 | *q++ = RFC1533_DOMAINNAME; | |
308 | *q++ = val; | |
309 | memcpy(q, slirp->vdomainname, val); | |
310 | q += val; | |
311 | } | |
312 | } | |
313 | ||
314 | if (slirp->tftp_server_name) { | |
315 | val = strlen(slirp->tftp_server_name); | |
316 | if (q + val + 2 >= end) { | |
317 | g_warning("DHCP packet size exceeded, " | |
318 | "omitting tftp-server-name option."); | |
319 | } else { | |
320 | *q++ = RFC2132_TFTP_SERVER_NAME; | |
321 | *q++ = val; | |
322 | memcpy(q, slirp->tftp_server_name, val); | |
323 | q += val; | |
324 | } | |
325 | } | |
326 | ||
327 | if (slirp->vdnssearch) { | |
328 | val = slirp->vdnssearch_len; | |
329 | if (q + val >= end) { | |
330 | g_warning("DHCP packet size exceeded, " | |
331 | "omitting domain-search option."); | |
332 | } else { | |
333 | memcpy(q, slirp->vdnssearch, val); | |
334 | q += val; | |
335 | } | |
336 | } | |
337 | } else { | |
338 | static const char nak_msg[] = "requested address not available"; | |
339 | ||
340 | DPRINTF("nak'ed addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); | |
341 | ||
342 | *q++ = RFC2132_MSG_TYPE; | |
343 | *q++ = 1; | |
344 | *q++ = DHCPNAK; | |
345 | ||
346 | *q++ = RFC2132_MESSAGE; | |
347 | *q++ = sizeof(nak_msg) - 1; | |
348 | memcpy(q, nak_msg, sizeof(nak_msg) - 1); | |
349 | q += sizeof(nak_msg) - 1; | |
350 | } | |
351 | assert(q < end); | |
352 | *q = RFC1533_END; | |
353 | ||
354 | daddr.sin_addr.s_addr = 0xffffffffu; | |
355 | ||
356 | m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr); | |
357 | udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); | |
358 | } | |
359 | ||
360 | void bootp_input(struct mbuf *m) | |
361 | { | |
362 | struct bootp_t *bp = mtod(m, struct bootp_t *); | |
363 | ||
364 | if (bp->bp_op == BOOTP_REQUEST) { | |
365 | bootp_reply(m->slirp, bp); | |
366 | } | |
367 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* bootp/dhcp defines */ | |
2 | ||
3 | #ifndef SLIRP_BOOTP_H | |
4 | #define SLIRP_BOOTP_H | |
5 | ||
6 | #define BOOTP_SERVER 67 | |
7 | #define BOOTP_CLIENT 68 | |
8 | ||
9 | #define BOOTP_REQUEST 1 | |
10 | #define BOOTP_REPLY 2 | |
11 | ||
12 | #define RFC1533_COOKIE 99, 130, 83, 99 | |
13 | #define RFC1533_PAD 0 | |
14 | #define RFC1533_NETMASK 1 | |
15 | #define RFC1533_TIMEOFFSET 2 | |
16 | #define RFC1533_GATEWAY 3 | |
17 | #define RFC1533_TIMESERVER 4 | |
18 | #define RFC1533_IEN116NS 5 | |
19 | #define RFC1533_DNS 6 | |
20 | #define RFC1533_LOGSERVER 7 | |
21 | #define RFC1533_COOKIESERVER 8 | |
22 | #define RFC1533_LPRSERVER 9 | |
23 | #define RFC1533_IMPRESSSERVER 10 | |
24 | #define RFC1533_RESOURCESERVER 11 | |
25 | #define RFC1533_HOSTNAME 12 | |
26 | #define RFC1533_BOOTFILESIZE 13 | |
27 | #define RFC1533_MERITDUMPFILE 14 | |
28 | #define RFC1533_DOMAINNAME 15 | |
29 | #define RFC1533_SWAPSERVER 16 | |
30 | #define RFC1533_ROOTPATH 17 | |
31 | #define RFC1533_EXTENSIONPATH 18 | |
32 | #define RFC1533_IPFORWARDING 19 | |
33 | #define RFC1533_IPSOURCEROUTING 20 | |
34 | #define RFC1533_IPPOLICYFILTER 21 | |
35 | #define RFC1533_IPMAXREASSEMBLY 22 | |
36 | #define RFC1533_IPTTL 23 | |
37 | #define RFC1533_IPMTU 24 | |
38 | #define RFC1533_IPMTUPLATEAU 25 | |
39 | #define RFC1533_INTMTU 26 | |
40 | #define RFC1533_INTLOCALSUBNETS 27 | |
41 | #define RFC1533_INTBROADCAST 28 | |
42 | #define RFC1533_INTICMPDISCOVER 29 | |
43 | #define RFC1533_INTICMPRESPOND 30 | |
44 | #define RFC1533_INTROUTEDISCOVER 31 | |
45 | #define RFC1533_INTROUTESOLICIT 32 | |
46 | #define RFC1533_INTSTATICROUTES 33 | |
47 | #define RFC1533_LLTRAILERENCAP 34 | |
48 | #define RFC1533_LLARPCACHETMO 35 | |
49 | #define RFC1533_LLETHERNETENCAP 36 | |
50 | #define RFC1533_TCPTTL 37 | |
51 | #define RFC1533_TCPKEEPALIVETMO 38 | |
52 | #define RFC1533_TCPKEEPALIVEGB 39 | |
53 | #define RFC1533_NISDOMAIN 40 | |
54 | #define RFC1533_NISSERVER 41 | |
55 | #define RFC1533_NTPSERVER 42 | |
56 | #define RFC1533_VENDOR 43 | |
57 | #define RFC1533_NBNS 44 | |
58 | #define RFC1533_NBDD 45 | |
59 | #define RFC1533_NBNT 46 | |
60 | #define RFC1533_NBSCOPE 47 | |
61 | #define RFC1533_XFS 48 | |
62 | #define RFC1533_XDM 49 | |
63 | ||
64 | #define RFC2132_REQ_ADDR 50 | |
65 | #define RFC2132_LEASE_TIME 51 | |
66 | #define RFC2132_MSG_TYPE 53 | |
67 | #define RFC2132_SRV_ID 54 | |
68 | #define RFC2132_PARAM_LIST 55 | |
69 | #define RFC2132_MESSAGE 56 | |
70 | #define RFC2132_MAX_SIZE 57 | |
71 | #define RFC2132_RENEWAL_TIME 58 | |
72 | #define RFC2132_REBIND_TIME 59 | |
73 | #define RFC2132_TFTP_SERVER_NAME 66 | |
74 | ||
75 | #define DHCPDISCOVER 1 | |
76 | #define DHCPOFFER 2 | |
77 | #define DHCPREQUEST 3 | |
78 | #define DHCPACK 5 | |
79 | #define DHCPNAK 6 | |
80 | ||
81 | #define RFC1533_VENDOR_MAJOR 0 | |
82 | #define RFC1533_VENDOR_MINOR 0 | |
83 | ||
84 | #define RFC1533_VENDOR_MAGIC 128 | |
85 | #define RFC1533_VENDOR_ADDPARM 129 | |
86 | #define RFC1533_VENDOR_ETHDEV 130 | |
87 | #define RFC1533_VENDOR_HOWTO 132 | |
88 | #define RFC1533_VENDOR_MNUOPTS 160 | |
89 | #define RFC1533_VENDOR_SELECTION 176 | |
90 | #define RFC1533_VENDOR_MOTD 184 | |
91 | #define RFC1533_VENDOR_NUMOFMOTD 8 | |
92 | #define RFC1533_VENDOR_IMG 192 | |
93 | #define RFC1533_VENDOR_NUMOFIMG 16 | |
94 | ||
95 | #define RFC1533_END 255 | |
96 | #define BOOTP_VENDOR_LEN 64 | |
97 | #define DHCP_OPT_LEN 312 | |
98 | ||
99 | struct bootp_t { | |
100 | struct ip ip; | |
101 | struct udphdr udp; | |
102 | uint8_t bp_op; | |
103 | uint8_t bp_htype; | |
104 | uint8_t bp_hlen; | |
105 | uint8_t bp_hops; | |
106 | uint32_t bp_xid; | |
107 | uint16_t bp_secs; | |
108 | uint16_t unused; | |
109 | struct in_addr bp_ciaddr; | |
110 | struct in_addr bp_yiaddr; | |
111 | struct in_addr bp_siaddr; | |
112 | struct in_addr bp_giaddr; | |
113 | uint8_t bp_hwaddr[16]; | |
114 | uint8_t bp_sname[64]; | |
115 | uint8_t bp_file[128]; | |
116 | uint8_t bp_vend[DHCP_OPT_LEN]; | |
117 | }; | |
118 | ||
119 | typedef struct { | |
120 | uint16_t allocated; | |
121 | uint8_t macaddr[6]; | |
122 | } BOOTPClient; | |
123 | ||
124 | #define NB_BOOTP_CLIENTS 16 | |
125 | ||
126 | void bootp_input(struct mbuf *m); | |
127 | ||
128 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1988, 1992, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 | |
30 | * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp | |
31 | */ | |
32 | ||
33 | #include "slirp.h" | |
34 | ||
35 | /* | |
36 | * Checksum routine for Internet Protocol family headers (Portable Version). | |
37 | * | |
38 | * This routine is very heavily used in the network | |
39 | * code and should be modified for each CPU to be as fast as possible. | |
40 | * | |
41 | * XXX Since we will never span more than 1 mbuf, we can optimise this | |
42 | */ | |
43 | ||
44 | #define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) | |
45 | #define REDUCE \ | |
46 | { \ | |
47 | l_util.l = sum; \ | |
48 | sum = l_util.s[0] + l_util.s[1]; \ | |
49 | (void)ADDCARRY(sum); \ | |
50 | } | |
51 | ||
52 | int cksum(struct mbuf *m, int len) | |
53 | { | |
54 | register uint16_t *w; | |
55 | register int sum = 0; | |
56 | register int mlen = 0; | |
57 | int byte_swapped = 0; | |
58 | ||
59 | union { | |
60 | uint8_t c[2]; | |
61 | uint16_t s; | |
62 | } s_util; | |
63 | union { | |
64 | uint16_t s[2]; | |
65 | uint32_t l; | |
66 | } l_util; | |
67 | ||
68 | if (m->m_len == 0) | |
69 | goto cont; | |
70 | w = mtod(m, uint16_t *); | |
71 | ||
72 | mlen = m->m_len; | |
73 | ||
74 | if (len < mlen) | |
75 | mlen = len; | |
76 | len -= mlen; | |
77 | /* | |
78 | * Force to even boundary. | |
79 | */ | |
80 | if ((1 & (uintptr_t)w) && (mlen > 0)) { | |
81 | REDUCE; | |
82 | sum <<= 8; | |
83 | s_util.c[0] = *(uint8_t *)w; | |
84 | w = (uint16_t *)((int8_t *)w + 1); | |
85 | mlen--; | |
86 | byte_swapped = 1; | |
87 | } | |
88 | /* | |
89 | * Unroll the loop to make overhead from | |
90 | * branches &c small. | |
91 | */ | |
92 | while ((mlen -= 32) >= 0) { | |
93 | sum += w[0]; | |
94 | sum += w[1]; | |
95 | sum += w[2]; | |
96 | sum += w[3]; | |
97 | sum += w[4]; | |
98 | sum += w[5]; | |
99 | sum += w[6]; | |
100 | sum += w[7]; | |
101 | sum += w[8]; | |
102 | sum += w[9]; | |
103 | sum += w[10]; | |
104 | sum += w[11]; | |
105 | sum += w[12]; | |
106 | sum += w[13]; | |
107 | sum += w[14]; | |
108 | sum += w[15]; | |
109 | w += 16; | |
110 | } | |
111 | mlen += 32; | |
112 | while ((mlen -= 8) >= 0) { | |
113 | sum += w[0]; | |
114 | sum += w[1]; | |
115 | sum += w[2]; | |
116 | sum += w[3]; | |
117 | w += 4; | |
118 | } | |
119 | mlen += 8; | |
120 | if (mlen == 0 && byte_swapped == 0) | |
121 | goto cont; | |
122 | REDUCE; | |
123 | while ((mlen -= 2) >= 0) { | |
124 | sum += *w++; | |
125 | } | |
126 | ||
127 | if (byte_swapped) { | |
128 | REDUCE; | |
129 | sum <<= 8; | |
130 | if (mlen == -1) { | |
131 | s_util.c[1] = *(uint8_t *)w; | |
132 | sum += s_util.s; | |
133 | mlen = 0; | |
134 | } else | |
135 | ||
136 | mlen = -1; | |
137 | } else if (mlen == -1) | |
138 | s_util.c[0] = *(uint8_t *)w; | |
139 | ||
140 | cont: | |
141 | if (len) { | |
142 | DEBUG_ERROR("cksum: out of data"); | |
143 | DEBUG_ERROR(" len = %d", len); | |
144 | } | |
145 | if (mlen == -1) { | |
146 | /* The last mbuf has odd # of bytes. Follow the | |
147 | standard (the odd byte may be shifted left by 8 bits | |
148 | or not as determined by endian-ness of the machine) */ | |
149 | s_util.c[1] = 0; | |
150 | sum += s_util.s; | |
151 | } | |
152 | REDUCE; | |
153 | return (~sum & 0xffff); | |
154 | } | |
155 | ||
156 | int ip6_cksum(struct mbuf *m) | |
157 | { | |
158 | /* TODO: Optimize this by being able to pass the ip6_pseudohdr to cksum | |
159 | * separately from the mbuf */ | |
160 | struct ip6 save_ip, *ip = mtod(m, struct ip6 *); | |
161 | struct ip6_pseudohdr *ih = mtod(m, struct ip6_pseudohdr *); | |
162 | int sum; | |
163 | ||
164 | save_ip = *ip; | |
165 | ||
166 | ih->ih_src = save_ip.ip_src; | |
167 | ih->ih_dst = save_ip.ip_dst; | |
168 | ih->ih_pl = htonl((uint32_t)ntohs(save_ip.ip_pl)); | |
169 | ih->ih_zero_hi = 0; | |
170 | ih->ih_zero_lo = 0; | |
171 | ih->ih_nh = save_ip.ip_nh; | |
172 | ||
173 | sum = cksum(m, ((int)sizeof(struct ip6_pseudohdr)) + ntohl(ih->ih_pl)); | |
174 | ||
175 | *ip = save_ip; | |
176 | ||
177 | return sum; | |
178 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef DEBUG_H_ | |
6 | #define DEBUG_H_ | |
7 | ||
8 | #define DBG_CALL (1 << 0) | |
9 | #define DBG_MISC (1 << 1) | |
10 | #define DBG_ERROR (1 << 2) | |
11 | #define DBG_TFTP (1 << 3) | |
12 | ||
13 | extern int slirp_debug; | |
14 | ||
15 | #define DEBUG_CALL(fmt, ...) \ | |
16 | do { \ | |
17 | if (G_UNLIKELY(slirp_debug & DBG_CALL)) { \ | |
18 | g_debug(fmt "...", ##__VA_ARGS__); \ | |
19 | } \ | |
20 | } while (0) | |
21 | ||
22 | #define DEBUG_ARG(fmt, ...) \ | |
23 | do { \ | |
24 | if (G_UNLIKELY(slirp_debug & DBG_CALL)) { \ | |
25 | g_debug(" " fmt, ##__VA_ARGS__); \ | |
26 | } \ | |
27 | } while (0) | |
28 | ||
29 | #define DEBUG_MISC(fmt, ...) \ | |
30 | do { \ | |
31 | if (G_UNLIKELY(slirp_debug & DBG_MISC)) { \ | |
32 | g_debug(fmt, ##__VA_ARGS__); \ | |
33 | } \ | |
34 | } while (0) | |
35 | ||
36 | #define DEBUG_ERROR(fmt, ...) \ | |
37 | do { \ | |
38 | if (G_UNLIKELY(slirp_debug & DBG_ERROR)) { \ | |
39 | g_debug(fmt, ##__VA_ARGS__); \ | |
40 | } \ | |
41 | } while (0) | |
42 | ||
43 | #define DEBUG_TFTP(fmt, ...) \ | |
44 | do { \ | |
45 | if (G_UNLIKELY(slirp_debug & DBG_TFTP)) { \ | |
46 | g_debug(fmt, ##__VA_ARGS__); \ | |
47 | } \ | |
48 | } while (0) | |
49 | ||
50 | #endif /* DEBUG_H_ */ |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * SLIRP stateless DHCPv6 | |
3 | * | |
4 | * We only support stateless DHCPv6, e.g. for network booting. | |
5 | * See RFC 3315, RFC 3736, RFC 3646 and RFC 5970 for details. | |
6 | * | |
7 | * Copyright 2016 Thomas Huth, Red Hat Inc. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * | |
13 | * 1. Redistributions of source code must retain the above | |
14 | * copyright notice, this list of conditions and the following | |
15 | * disclaimer. | |
16 | * | |
17 | * 2. Redistributions in binary form must reproduce the above | |
18 | * copyright notice, this list of conditions and the following | |
19 | * disclaimer in the documentation and/or other materials provided | |
20 | * with the distribution. | |
21 | * | |
22 | * 3. Neither the name of the copyright holder nor the names of its | |
23 | * contributors may be used to endorse or promote products derived | |
24 | * from this software without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
27 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
28 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
29 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
30 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
31 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
32 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
33 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
35 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
37 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
38 | */ | |
39 | ||
40 | #include "slirp.h" | |
41 | #include "dhcpv6.h" | |
42 | ||
43 | /* DHCPv6 message types */ | |
44 | #define MSGTYPE_REPLY 7 | |
45 | #define MSGTYPE_INFO_REQUEST 11 | |
46 | ||
47 | /* DHCPv6 option types */ | |
48 | #define OPTION_CLIENTID 1 | |
49 | #define OPTION_IAADDR 5 | |
50 | #define OPTION_ORO 6 | |
51 | #define OPTION_DNS_SERVERS 23 | |
52 | #define OPTION_BOOTFILE_URL 59 | |
53 | ||
54 | struct requested_infos { | |
55 | uint8_t *client_id; | |
56 | int client_id_len; | |
57 | bool want_dns; | |
58 | bool want_boot_url; | |
59 | }; | |
60 | ||
61 | /** | |
62 | * Analyze the info request message sent by the client to see what data it | |
63 | * provided and what it wants to have. The information is gathered in the | |
64 | * "requested_infos" struct. Note that client_id (if provided) points into | |
65 | * the odata region, thus the caller must keep odata valid as long as it | |
66 | * needs to access the requested_infos struct. | |
67 | */ | |
68 | static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen, | |
69 | struct requested_infos *ri) | |
70 | { | |
71 | int i, req_opt; | |
72 | ||
73 | while (olen > 4) { | |
74 | /* Parse one option */ | |
75 | int option = odata[0] << 8 | odata[1]; | |
76 | int len = odata[2] << 8 | odata[3]; | |
77 | ||
78 | if (len + 4 > olen) { | |
79 | slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", | |
80 | slirp->opaque); | |
81 | return -E2BIG; | |
82 | } | |
83 | ||
84 | switch (option) { | |
85 | case OPTION_IAADDR: | |
86 | /* According to RFC3315, we must discard requests with IA option */ | |
87 | return -EINVAL; | |
88 | case OPTION_CLIENTID: | |
89 | if (len > 256) { | |
90 | /* Avoid very long IDs which could cause problems later */ | |
91 | return -E2BIG; | |
92 | } | |
93 | ri->client_id = odata + 4; | |
94 | ri->client_id_len = len; | |
95 | break; | |
96 | case OPTION_ORO: /* Option request option */ | |
97 | if (len & 1) { | |
98 | return -EINVAL; | |
99 | } | |
100 | /* Check which options the client wants to have */ | |
101 | for (i = 0; i < len; i += 2) { | |
102 | req_opt = odata[4 + i] << 8 | odata[4 + i + 1]; | |
103 | switch (req_opt) { | |
104 | case OPTION_DNS_SERVERS: | |
105 | ri->want_dns = true; | |
106 | break; | |
107 | case OPTION_BOOTFILE_URL: | |
108 | ri->want_boot_url = true; | |
109 | break; | |
110 | default: | |
111 | DEBUG_MISC("dhcpv6: Unsupported option request %d", | |
112 | req_opt); | |
113 | } | |
114 | } | |
115 | break; | |
116 | default: | |
117 | DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d", option, | |
118 | len); | |
119 | } | |
120 | ||
121 | odata += len + 4; | |
122 | olen -= len + 4; | |
123 | } | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | ||
129 | /** | |
130 | * Handle information request messages | |
131 | */ | |
132 | static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas, | |
133 | uint32_t xid, uint8_t *odata, int olen) | |
134 | { | |
135 | struct requested_infos ri = { NULL }; | |
136 | struct sockaddr_in6 sa6, da6; | |
137 | struct mbuf *m; | |
138 | uint8_t *resp; | |
139 | ||
140 | if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) { | |
141 | return; | |
142 | } | |
143 | ||
144 | m = m_get(slirp); | |
145 | if (!m) { | |
146 | return; | |
147 | } | |
148 | memset(m->m_data, 0, m->m_size); | |
149 | m->m_data += IF_MAXLINKHDR; | |
150 | resp = (uint8_t *)m->m_data + sizeof(struct ip6) + sizeof(struct udphdr); | |
151 | ||
152 | /* Fill in response */ | |
153 | *resp++ = MSGTYPE_REPLY; | |
154 | *resp++ = (uint8_t)(xid >> 16); | |
155 | *resp++ = (uint8_t)(xid >> 8); | |
156 | *resp++ = (uint8_t)xid; | |
157 | ||
158 | if (ri.client_id) { | |
159 | *resp++ = OPTION_CLIENTID >> 8; /* option-code high byte */ | |
160 | *resp++ = OPTION_CLIENTID; /* option-code low byte */ | |
161 | *resp++ = ri.client_id_len >> 8; /* option-len high byte */ | |
162 | *resp++ = ri.client_id_len; /* option-len low byte */ | |
163 | memcpy(resp, ri.client_id, ri.client_id_len); | |
164 | resp += ri.client_id_len; | |
165 | } | |
166 | if (ri.want_dns) { | |
167 | *resp++ = OPTION_DNS_SERVERS >> 8; /* option-code high byte */ | |
168 | *resp++ = OPTION_DNS_SERVERS; /* option-code low byte */ | |
169 | *resp++ = 0; /* option-len high byte */ | |
170 | *resp++ = 16; /* option-len low byte */ | |
171 | memcpy(resp, &slirp->vnameserver_addr6, 16); | |
172 | resp += 16; | |
173 | } | |
174 | if (ri.want_boot_url) { | |
175 | uint8_t *sa = slirp->vhost_addr6.s6_addr; | |
176 | int slen, smaxlen; | |
177 | ||
178 | *resp++ = OPTION_BOOTFILE_URL >> 8; /* option-code high byte */ | |
179 | *resp++ = OPTION_BOOTFILE_URL; /* option-code low byte */ | |
180 | smaxlen = (uint8_t *)m->m_data + slirp->if_mtu - (resp + 2); | |
181 | slen = snprintf((char *)resp + 2, smaxlen, | |
182 | "tftp://[%02x%02x:%02x%02x:%02x%02x:%02x%02x:" | |
183 | "%02x%02x:%02x%02x:%02x%02x:%02x%02x]/%s", | |
184 | sa[0], sa[1], sa[2], sa[3], sa[4], sa[5], sa[6], sa[7], | |
185 | sa[8], sa[9], sa[10], sa[11], sa[12], sa[13], sa[14], | |
186 | sa[15], slirp->bootp_filename); | |
187 | slen = MIN(slen, smaxlen); | |
188 | *resp++ = slen >> 8; /* option-len high byte */ | |
189 | *resp++ = slen; /* option-len low byte */ | |
190 | resp += slen; | |
191 | } | |
192 | ||
193 | sa6.sin6_addr = slirp->vhost_addr6; | |
194 | sa6.sin6_port = DHCPV6_SERVER_PORT; | |
195 | da6.sin6_addr = srcsas->sin6_addr; | |
196 | da6.sin6_port = srcsas->sin6_port; | |
197 | m->m_data += sizeof(struct ip6) + sizeof(struct udphdr); | |
198 | m->m_len = resp - (uint8_t *)m->m_data; | |
199 | udp6_output(NULL, m, &sa6, &da6); | |
200 | } | |
201 | ||
202 | /** | |
203 | * Handle DHCPv6 messages sent by the client | |
204 | */ | |
205 | void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m) | |
206 | { | |
207 | uint8_t *data = (uint8_t *)m->m_data + sizeof(struct udphdr); | |
208 | int data_len = m->m_len - sizeof(struct udphdr); | |
209 | uint32_t xid; | |
210 | ||
211 | if (data_len < 4) { | |
212 | return; | |
213 | } | |
214 | ||
215 | xid = ntohl(*(uint32_t *)data) & 0xffffff; | |
216 | ||
217 | switch (data[0]) { | |
218 | case MSGTYPE_INFO_REQUEST: | |
219 | dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4); | |
220 | break; | |
221 | default: | |
222 | DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]); | |
223 | } | |
224 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Definitions and prototypes for SLIRP stateless DHCPv6 | |
3 | * | |
4 | * Copyright 2016 Thomas Huth, Red Hat Inc. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the above | |
11 | * copyright notice, this list of conditions and the following | |
12 | * disclaimer. | |
13 | * | |
14 | * 2. Redistributions in binary form must reproduce the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer in the documentation and/or other materials provided | |
17 | * with the distribution. | |
18 | * | |
19 | * 3. Neither the name of the copyright holder nor the names of its | |
20 | * contributors may be used to endorse or promote products derived | |
21 | * from this software without specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
34 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
35 | */ | |
36 | #ifndef SLIRP_DHCPV6_H | |
37 | #define SLIRP_DHCPV6_H | |
38 | ||
39 | #define DHCPV6_SERVER_PORT 547 | |
40 | ||
41 | #define ALLDHCP_MULTICAST \ | |
42 | { \ | |
43 | .s6_addr = { \ | |
44 | 0xff, \ | |
45 | 0x02, \ | |
46 | 0x00, \ | |
47 | 0x00, \ | |
48 | 0x00, \ | |
49 | 0x00, \ | |
50 | 0x00, \ | |
51 | 0x00, \ | |
52 | 0x00, \ | |
53 | 0x00, \ | |
54 | 0x00, \ | |
55 | 0x00, \ | |
56 | 0x00, \ | |
57 | 0x01, \ | |
58 | 0x00, \ | |
59 | 0x02 \ | |
60 | } \ | |
61 | } | |
62 | ||
63 | #define in6_dhcp_multicast(a) in6_equal(a, &(struct in6_addr)ALLDHCP_MULTICAST) | |
64 | ||
65 | void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m); | |
66 | ||
67 | #endif |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * Domain search option for DHCP (RFC 3397) | |
3 | * | |
4 | * Copyright (c) 2012 Klaus Stengel | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "slirp.h" | |
26 | ||
27 | static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119; | |
28 | static const uint8_t MAX_OPT_LEN = 255; | |
29 | static const uint8_t OPT_HEADER_LEN = 2; | |
30 | static const uint8_t REFERENCE_LEN = 2; | |
31 | ||
32 | struct compact_domain; | |
33 | ||
34 | typedef struct compact_domain { | |
35 | struct compact_domain *self; | |
36 | struct compact_domain *refdom; | |
37 | uint8_t *labels; | |
38 | size_t len; | |
39 | size_t common_octets; | |
40 | } CompactDomain; | |
41 | ||
42 | static size_t domain_suffix_diffoff(const CompactDomain *a, | |
43 | const CompactDomain *b) | |
44 | { | |
45 | size_t la = a->len, lb = b->len; | |
46 | uint8_t *da = a->labels + la, *db = b->labels + lb; | |
47 | size_t i, lm = (la < lb) ? la : lb; | |
48 | ||
49 | for (i = 0; i < lm; i++) { | |
50 | da--; | |
51 | db--; | |
52 | if (*da != *db) { | |
53 | break; | |
54 | } | |
55 | } | |
56 | return i; | |
57 | } | |
58 | ||
59 | static int domain_suffix_ord(const void *cva, const void *cvb) | |
60 | { | |
61 | const CompactDomain *a = cva, *b = cvb; | |
62 | size_t la = a->len, lb = b->len; | |
63 | size_t doff = domain_suffix_diffoff(a, b); | |
64 | uint8_t ca = a->labels[la - doff]; | |
65 | uint8_t cb = b->labels[lb - doff]; | |
66 | ||
67 | if (ca < cb) { | |
68 | return -1; | |
69 | } | |
70 | if (ca > cb) { | |
71 | return 1; | |
72 | } | |
73 | if (la < lb) { | |
74 | return -1; | |
75 | } | |
76 | if (la > lb) { | |
77 | return 1; | |
78 | } | |
79 | return 0; | |
80 | } | |
81 | ||
82 | static size_t domain_common_label(CompactDomain *a, CompactDomain *b) | |
83 | { | |
84 | size_t res, doff = domain_suffix_diffoff(a, b); | |
85 | uint8_t *first_eq_pos = a->labels + (a->len - doff); | |
86 | uint8_t *label = a->labels; | |
87 | ||
88 | while (*label && label < first_eq_pos) { | |
89 | label += *label + 1; | |
90 | } | |
91 | res = a->len - (label - a->labels); | |
92 | /* only report if it can help to reduce the packet size */ | |
93 | return (res > REFERENCE_LEN) ? res : 0; | |
94 | } | |
95 | ||
96 | static void domain_fixup_order(CompactDomain *cd, size_t n) | |
97 | { | |
98 | size_t i; | |
99 | ||
100 | for (i = 0; i < n; i++) { | |
101 | CompactDomain *cur = cd + i, *next = cd[i].self; | |
102 | ||
103 | while (!cur->common_octets) { | |
104 | CompactDomain *tmp = next->self; /* backup target value */ | |
105 | ||
106 | next->self = cur; | |
107 | cur->common_octets++; | |
108 | ||
109 | cur = next; | |
110 | next = tmp; | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
115 | static void domain_mklabels(CompactDomain *cd, const char *input) | |
116 | { | |
117 | uint8_t *len_marker = cd->labels; | |
118 | uint8_t *output = len_marker; /* pre-incremented */ | |
119 | const char *in = input; | |
120 | char cur_chr; | |
121 | size_t len = 0; | |
122 | ||
123 | if (cd->len == 0) { | |
124 | goto fail; | |
125 | } | |
126 | cd->len++; | |
127 | ||
128 | do { | |
129 | cur_chr = *in++; | |
130 | if (cur_chr == '.' || cur_chr == '\0') { | |
131 | len = output - len_marker; | |
132 | if ((len == 0 && cur_chr == '.') || len >= 64) { | |
133 | goto fail; | |
134 | } | |
135 | *len_marker = len; | |
136 | ||
137 | output++; | |
138 | len_marker = output; | |
139 | } else { | |
140 | output++; | |
141 | *output = cur_chr; | |
142 | } | |
143 | } while (cur_chr != '\0'); | |
144 | ||
145 | /* ensure proper zero-termination */ | |
146 | if (len != 0) { | |
147 | *len_marker = 0; | |
148 | cd->len++; | |
149 | } | |
150 | return; | |
151 | ||
152 | fail: | |
153 | g_warning("failed to parse domain name '%s'\n", input); | |
154 | cd->len = 0; | |
155 | } | |
156 | ||
157 | static void domain_mkxrefs(CompactDomain *doms, CompactDomain *last, | |
158 | size_t depth) | |
159 | { | |
160 | CompactDomain *i = doms, *target = doms; | |
161 | ||
162 | do { | |
163 | if (i->labels < target->labels) { | |
164 | target = i; | |
165 | } | |
166 | } while (i++ != last); | |
167 | ||
168 | for (i = doms; i != last; i++) { | |
169 | CompactDomain *group_last; | |
170 | size_t next_depth; | |
171 | ||
172 | if (i->common_octets == depth) { | |
173 | continue; | |
174 | } | |
175 | ||
176 | next_depth = -1; | |
177 | for (group_last = i; group_last != last; group_last++) { | |
178 | size_t co = group_last->common_octets; | |
179 | if (co <= depth) { | |
180 | break; | |
181 | } | |
182 | if (co < next_depth) { | |
183 | next_depth = co; | |
184 | } | |
185 | } | |
186 | domain_mkxrefs(i, group_last, next_depth); | |
187 | ||
188 | i = group_last; | |
189 | if (i == last) { | |
190 | break; | |
191 | } | |
192 | } | |
193 | ||
194 | if (depth == 0) { | |
195 | return; | |
196 | } | |
197 | ||
198 | i = doms; | |
199 | do { | |
200 | if (i != target && i->refdom == NULL) { | |
201 | i->refdom = target; | |
202 | i->common_octets = depth; | |
203 | } | |
204 | } while (i++ != last); | |
205 | } | |
206 | ||
207 | static size_t domain_compactify(CompactDomain *domains, size_t n) | |
208 | { | |
209 | uint8_t *start = domains->self->labels, *outptr = start; | |
210 | size_t i; | |
211 | ||
212 | for (i = 0; i < n; i++) { | |
213 | CompactDomain *cd = domains[i].self; | |
214 | CompactDomain *rd = cd->refdom; | |
215 | ||
216 | if (rd != NULL) { | |
217 | size_t moff = (rd->labels - start) + (rd->len - cd->common_octets); | |
218 | if (moff < 0x3FFFu) { | |
219 | cd->len -= cd->common_octets - 2; | |
220 | cd->labels[cd->len - 1] = moff & 0xFFu; | |
221 | cd->labels[cd->len - 2] = 0xC0u | (moff >> 8); | |
222 | } | |
223 | } | |
224 | ||
225 | if (cd->labels != outptr) { | |
226 | memmove(outptr, cd->labels, cd->len); | |
227 | cd->labels = outptr; | |
228 | } | |
229 | outptr += cd->len; | |
230 | } | |
231 | return outptr - start; | |
232 | } | |
233 | ||
234 | int translate_dnssearch(Slirp *s, const char **names) | |
235 | { | |
236 | size_t blocks, bsrc_start, bsrc_end, bdst_start; | |
237 | size_t i, num_domains, memreq = 0; | |
238 | uint8_t *result = NULL, *outptr; | |
239 | CompactDomain *domains = NULL; | |
240 | const char **nameptr = names; | |
241 | ||
242 | while (*nameptr != NULL) { | |
243 | nameptr++; | |
244 | } | |
245 | ||
246 | num_domains = nameptr - names; | |
247 | if (num_domains == 0) { | |
248 | return -2; | |
249 | } | |
250 | ||
251 | domains = g_malloc(num_domains * sizeof(*domains)); | |
252 | ||
253 | for (i = 0; i < num_domains; i++) { | |
254 | size_t nlen = strlen(names[i]); | |
255 | memreq += nlen + 2; /* 1 zero octet + 1 label length octet */ | |
256 | domains[i].self = domains + i; | |
257 | domains[i].len = nlen; | |
258 | domains[i].common_octets = 0; | |
259 | domains[i].refdom = NULL; | |
260 | } | |
261 | ||
262 | /* reserve extra 2 header bytes for each 255 bytes of output */ | |
263 | memreq += DIV_ROUND_UP(memreq, MAX_OPT_LEN) * OPT_HEADER_LEN; | |
264 | result = g_malloc(memreq * sizeof(*result)); | |
265 | ||
266 | outptr = result; | |
267 | for (i = 0; i < num_domains; i++) { | |
268 | domains[i].labels = outptr; | |
269 | domain_mklabels(domains + i, names[i]); | |
270 | outptr += domains[i].len; | |
271 | } | |
272 | ||
273 | if (outptr == result) { | |
274 | g_free(domains); | |
275 | g_free(result); | |
276 | return -1; | |
277 | } | |
278 | ||
279 | qsort(domains, num_domains, sizeof(*domains), domain_suffix_ord); | |
280 | domain_fixup_order(domains, num_domains); | |
281 | ||
282 | for (i = 1; i < num_domains; i++) { | |
283 | size_t cl = domain_common_label(domains + i - 1, domains + i); | |
284 | domains[i - 1].common_octets = cl; | |
285 | } | |
286 | ||
287 | domain_mkxrefs(domains, domains + num_domains - 1, 0); | |
288 | memreq = domain_compactify(domains, num_domains); | |
289 | ||
290 | blocks = DIV_ROUND_UP(memreq, MAX_OPT_LEN); | |
291 | bsrc_end = memreq; | |
292 | bsrc_start = (blocks - 1) * MAX_OPT_LEN; | |
293 | bdst_start = bsrc_start + blocks * OPT_HEADER_LEN; | |
294 | memreq += blocks * OPT_HEADER_LEN; | |
295 | ||
296 | while (blocks--) { | |
297 | size_t len = bsrc_end - bsrc_start; | |
298 | memmove(result + bdst_start, result + bsrc_start, len); | |
299 | result[bdst_start - 2] = RFC3397_OPT_DOMAIN_SEARCH; | |
300 | result[bdst_start - 1] = len; | |
301 | bsrc_end = bsrc_start; | |
302 | bsrc_start -= MAX_OPT_LEN; | |
303 | bdst_start -= MAX_OPT_LEN + OPT_HEADER_LEN; | |
304 | } | |
305 | ||
306 | g_free(domains); | |
307 | s->vdnssearch = result; | |
308 | s->vdnssearch_len = memreq; | |
309 | return 0; | |
310 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #include "slirp.h" | |
6 | ||
7 | static void ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead) | |
8 | { | |
9 | ifm->ifs_next = ifmhead->ifs_next; | |
10 | ifmhead->ifs_next = ifm; | |
11 | ifm->ifs_prev = ifmhead; | |
12 | ifm->ifs_next->ifs_prev = ifm; | |
13 | } | |
14 | ||
15 | static void ifs_remque(struct mbuf *ifm) | |
16 | { | |
17 | ifm->ifs_prev->ifs_next = ifm->ifs_next; | |
18 | ifm->ifs_next->ifs_prev = ifm->ifs_prev; | |
19 | } | |
20 | ||
21 | void if_init(Slirp *slirp) | |
22 | { | |
23 | slirp->if_fastq.qh_link = slirp->if_fastq.qh_rlink = &slirp->if_fastq; | |
24 | slirp->if_batchq.qh_link = slirp->if_batchq.qh_rlink = &slirp->if_batchq; | |
25 | } | |
26 | ||
27 | /* | |
28 | * if_output: Queue packet into an output queue. | |
29 | * There are 2 output queue's, if_fastq and if_batchq. | |
30 | * Each output queue is a doubly linked list of double linked lists | |
31 | * of mbufs, each list belonging to one "session" (socket). This | |
32 | * way, we can output packets fairly by sending one packet from each | |
33 | * session, instead of all the packets from one session, then all packets | |
34 | * from the next session, etc. Packets on the if_fastq get absolute | |
35 | * priority, but if one session hogs the link, it gets "downgraded" | |
36 | * to the batchq until it runs out of packets, then it'll return | |
37 | * to the fastq (eg. if the user does an ls -alR in a telnet session, | |
38 | * it'll temporarily get downgraded to the batchq) | |
39 | */ | |
40 | void if_output(struct socket *so, struct mbuf *ifm) | |
41 | { | |
42 | Slirp *slirp = ifm->slirp; | |
43 | struct mbuf *ifq; | |
44 | int on_fastq = 1; | |
45 | ||
46 | DEBUG_CALL("if_output"); | |
47 | DEBUG_ARG("so = %p", so); | |
48 | DEBUG_ARG("ifm = %p", ifm); | |
49 | ||
50 | /* | |
51 | * First remove the mbuf from m_usedlist, | |
52 | * since we're gonna use m_next and m_prev ourselves | |
53 | * XXX Shouldn't need this, gotta change dtom() etc. | |
54 | */ | |
55 | if (ifm->m_flags & M_USEDLIST) { | |
56 | remque(ifm); | |
57 | ifm->m_flags &= ~M_USEDLIST; | |
58 | } | |
59 | ||
60 | /* | |
61 | * See if there's already a batchq list for this session. | |
62 | * This can include an interactive session, which should go on fastq, | |
63 | * but gets too greedy... hence it'll be downgraded from fastq to batchq. | |
64 | * We mustn't put this packet back on the fastq (or we'll send it out of | |
65 | * order) | |
66 | * XXX add cache here? | |
67 | */ | |
68 | if (so) { | |
69 | for (ifq = (struct mbuf *)slirp->if_batchq.qh_rlink; | |
70 | (struct quehead *)ifq != &slirp->if_batchq; ifq = ifq->ifq_prev) { | |
71 | if (so == ifq->ifq_so) { | |
72 | /* A match! */ | |
73 | ifm->ifq_so = so; | |
74 | ifs_insque(ifm, ifq->ifs_prev); | |
75 | goto diddit; | |
76 | } | |
77 | } | |
78 | } | |
79 | ||
80 | /* No match, check which queue to put it on */ | |
81 | if (so && (so->so_iptos & IPTOS_LOWDELAY)) { | |
82 | ifq = (struct mbuf *)slirp->if_fastq.qh_rlink; | |
83 | on_fastq = 1; | |
84 | /* | |
85 | * Check if this packet is a part of the last | |
86 | * packet's session | |
87 | */ | |
88 | if (ifq->ifq_so == so) { | |
89 | ifm->ifq_so = so; | |
90 | ifs_insque(ifm, ifq->ifs_prev); | |
91 | goto diddit; | |
92 | } | |
93 | } else { | |
94 | ifq = (struct mbuf *)slirp->if_batchq.qh_rlink; | |
95 | } | |
96 | ||
97 | /* Create a new doubly linked list for this session */ | |
98 | ifm->ifq_so = so; | |
99 | ifs_init(ifm); | |
100 | insque(ifm, ifq); | |
101 | ||
102 | diddit: | |
103 | if (so) { | |
104 | /* Update *_queued */ | |
105 | so->so_queued++; | |
106 | so->so_nqueued++; | |
107 | /* | |
108 | * Check if the interactive session should be downgraded to | |
109 | * the batchq. A session is downgraded if it has queued 6 | |
110 | * packets without pausing, and at least 3 of those packets | |
111 | * have been sent over the link | |
112 | * (XXX These are arbitrary numbers, probably not optimal..) | |
113 | */ | |
114 | if (on_fastq && | |
115 | ((so->so_nqueued >= 6) && (so->so_nqueued - so->so_queued) >= 3)) { | |
116 | /* Remove from current queue... */ | |
117 | remque(ifm->ifs_next); | |
118 | ||
119 | /* ...And insert in the new. That'll teach ya! */ | |
120 | insque(ifm->ifs_next, &slirp->if_batchq); | |
121 | } | |
122 | } | |
123 | ||
124 | /* | |
125 | * This prevents us from malloc()ing too many mbufs | |
126 | */ | |
127 | if_start(ifm->slirp); | |
128 | } | |
129 | ||
130 | /* | |
131 | * Send one packet from each session. | |
132 | * If there are packets on the fastq, they are sent FIFO, before | |
133 | * everything else. Then we choose the first packet from each | |
134 | * batchq session (socket) and send it. | |
135 | * For example, if there are 3 ftp sessions fighting for bandwidth, | |
136 | * one packet will be sent from the first session, then one packet | |
137 | * from the second session, then one packet from the third. | |
138 | */ | |
139 | void if_start(Slirp *slirp) | |
140 | { | |
141 | uint64_t now = slirp->cb->clock_get_ns(slirp->opaque); | |
142 | bool from_batchq = false; | |
143 | struct mbuf *ifm, *ifm_next, *ifqt; | |
144 | ||
145 | DEBUG_CALL("if_start"); | |
146 | ||
147 | if (slirp->if_start_busy) { | |
148 | return; | |
149 | } | |
150 | slirp->if_start_busy = true; | |
151 | ||
152 | struct mbuf *batch_head = NULL; | |
153 | if (slirp->if_batchq.qh_link != &slirp->if_batchq) { | |
154 | batch_head = (struct mbuf *)slirp->if_batchq.qh_link; | |
155 | } | |
156 | ||
157 | if (slirp->if_fastq.qh_link != &slirp->if_fastq) { | |
158 | ifm_next = (struct mbuf *)slirp->if_fastq.qh_link; | |
159 | } else if (batch_head) { | |
160 | /* Nothing on fastq, pick up from batchq */ | |
161 | ifm_next = batch_head; | |
162 | from_batchq = true; | |
163 | } else { | |
164 | ifm_next = NULL; | |
165 | } | |
166 | ||
167 | while (ifm_next) { | |
168 | ifm = ifm_next; | |
169 | ||
170 | ifm_next = ifm->ifq_next; | |
171 | if ((struct quehead *)ifm_next == &slirp->if_fastq) { | |
172 | /* No more packets in fastq, switch to batchq */ | |
173 | ifm_next = batch_head; | |
174 | from_batchq = true; | |
175 | } | |
176 | if ((struct quehead *)ifm_next == &slirp->if_batchq) { | |
177 | /* end of batchq */ | |
178 | ifm_next = NULL; | |
179 | } | |
180 | ||
181 | /* Try to send packet unless it already expired */ | |
182 | if (ifm->expiration_date >= now && !if_encap(slirp, ifm)) { | |
183 | /* Packet is delayed due to pending ARP or NDP resolution */ | |
184 | continue; | |
185 | } | |
186 | ||
187 | /* Remove it from the queue */ | |
188 | ifqt = ifm->ifq_prev; | |
189 | remque(ifm); | |
190 | ||
191 | /* If there are more packets for this session, re-queue them */ | |
192 | if (ifm->ifs_next != ifm) { | |
193 | struct mbuf *next = ifm->ifs_next; | |
194 | ||
195 | insque(next, ifqt); | |
196 | ifs_remque(ifm); | |
197 | if (!from_batchq) { | |
198 | ifm_next = next; | |
199 | } | |
200 | } | |
201 | ||
202 | /* Update so_queued */ | |
203 | if (ifm->ifq_so && --ifm->ifq_so->so_queued == 0) { | |
204 | /* If there's no more queued, reset nqueued */ | |
205 | ifm->ifq_so->so_nqueued = 0; | |
206 | } | |
207 | ||
208 | m_free(ifm); | |
209 | } | |
210 | ||
211 | slirp->if_start_busy = false; | |
212 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef IF_H | |
6 | #define IF_H | |
7 | ||
8 | #define IF_COMPRESS 0x01 /* We want compression */ | |
9 | #define IF_NOCOMPRESS 0x02 /* Do not do compression */ | |
10 | #define IF_AUTOCOMP 0x04 /* Autodetect (default) */ | |
11 | #define IF_NOCIDCOMP 0x08 /* CID compression */ | |
12 | ||
13 | #define IF_MTU_DEFAULT 1500 | |
14 | #define IF_MTU_MIN 68 | |
15 | #define IF_MTU_MAX 65521 | |
16 | #define IF_MRU_DEFAULT 1500 | |
17 | #define IF_MRU_MIN 68 | |
18 | #define IF_MRU_MAX 65521 | |
19 | #define IF_COMP IF_AUTOCOMP /* Flags for compression */ | |
20 | ||
21 | /* 2 for alignment, 14 for ethernet */ | |
22 | #define IF_MAXLINKHDR (2 + ETH_HLEN) | |
23 | ||
24 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)ip.h 8.1 (Berkeley) 6/10/93 | |
30 | * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef IP_H | |
34 | #define IP_H | |
35 | ||
36 | #include <glib.h> | |
37 | ||
38 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
39 | #undef NTOHL | |
40 | #undef NTOHS | |
41 | #undef HTONL | |
42 | #undef HTONS | |
43 | #define NTOHL(d) | |
44 | #define NTOHS(d) | |
45 | #define HTONL(d) | |
46 | #define HTONS(d) | |
47 | #else | |
48 | #ifndef NTOHL | |
49 | #define NTOHL(d) ((d) = ntohl((d))) | |
50 | #endif | |
51 | #ifndef NTOHS | |
52 | #define NTOHS(d) ((d) = ntohs((uint16_t)(d))) | |
53 | #endif | |
54 | #ifndef HTONL | |
55 | #define HTONL(d) ((d) = htonl((d))) | |
56 | #endif | |
57 | #ifndef HTONS | |
58 | #define HTONS(d) ((d) = htons((uint16_t)(d))) | |
59 | #endif | |
60 | #endif | |
61 | ||
62 | typedef uint32_t n_long; /* long as received from the net */ | |
63 | ||
64 | /* | |
65 | * Definitions for internet protocol version 4. | |
66 | * Per RFC 791, September 1981. | |
67 | */ | |
68 | #define IPVERSION 4 | |
69 | ||
70 | /* | |
71 | * Structure of an internet header, naked of options. | |
72 | */ | |
73 | struct ip { | |
74 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
75 | uint8_t ip_v : 4, /* version */ | |
76 | ip_hl : 4; /* header length */ | |
77 | #else | |
78 | uint8_t ip_hl : 4, /* header length */ | |
79 | ip_v : 4; /* version */ | |
80 | #endif | |
81 | uint8_t ip_tos; /* type of service */ | |
82 | uint16_t ip_len; /* total length */ | |
83 | uint16_t ip_id; /* identification */ | |
84 | uint16_t ip_off; /* fragment offset field */ | |
85 | #define IP_DF 0x4000 /* don't fragment flag */ | |
86 | #define IP_MF 0x2000 /* more fragments flag */ | |
87 | #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ | |
88 | uint8_t ip_ttl; /* time to live */ | |
89 | uint8_t ip_p; /* protocol */ | |
90 | uint16_t ip_sum; /* checksum */ | |
91 | struct in_addr ip_src, ip_dst; /* source and dest address */ | |
92 | } SLIRP_PACKED; | |
93 | ||
94 | #define IP_MAXPACKET 65535 /* maximum packet size */ | |
95 | ||
96 | /* | |
97 | * Definitions for IP type of service (ip_tos) | |
98 | */ | |
99 | #define IPTOS_LOWDELAY 0x10 | |
100 | #define IPTOS_THROUGHPUT 0x08 | |
101 | #define IPTOS_RELIABILITY 0x04 | |
102 | ||
103 | /* | |
104 | * Definitions for options. | |
105 | */ | |
106 | #define IPOPT_COPIED(o) ((o)&0x80) | |
107 | #define IPOPT_CLASS(o) ((o)&0x60) | |
108 | #define IPOPT_NUMBER(o) ((o)&0x1f) | |
109 | ||
110 | #define IPOPT_CONTROL 0x00 | |
111 | #define IPOPT_RESERVED1 0x20 | |
112 | #define IPOPT_DEBMEAS 0x40 | |
113 | #define IPOPT_RESERVED2 0x60 | |
114 | ||
115 | #define IPOPT_EOL 0 /* end of option list */ | |
116 | #define IPOPT_NOP 1 /* no operation */ | |
117 | ||
118 | #define IPOPT_RR 7 /* record packet route */ | |
119 | #define IPOPT_TS 68 /* timestamp */ | |
120 | #define IPOPT_SECURITY 130 /* provide s,c,h,tcc */ | |
121 | #define IPOPT_LSRR 131 /* loose source route */ | |
122 | #define IPOPT_SATID 136 /* satnet id */ | |
123 | #define IPOPT_SSRR 137 /* strict source route */ | |
124 | ||
125 | /* | |
126 | * Offsets to fields in options other than EOL and NOP. | |
127 | */ | |
128 | #define IPOPT_OPTVAL 0 /* option ID */ | |
129 | #define IPOPT_OLEN 1 /* option length */ | |
130 | #define IPOPT_OFFSET 2 /* offset within option */ | |
131 | #define IPOPT_MINOFF 4 /* min value of above */ | |
132 | ||
133 | /* | |
134 | * Time stamp option structure. | |
135 | */ | |
136 | struct ip_timestamp { | |
137 | uint8_t ipt_code; /* IPOPT_TS */ | |
138 | uint8_t ipt_len; /* size of structure (variable) */ | |
139 | uint8_t ipt_ptr; /* index of current entry */ | |
140 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
141 | uint8_t ipt_oflw : 4, /* overflow counter */ | |
142 | ipt_flg : 4; /* flags, see below */ | |
143 | #else | |
144 | uint8_t ipt_flg : 4, /* flags, see below */ | |
145 | ipt_oflw : 4; /* overflow counter */ | |
146 | #endif | |
147 | union ipt_timestamp { | |
148 | n_long ipt_time[1]; | |
149 | struct ipt_ta { | |
150 | struct in_addr ipt_addr; | |
151 | n_long ipt_time; | |
152 | } ipt_ta[1]; | |
153 | } ipt_timestamp; | |
154 | } SLIRP_PACKED; | |
155 | ||
156 | /* flag bits for ipt_flg */ | |
157 | #define IPOPT_TS_TSONLY 0 /* timestamps only */ | |
158 | #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ | |
159 | #define IPOPT_TS_PRESPEC 3 /* specified modules only */ | |
160 | ||
161 | /* bits for security (not byte swapped) */ | |
162 | #define IPOPT_SECUR_UNCLASS 0x0000 | |
163 | #define IPOPT_SECUR_CONFID 0xf135 | |
164 | #define IPOPT_SECUR_EFTO 0x789a | |
165 | #define IPOPT_SECUR_MMMM 0xbc4d | |
166 | #define IPOPT_SECUR_RESTR 0xaf13 | |
167 | #define IPOPT_SECUR_SECRET 0xd788 | |
168 | #define IPOPT_SECUR_TOPSECRET 0x6bc5 | |
169 | ||
170 | /* | |
171 | * Internet implementation parameters. | |
172 | */ | |
173 | #define MAXTTL 255 /* maximum time to live (seconds) */ | |
174 | #define IPDEFTTL 64 /* default ttl, from RFC 1340 */ | |
175 | #define IPFRAGTTL 60 /* time to live for frags, slowhz */ | |
176 | #define IPTTLDEC 1 /* subtracted when forwarding */ | |
177 | ||
178 | #define IP_MSS 576 /* default maximum segment size */ | |
179 | ||
180 | #if GLIB_SIZEOF_VOID_P == 4 | |
181 | struct mbuf_ptr { | |
182 | struct mbuf *mptr; | |
183 | uint32_t dummy; | |
184 | } SLIRP_PACKED; | |
185 | #else | |
186 | struct mbuf_ptr { | |
187 | struct mbuf *mptr; | |
188 | } SLIRP_PACKED; | |
189 | #endif | |
190 | struct qlink { | |
191 | void *next, *prev; | |
192 | }; | |
193 | ||
194 | /* | |
195 | * Overlay for ip header used by other protocols (tcp, udp). | |
196 | */ | |
197 | struct ipovly { | |
198 | struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ | |
199 | uint8_t ih_x1; /* (unused) */ | |
200 | uint8_t ih_pr; /* protocol */ | |
201 | uint16_t ih_len; /* protocol length */ | |
202 | struct in_addr ih_src; /* source internet address */ | |
203 | struct in_addr ih_dst; /* destination internet address */ | |
204 | } SLIRP_PACKED; | |
205 | ||
206 | /* | |
207 | * Ip reassembly queue structure. Each fragment | |
208 | * being reassembled is attached to one of these structures. | |
209 | * They are timed out after ipq_ttl drops to 0, and may also | |
210 | * be reclaimed if memory becomes tight. | |
211 | * size 28 bytes | |
212 | */ | |
213 | struct ipq { | |
214 | struct qlink frag_link; /* to ip headers of fragments */ | |
215 | struct qlink ip_link; /* to other reass headers */ | |
216 | uint8_t ipq_ttl; /* time for reass q to live */ | |
217 | uint8_t ipq_p; /* protocol of this fragment */ | |
218 | uint16_t ipq_id; /* sequence id for reassembly */ | |
219 | struct in_addr ipq_src, ipq_dst; | |
220 | }; | |
221 | ||
222 | /* | |
223 | * Ip header, when holding a fragment. | |
224 | * | |
225 | * Note: ipf_link must be at same offset as frag_link above | |
226 | */ | |
227 | struct ipasfrag { | |
228 | struct qlink ipf_link; | |
229 | struct ip ipf_ip; | |
230 | }; | |
231 | ||
232 | G_STATIC_ASSERT(offsetof(struct ipq, frag_link) == | |
233 | offsetof(struct ipasfrag, ipf_link)); | |
234 | ||
235 | #define ipf_off ipf_ip.ip_off | |
236 | #define ipf_tos ipf_ip.ip_tos | |
237 | #define ipf_len ipf_ip.ip_len | |
238 | #define ipf_next ipf_link.next | |
239 | #define ipf_prev ipf_link.prev | |
240 | ||
241 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #ifndef SLIRP_IP6_H | |
7 | #define SLIRP_IP6_H | |
8 | ||
9 | #include <glib.h> | |
10 | #include <string.h> | |
11 | ||
12 | #define ALLNODES_MULTICAST \ | |
13 | { \ | |
14 | .s6_addr = { \ | |
15 | 0xff, \ | |
16 | 0x02, \ | |
17 | 0x00, \ | |
18 | 0x00, \ | |
19 | 0x00, \ | |
20 | 0x00, \ | |
21 | 0x00, \ | |
22 | 0x00, \ | |
23 | 0x00, \ | |
24 | 0x00, \ | |
25 | 0x00, \ | |
26 | 0x00, \ | |
27 | 0x00, \ | |
28 | 0x00, \ | |
29 | 0x00, \ | |
30 | 0x01 \ | |
31 | } \ | |
32 | } | |
33 | ||
34 | #define SOLICITED_NODE_PREFIX \ | |
35 | { \ | |
36 | .s6_addr = { \ | |
37 | 0xff, \ | |
38 | 0x02, \ | |
39 | 0x00, \ | |
40 | 0x00, \ | |
41 | 0x00, \ | |
42 | 0x00, \ | |
43 | 0x00, \ | |
44 | 0x00, \ | |
45 | 0x00, \ | |
46 | 0x00, \ | |
47 | 0x00, \ | |
48 | 0x01, \ | |
49 | 0xff, \ | |
50 | 0x00, \ | |
51 | 0x00, \ | |
52 | 0x00 \ | |
53 | } \ | |
54 | } | |
55 | ||
56 | #define LINKLOCAL_ADDR \ | |
57 | { \ | |
58 | .s6_addr = { \ | |
59 | 0xfe, \ | |
60 | 0x80, \ | |
61 | 0x00, \ | |
62 | 0x00, \ | |
63 | 0x00, \ | |
64 | 0x00, \ | |
65 | 0x00, \ | |
66 | 0x00, \ | |
67 | 0x00, \ | |
68 | 0x00, \ | |
69 | 0x00, \ | |
70 | 0x00, \ | |
71 | 0x00, \ | |
72 | 0x00, \ | |
73 | 0x00, \ | |
74 | 0x02 \ | |
75 | } \ | |
76 | } | |
77 | ||
78 | #define ZERO_ADDR \ | |
79 | { \ | |
80 | .s6_addr = { \ | |
81 | 0x00, \ | |
82 | 0x00, \ | |
83 | 0x00, \ | |
84 | 0x00, \ | |
85 | 0x00, \ | |
86 | 0x00, \ | |
87 | 0x00, \ | |
88 | 0x00, \ | |
89 | 0x00, \ | |
90 | 0x00, \ | |
91 | 0x00, \ | |
92 | 0x00, \ | |
93 | 0x00, \ | |
94 | 0x00, \ | |
95 | 0x00, \ | |
96 | 0x00 \ | |
97 | } \ | |
98 | } | |
99 | ||
100 | static inline bool in6_equal(const struct in6_addr *a, const struct in6_addr *b) | |
101 | { | |
102 | return memcmp(a, b, sizeof(*a)) == 0; | |
103 | } | |
104 | ||
105 | static inline bool in6_equal_net(const struct in6_addr *a, | |
106 | const struct in6_addr *b, int prefix_len) | |
107 | { | |
108 | if (memcmp(a, b, prefix_len / 8) != 0) { | |
109 | return 0; | |
110 | } | |
111 | ||
112 | if (prefix_len % 8 == 0) { | |
113 | return 1; | |
114 | } | |
115 | ||
116 | return a->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)) == | |
117 | b->s6_addr[prefix_len / 8] >> (8 - (prefix_len % 8)); | |
118 | } | |
119 | ||
120 | static inline bool in6_equal_mach(const struct in6_addr *a, | |
121 | const struct in6_addr *b, int prefix_len) | |
122 | { | |
123 | if (memcmp(&(a->s6_addr[DIV_ROUND_UP(prefix_len, 8)]), | |
124 | &(b->s6_addr[DIV_ROUND_UP(prefix_len, 8)]), | |
125 | 16 - DIV_ROUND_UP(prefix_len, 8)) != 0) { | |
126 | return 0; | |
127 | } | |
128 | ||
129 | if (prefix_len % 8 == 0) { | |
130 | return 1; | |
131 | } | |
132 | ||
133 | return (a->s6_addr[prefix_len / 8] & | |
134 | ((1U << (8 - (prefix_len % 8))) - 1)) == | |
135 | (b->s6_addr[prefix_len / 8] & ((1U << (8 - (prefix_len % 8))) - 1)); | |
136 | } | |
137 | ||
138 | ||
139 | #define in6_equal_router(a) \ | |
140 | ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len) && \ | |
141 | in6_equal_mach(a, &slirp->vhost_addr6, slirp->vprefix_len)) || \ | |
142 | (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64) && \ | |
143 | in6_equal_mach(a, &slirp->vhost_addr6, 64))) | |
144 | ||
145 | #define in6_equal_dns(a) \ | |
146 | ((in6_equal_net(a, &slirp->vprefix_addr6, slirp->vprefix_len) && \ | |
147 | in6_equal_mach(a, &slirp->vnameserver_addr6, slirp->vprefix_len)) || \ | |
148 | (in6_equal_net(a, &(struct in6_addr)LINKLOCAL_ADDR, 64) && \ | |
149 | in6_equal_mach(a, &slirp->vnameserver_addr6, 64))) | |
150 | ||
151 | #define in6_equal_host(a) (in6_equal_router(a) || in6_equal_dns(a)) | |
152 | ||
153 | #define in6_solicitednode_multicast(a) \ | |
154 | (in6_equal_net(a, &(struct in6_addr)SOLICITED_NODE_PREFIX, 104)) | |
155 | ||
156 | #define in6_zero(a) (in6_equal(a, &(struct in6_addr)ZERO_ADDR)) | |
157 | ||
158 | /* Compute emulated host MAC address from its ipv6 address */ | |
159 | static inline void in6_compute_ethaddr(struct in6_addr ip, | |
160 | uint8_t eth[ETH_ALEN]) | |
161 | { | |
162 | eth[0] = 0x52; | |
163 | eth[1] = 0x56; | |
164 | memcpy(ð[2], &ip.s6_addr[16 - (ETH_ALEN - 2)], ETH_ALEN - 2); | |
165 | } | |
166 | ||
167 | /* | |
168 | * Definitions for internet protocol version 6. | |
169 | * Per RFC 2460, December 1998. | |
170 | */ | |
171 | #define IP6VERSION 6 | |
172 | #define IP6_HOP_LIMIT 255 | |
173 | ||
174 | /* | |
175 | * Structure of an internet header, naked of options. | |
176 | */ | |
177 | struct ip6 { | |
178 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
179 | uint32_t ip_v : 4, /* version */ | |
180 | ip_tc_hi : 4, /* traffic class */ | |
181 | ip_tc_lo : 4, ip_fl_hi : 4, /* flow label */ | |
182 | ip_fl_lo : 16; | |
183 | #else | |
184 | uint32_t ip_tc_hi : 4, ip_v : 4, ip_fl_hi : 4, ip_tc_lo : 4, ip_fl_lo : 16; | |
185 | #endif | |
186 | uint16_t ip_pl; /* payload length */ | |
187 | uint8_t ip_nh; /* next header */ | |
188 | uint8_t ip_hl; /* hop limit */ | |
189 | struct in6_addr ip_src, ip_dst; /* source and dest address */ | |
190 | }; | |
191 | ||
192 | /* | |
193 | * IPv6 pseudo-header used by upper-layer protocols | |
194 | */ | |
195 | struct ip6_pseudohdr { | |
196 | struct in6_addr ih_src; /* source internet address */ | |
197 | struct in6_addr ih_dst; /* destination internet address */ | |
198 | uint32_t ih_pl; /* upper-layer packet length */ | |
199 | uint16_t ih_zero_hi; /* zero */ | |
200 | uint8_t ih_zero_lo; /* zero */ | |
201 | uint8_t ih_nh; /* next header */ | |
202 | }; | |
203 | ||
204 | /* | |
205 | * We don't want to mark these ip6 structs as packed as they are naturally | |
206 | * correctly aligned; instead assert that there is no stray padding. | |
207 | * If we marked the struct as packed then we would be unable to take | |
208 | * the address of any of the fields in it. | |
209 | */ | |
210 | G_STATIC_ASSERT(sizeof(struct ip6) == 40); | |
211 | G_STATIC_ASSERT(sizeof(struct ip6_pseudohdr) == 40); | |
212 | ||
213 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #include "slirp.h" | |
7 | #include "ip6_icmp.h" | |
8 | ||
9 | #define NDP_Interval \ | |
10 | g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) | |
11 | ||
12 | static void ra_timer_handler(void *opaque) | |
13 | { | |
14 | Slirp *slirp = opaque; | |
15 | ||
16 | slirp->cb->timer_mod(slirp->ra_timer, | |
17 | slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + | |
18 | NDP_Interval, | |
19 | slirp->opaque); | |
20 | ndp_send_ra(slirp); | |
21 | } | |
22 | ||
23 | void icmp6_init(Slirp *slirp) | |
24 | { | |
25 | if (!slirp->in6_enabled) { | |
26 | return; | |
27 | } | |
28 | ||
29 | slirp->ra_timer = | |
30 | slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque); | |
31 | slirp->cb->timer_mod(slirp->ra_timer, | |
32 | slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + | |
33 | NDP_Interval, | |
34 | slirp->opaque); | |
35 | } | |
36 | ||
37 | void icmp6_cleanup(Slirp *slirp) | |
38 | { | |
39 | if (!slirp->in6_enabled) { | |
40 | return; | |
41 | } | |
42 | ||
43 | slirp->cb->timer_free(slirp->ra_timer, slirp->opaque); | |
44 | } | |
45 | ||
46 | static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip, | |
47 | struct icmp6 *icmp) | |
48 | { | |
49 | struct mbuf *t = m_get(slirp); | |
50 | t->m_len = sizeof(struct ip6) + ntohs(ip->ip_pl); | |
51 | memcpy(t->m_data, m->m_data, t->m_len); | |
52 | ||
53 | /* IPv6 Packet */ | |
54 | struct ip6 *rip = mtod(t, struct ip6 *); | |
55 | rip->ip_dst = ip->ip_src; | |
56 | rip->ip_src = ip->ip_dst; | |
57 | ||
58 | /* ICMPv6 packet */ | |
59 | t->m_data += sizeof(struct ip6); | |
60 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
61 | ricmp->icmp6_type = ICMP6_ECHO_REPLY; | |
62 | ricmp->icmp6_cksum = 0; | |
63 | ||
64 | /* Checksum */ | |
65 | t->m_data -= sizeof(struct ip6); | |
66 | ricmp->icmp6_cksum = ip6_cksum(t); | |
67 | ||
68 | ip6_output(NULL, t, 0); | |
69 | } | |
70 | ||
71 | void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code) | |
72 | { | |
73 | Slirp *slirp = m->slirp; | |
74 | struct mbuf *t; | |
75 | struct ip6 *ip = mtod(m, struct ip6 *); | |
76 | char addrstr[INET6_ADDRSTRLEN]; | |
77 | ||
78 | DEBUG_CALL("icmp6_send_error"); | |
79 | DEBUG_ARG("type = %d, code = %d", type, code); | |
80 | ||
81 | if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || in6_zero(&ip->ip_src)) { | |
82 | /* TODO icmp error? */ | |
83 | return; | |
84 | } | |
85 | ||
86 | t = m_get(slirp); | |
87 | ||
88 | /* IPv6 packet */ | |
89 | struct ip6 *rip = mtod(t, struct ip6 *); | |
90 | rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; | |
91 | rip->ip_dst = ip->ip_src; | |
92 | inet_ntop(AF_INET6, &rip->ip_dst, addrstr, INET6_ADDRSTRLEN); | |
93 | DEBUG_ARG("target = %s", addrstr); | |
94 | ||
95 | rip->ip_nh = IPPROTO_ICMPV6; | |
96 | const int error_data_len = MIN( | |
97 | m->m_len, slirp->if_mtu - (sizeof(struct ip6) + ICMP6_ERROR_MINLEN)); | |
98 | rip->ip_pl = htons(ICMP6_ERROR_MINLEN + error_data_len); | |
99 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
100 | ||
101 | /* ICMPv6 packet */ | |
102 | t->m_data += sizeof(struct ip6); | |
103 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
104 | ricmp->icmp6_type = type; | |
105 | ricmp->icmp6_code = code; | |
106 | ricmp->icmp6_cksum = 0; | |
107 | ||
108 | switch (type) { | |
109 | case ICMP6_UNREACH: | |
110 | case ICMP6_TIMXCEED: | |
111 | ricmp->icmp6_err.unused = 0; | |
112 | break; | |
113 | case ICMP6_TOOBIG: | |
114 | ricmp->icmp6_err.mtu = htonl(slirp->if_mtu); | |
115 | break; | |
116 | case ICMP6_PARAMPROB: | |
117 | /* TODO: Handle this case */ | |
118 | break; | |
119 | default: | |
120 | g_assert_not_reached(); | |
121 | break; | |
122 | } | |
123 | t->m_data += ICMP6_ERROR_MINLEN; | |
124 | memcpy(t->m_data, m->m_data, error_data_len); | |
125 | ||
126 | /* Checksum */ | |
127 | t->m_data -= ICMP6_ERROR_MINLEN; | |
128 | t->m_data -= sizeof(struct ip6); | |
129 | ricmp->icmp6_cksum = ip6_cksum(t); | |
130 | ||
131 | ip6_output(NULL, t, 0); | |
132 | } | |
133 | ||
134 | /* | |
135 | * Send NDP Router Advertisement | |
136 | */ | |
137 | void ndp_send_ra(Slirp *slirp) | |
138 | { | |
139 | DEBUG_CALL("ndp_send_ra"); | |
140 | ||
141 | /* Build IPv6 packet */ | |
142 | struct mbuf *t = m_get(slirp); | |
143 | struct ip6 *rip = mtod(t, struct ip6 *); | |
144 | size_t pl_size = 0; | |
145 | struct in6_addr addr; | |
146 | uint32_t scope_id; | |
147 | ||
148 | rip->ip_src = (struct in6_addr)LINKLOCAL_ADDR; | |
149 | rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; | |
150 | rip->ip_nh = IPPROTO_ICMPV6; | |
151 | ||
152 | /* Build ICMPv6 packet */ | |
153 | t->m_data += sizeof(struct ip6); | |
154 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
155 | ricmp->icmp6_type = ICMP6_NDP_RA; | |
156 | ricmp->icmp6_code = 0; | |
157 | ricmp->icmp6_cksum = 0; | |
158 | ||
159 | /* NDP */ | |
160 | ricmp->icmp6_nra.chl = NDP_AdvCurHopLimit; | |
161 | ricmp->icmp6_nra.M = NDP_AdvManagedFlag; | |
162 | ricmp->icmp6_nra.O = NDP_AdvOtherConfigFlag; | |
163 | ricmp->icmp6_nra.reserved = 0; | |
164 | ricmp->icmp6_nra.lifetime = htons(NDP_AdvDefaultLifetime); | |
165 | ricmp->icmp6_nra.reach_time = htonl(NDP_AdvReachableTime); | |
166 | ricmp->icmp6_nra.retrans_time = htonl(NDP_AdvRetransTime); | |
167 | t->m_data += ICMP6_NDP_RA_MINLEN; | |
168 | pl_size += ICMP6_NDP_RA_MINLEN; | |
169 | ||
170 | /* Source link-layer address (NDP option) */ | |
171 | struct ndpopt *opt = mtod(t, struct ndpopt *); | |
172 | opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; | |
173 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
174 | in6_compute_ethaddr(rip->ip_src, opt->ndpopt_linklayer); | |
175 | t->m_data += NDPOPT_LINKLAYER_LEN; | |
176 | pl_size += NDPOPT_LINKLAYER_LEN; | |
177 | ||
178 | /* Prefix information (NDP option) */ | |
179 | struct ndpopt *opt2 = mtod(t, struct ndpopt *); | |
180 | opt2->ndpopt_type = NDPOPT_PREFIX_INFO; | |
181 | opt2->ndpopt_len = NDPOPT_PREFIXINFO_LEN / 8; | |
182 | opt2->ndpopt_prefixinfo.prefix_length = slirp->vprefix_len; | |
183 | opt2->ndpopt_prefixinfo.L = 1; | |
184 | opt2->ndpopt_prefixinfo.A = 1; | |
185 | opt2->ndpopt_prefixinfo.reserved1 = 0; | |
186 | opt2->ndpopt_prefixinfo.valid_lt = htonl(NDP_AdvValidLifetime); | |
187 | opt2->ndpopt_prefixinfo.pref_lt = htonl(NDP_AdvPrefLifetime); | |
188 | opt2->ndpopt_prefixinfo.reserved2 = 0; | |
189 | opt2->ndpopt_prefixinfo.prefix = slirp->vprefix_addr6; | |
190 | t->m_data += NDPOPT_PREFIXINFO_LEN; | |
191 | pl_size += NDPOPT_PREFIXINFO_LEN; | |
192 | ||
193 | /* Prefix information (NDP option) */ | |
194 | if (get_dns6_addr(&addr, &scope_id) >= 0) { | |
195 | /* Host system does have an IPv6 DNS server, announce our proxy. */ | |
196 | struct ndpopt *opt3 = mtod(t, struct ndpopt *); | |
197 | opt3->ndpopt_type = NDPOPT_RDNSS; | |
198 | opt3->ndpopt_len = NDPOPT_RDNSS_LEN / 8; | |
199 | opt3->ndpopt_rdnss.reserved = 0; | |
200 | opt3->ndpopt_rdnss.lifetime = htonl(2 * NDP_MaxRtrAdvInterval); | |
201 | opt3->ndpopt_rdnss.addr = slirp->vnameserver_addr6; | |
202 | t->m_data += NDPOPT_RDNSS_LEN; | |
203 | pl_size += NDPOPT_RDNSS_LEN; | |
204 | } | |
205 | ||
206 | rip->ip_pl = htons(pl_size); | |
207 | t->m_data -= sizeof(struct ip6) + pl_size; | |
208 | t->m_len = sizeof(struct ip6) + pl_size; | |
209 | ||
210 | /* ICMPv6 Checksum */ | |
211 | ricmp->icmp6_cksum = ip6_cksum(t); | |
212 | ||
213 | ip6_output(NULL, t, 0); | |
214 | } | |
215 | ||
216 | /* | |
217 | * Send NDP Neighbor Solitication | |
218 | */ | |
219 | void ndp_send_ns(Slirp *slirp, struct in6_addr addr) | |
220 | { | |
221 | char addrstr[INET6_ADDRSTRLEN]; | |
222 | ||
223 | inet_ntop(AF_INET6, &addr, addrstr, INET6_ADDRSTRLEN); | |
224 | ||
225 | DEBUG_CALL("ndp_send_ns"); | |
226 | DEBUG_ARG("target = %s", addrstr); | |
227 | ||
228 | /* Build IPv6 packet */ | |
229 | struct mbuf *t = m_get(slirp); | |
230 | struct ip6 *rip = mtod(t, struct ip6 *); | |
231 | rip->ip_src = slirp->vhost_addr6; | |
232 | rip->ip_dst = (struct in6_addr)SOLICITED_NODE_PREFIX; | |
233 | memcpy(&rip->ip_dst.s6_addr[13], &addr.s6_addr[13], 3); | |
234 | rip->ip_nh = IPPROTO_ICMPV6; | |
235 | rip->ip_pl = htons(ICMP6_NDP_NS_MINLEN + NDPOPT_LINKLAYER_LEN); | |
236 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
237 | ||
238 | /* Build ICMPv6 packet */ | |
239 | t->m_data += sizeof(struct ip6); | |
240 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
241 | ricmp->icmp6_type = ICMP6_NDP_NS; | |
242 | ricmp->icmp6_code = 0; | |
243 | ricmp->icmp6_cksum = 0; | |
244 | ||
245 | /* NDP */ | |
246 | ricmp->icmp6_nns.reserved = 0; | |
247 | ricmp->icmp6_nns.target = addr; | |
248 | ||
249 | /* Build NDP option */ | |
250 | t->m_data += ICMP6_NDP_NS_MINLEN; | |
251 | struct ndpopt *opt = mtod(t, struct ndpopt *); | |
252 | opt->ndpopt_type = NDPOPT_LINKLAYER_SOURCE; | |
253 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
254 | in6_compute_ethaddr(slirp->vhost_addr6, opt->ndpopt_linklayer); | |
255 | ||
256 | /* ICMPv6 Checksum */ | |
257 | t->m_data -= ICMP6_NDP_NA_MINLEN; | |
258 | t->m_data -= sizeof(struct ip6); | |
259 | ricmp->icmp6_cksum = ip6_cksum(t); | |
260 | ||
261 | ip6_output(NULL, t, 1); | |
262 | } | |
263 | ||
264 | /* | |
265 | * Send NDP Neighbor Advertisement | |
266 | */ | |
267 | static void ndp_send_na(Slirp *slirp, struct ip6 *ip, struct icmp6 *icmp) | |
268 | { | |
269 | /* Build IPv6 packet */ | |
270 | struct mbuf *t = m_get(slirp); | |
271 | struct ip6 *rip = mtod(t, struct ip6 *); | |
272 | rip->ip_src = icmp->icmp6_nns.target; | |
273 | if (in6_zero(&ip->ip_src)) { | |
274 | rip->ip_dst = (struct in6_addr)ALLNODES_MULTICAST; | |
275 | } else { | |
276 | rip->ip_dst = ip->ip_src; | |
277 | } | |
278 | rip->ip_nh = IPPROTO_ICMPV6; | |
279 | rip->ip_pl = htons(ICMP6_NDP_NA_MINLEN + NDPOPT_LINKLAYER_LEN); | |
280 | t->m_len = sizeof(struct ip6) + ntohs(rip->ip_pl); | |
281 | ||
282 | /* Build ICMPv6 packet */ | |
283 | t->m_data += sizeof(struct ip6); | |
284 | struct icmp6 *ricmp = mtod(t, struct icmp6 *); | |
285 | ricmp->icmp6_type = ICMP6_NDP_NA; | |
286 | ricmp->icmp6_code = 0; | |
287 | ricmp->icmp6_cksum = 0; | |
288 | ||
289 | /* NDP */ | |
290 | ricmp->icmp6_nna.R = NDP_IsRouter; | |
291 | ricmp->icmp6_nna.S = !IN6_IS_ADDR_MULTICAST(&rip->ip_dst); | |
292 | ricmp->icmp6_nna.O = 1; | |
293 | ricmp->icmp6_nna.reserved_hi = 0; | |
294 | ricmp->icmp6_nna.reserved_lo = 0; | |
295 | ricmp->icmp6_nna.target = icmp->icmp6_nns.target; | |
296 | ||
297 | /* Build NDP option */ | |
298 | t->m_data += ICMP6_NDP_NA_MINLEN; | |
299 | struct ndpopt *opt = mtod(t, struct ndpopt *); | |
300 | opt->ndpopt_type = NDPOPT_LINKLAYER_TARGET; | |
301 | opt->ndpopt_len = NDPOPT_LINKLAYER_LEN / 8; | |
302 | in6_compute_ethaddr(ricmp->icmp6_nna.target, opt->ndpopt_linklayer); | |
303 | ||
304 | /* ICMPv6 Checksum */ | |
305 | t->m_data -= ICMP6_NDP_NA_MINLEN; | |
306 | t->m_data -= sizeof(struct ip6); | |
307 | ricmp->icmp6_cksum = ip6_cksum(t); | |
308 | ||
309 | ip6_output(NULL, t, 0); | |
310 | } | |
311 | ||
312 | /* | |
313 | * Process a NDP message | |
314 | */ | |
315 | static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, | |
316 | struct icmp6 *icmp) | |
317 | { | |
318 | m->m_len += ETH_HLEN; | |
319 | m->m_data -= ETH_HLEN; | |
320 | struct ethhdr *eth = mtod(m, struct ethhdr *); | |
321 | m->m_len -= ETH_HLEN; | |
322 | m->m_data += ETH_HLEN; | |
323 | ||
324 | switch (icmp->icmp6_type) { | |
325 | case ICMP6_NDP_RS: | |
326 | DEBUG_CALL(" type = Router Solicitation"); | |
327 | if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && | |
328 | ntohs(ip->ip_pl) >= ICMP6_NDP_RS_MINLEN) { | |
329 | /* Gratuitous NDP */ | |
330 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
331 | ||
332 | ndp_send_ra(slirp); | |
333 | } | |
334 | break; | |
335 | ||
336 | case ICMP6_NDP_RA: | |
337 | DEBUG_CALL(" type = Router Advertisement"); | |
338 | slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't", | |
339 | slirp->opaque); | |
340 | break; | |
341 | ||
342 | case ICMP6_NDP_NS: | |
343 | DEBUG_CALL(" type = Neighbor Solicitation"); | |
344 | if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && | |
345 | !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nns.target) && | |
346 | ntohs(ip->ip_pl) >= ICMP6_NDP_NS_MINLEN && | |
347 | (!in6_zero(&ip->ip_src) || | |
348 | in6_solicitednode_multicast(&ip->ip_dst))) { | |
349 | if (in6_equal_host(&icmp->icmp6_nns.target)) { | |
350 | /* Gratuitous NDP */ | |
351 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
352 | ndp_send_na(slirp, ip, icmp); | |
353 | } | |
354 | } | |
355 | break; | |
356 | ||
357 | case ICMP6_NDP_NA: | |
358 | DEBUG_CALL(" type = Neighbor Advertisement"); | |
359 | if (ip->ip_hl == 255 && icmp->icmp6_code == 0 && | |
360 | ntohs(ip->ip_pl) >= ICMP6_NDP_NA_MINLEN && | |
361 | !IN6_IS_ADDR_MULTICAST(&icmp->icmp6_nna.target) && | |
362 | (!IN6_IS_ADDR_MULTICAST(&ip->ip_dst) || icmp->icmp6_nna.S == 0)) { | |
363 | ndp_table_add(slirp, ip->ip_src, eth->h_source); | |
364 | } | |
365 | break; | |
366 | ||
367 | case ICMP6_NDP_REDIRECT: | |
368 | DEBUG_CALL(" type = Redirect"); | |
369 | slirp->cb->guest_error( | |
370 | "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque); | |
371 | break; | |
372 | } | |
373 | } | |
374 | ||
375 | /* | |
376 | * Process a received ICMPv6 message. | |
377 | */ | |
378 | void icmp6_input(struct mbuf *m) | |
379 | { | |
380 | struct icmp6 *icmp; | |
381 | struct ip6 *ip = mtod(m, struct ip6 *); | |
382 | Slirp *slirp = m->slirp; | |
383 | int hlen = sizeof(struct ip6); | |
384 | ||
385 | DEBUG_CALL("icmp6_input"); | |
386 | DEBUG_ARG("m = %p", m); | |
387 | DEBUG_ARG("m_len = %d", m->m_len); | |
388 | ||
389 | if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { | |
390 | goto end; | |
391 | } | |
392 | ||
393 | if (ip6_cksum(m)) { | |
394 | goto end; | |
395 | } | |
396 | ||
397 | m->m_len -= hlen; | |
398 | m->m_data += hlen; | |
399 | icmp = mtod(m, struct icmp6 *); | |
400 | m->m_len += hlen; | |
401 | m->m_data -= hlen; | |
402 | ||
403 | DEBUG_ARG("icmp6_type = %d", icmp->icmp6_type); | |
404 | switch (icmp->icmp6_type) { | |
405 | case ICMP6_ECHO_REQUEST: | |
406 | if (in6_equal_host(&ip->ip_dst)) { | |
407 | icmp6_send_echoreply(m, slirp, ip, icmp); | |
408 | } else { | |
409 | /* TODO */ | |
410 | g_critical("external icmpv6 not supported yet"); | |
411 | } | |
412 | break; | |
413 | ||
414 | case ICMP6_NDP_RS: | |
415 | case ICMP6_NDP_RA: | |
416 | case ICMP6_NDP_NS: | |
417 | case ICMP6_NDP_NA: | |
418 | case ICMP6_NDP_REDIRECT: | |
419 | ndp_input(m, slirp, ip, icmp); | |
420 | break; | |
421 | ||
422 | case ICMP6_UNREACH: | |
423 | case ICMP6_TOOBIG: | |
424 | case ICMP6_TIMXCEED: | |
425 | case ICMP6_PARAMPROB: | |
426 | /* XXX? report error? close socket? */ | |
427 | default: | |
428 | break; | |
429 | } | |
430 | ||
431 | end: | |
432 | m_free(m); | |
433 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #ifndef SLIRP_IP6_ICMP_H | |
7 | #define SLIRP_IP6_ICMP_H | |
8 | ||
9 | /* | |
10 | * Interface Control Message Protocol version 6 Definitions. | |
11 | * Per RFC 4443, March 2006. | |
12 | * | |
13 | * Network Discover Protocol Definitions. | |
14 | * Per RFC 4861, September 2007. | |
15 | */ | |
16 | ||
17 | struct icmp6_echo { /* Echo Messages */ | |
18 | uint16_t id; | |
19 | uint16_t seq_num; | |
20 | }; | |
21 | ||
22 | union icmp6_error_body { | |
23 | uint32_t unused; | |
24 | uint32_t pointer; | |
25 | uint32_t mtu; | |
26 | }; | |
27 | ||
28 | /* | |
29 | * NDP Messages | |
30 | */ | |
31 | struct ndp_rs { /* Router Solicitation Message */ | |
32 | uint32_t reserved; | |
33 | }; | |
34 | ||
35 | struct ndp_ra { /* Router Advertisement Message */ | |
36 | uint8_t chl; /* Cur Hop Limit */ | |
37 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
38 | uint8_t M : 1, O : 1, reserved : 6; | |
39 | #else | |
40 | uint8_t reserved : 6, O : 1, M : 1; | |
41 | #endif | |
42 | uint16_t lifetime; /* Router Lifetime */ | |
43 | uint32_t reach_time; /* Reachable Time */ | |
44 | uint32_t retrans_time; /* Retrans Timer */ | |
45 | }; | |
46 | ||
47 | G_STATIC_ASSERT(sizeof(struct ndp_ra) == 12); | |
48 | ||
49 | struct ndp_ns { /* Neighbor Solicitation Message */ | |
50 | uint32_t reserved; | |
51 | struct in6_addr target; /* Target Address */ | |
52 | }; | |
53 | ||
54 | G_STATIC_ASSERT(sizeof(struct ndp_ns) == 20); | |
55 | ||
56 | struct ndp_na { /* Neighbor Advertisement Message */ | |
57 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
58 | uint32_t R : 1, /* Router Flag */ | |
59 | S : 1, /* Solicited Flag */ | |
60 | O : 1, /* Override Flag */ | |
61 | reserved_hi : 5, reserved_lo : 24; | |
62 | #else | |
63 | uint32_t reserved_hi : 5, O : 1, S : 1, R : 1, reserved_lo : 24; | |
64 | #endif | |
65 | struct in6_addr target; /* Target Address */ | |
66 | }; | |
67 | ||
68 | G_STATIC_ASSERT(sizeof(struct ndp_na) == 20); | |
69 | ||
70 | struct ndp_redirect { | |
71 | uint32_t reserved; | |
72 | struct in6_addr target; /* Target Address */ | |
73 | struct in6_addr dest; /* Destination Address */ | |
74 | }; | |
75 | ||
76 | G_STATIC_ASSERT(sizeof(struct ndp_redirect) == 36); | |
77 | ||
78 | /* | |
79 | * Structure of an icmpv6 header. | |
80 | */ | |
81 | struct icmp6 { | |
82 | uint8_t icmp6_type; /* type of message, see below */ | |
83 | uint8_t icmp6_code; /* type sub code */ | |
84 | uint16_t icmp6_cksum; /* ones complement cksum of struct */ | |
85 | union { | |
86 | union icmp6_error_body error_body; | |
87 | struct icmp6_echo echo; | |
88 | struct ndp_rs ndp_rs; | |
89 | struct ndp_ra ndp_ra; | |
90 | struct ndp_ns ndp_ns; | |
91 | struct ndp_na ndp_na; | |
92 | struct ndp_redirect ndp_redirect; | |
93 | } icmp6_body; | |
94 | #define icmp6_err icmp6_body.error_body | |
95 | #define icmp6_echo icmp6_body.echo | |
96 | #define icmp6_nrs icmp6_body.ndp_rs | |
97 | #define icmp6_nra icmp6_body.ndp_ra | |
98 | #define icmp6_nns icmp6_body.ndp_ns | |
99 | #define icmp6_nna icmp6_body.ndp_na | |
100 | #define icmp6_redirect icmp6_body.ndp_redirect | |
101 | }; | |
102 | ||
103 | G_STATIC_ASSERT(sizeof(struct icmp6) == 40); | |
104 | ||
105 | #define ICMP6_MINLEN 4 | |
106 | #define ICMP6_ERROR_MINLEN 8 | |
107 | #define ICMP6_ECHO_MINLEN 8 | |
108 | #define ICMP6_NDP_RS_MINLEN 8 | |
109 | #define ICMP6_NDP_RA_MINLEN 16 | |
110 | #define ICMP6_NDP_NS_MINLEN 24 | |
111 | #define ICMP6_NDP_NA_MINLEN 24 | |
112 | #define ICMP6_NDP_REDIRECT_MINLEN 40 | |
113 | ||
114 | /* | |
115 | * NDP Options | |
116 | */ | |
117 | struct ndpopt { | |
118 | uint8_t ndpopt_type; /* Option type */ | |
119 | uint8_t ndpopt_len; /* /!\ In units of 8 octets */ | |
120 | union { | |
121 | unsigned char linklayer_addr[6]; /* Source/Target Link-layer */ | |
122 | #define ndpopt_linklayer ndpopt_body.linklayer_addr | |
123 | struct prefixinfo { /* Prefix Information */ | |
124 | uint8_t prefix_length; | |
125 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
126 | uint8_t L : 1, A : 1, reserved1 : 6; | |
127 | #else | |
128 | uint8_t reserved1 : 6, A : 1, L : 1; | |
129 | #endif | |
130 | uint32_t valid_lt; /* Valid Lifetime */ | |
131 | uint32_t pref_lt; /* Preferred Lifetime */ | |
132 | uint32_t reserved2; | |
133 | struct in6_addr prefix; | |
134 | } SLIRP_PACKED prefixinfo; | |
135 | #define ndpopt_prefixinfo ndpopt_body.prefixinfo | |
136 | struct rdnss { | |
137 | uint16_t reserved; | |
138 | uint32_t lifetime; | |
139 | struct in6_addr addr; | |
140 | } SLIRP_PACKED rdnss; | |
141 | #define ndpopt_rdnss ndpopt_body.rdnss | |
142 | } ndpopt_body; | |
143 | } SLIRP_PACKED; | |
144 | ||
145 | /* NDP options type */ | |
146 | #define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */ | |
147 | #define NDPOPT_LINKLAYER_TARGET 2 /* Target Link-Layer Address */ | |
148 | #define NDPOPT_PREFIX_INFO 3 /* Prefix Information */ | |
149 | #define NDPOPT_RDNSS 25 /* Recursive DNS Server Address */ | |
150 | ||
151 | /* NDP options size, in octets. */ | |
152 | #define NDPOPT_LINKLAYER_LEN 8 | |
153 | #define NDPOPT_PREFIXINFO_LEN 32 | |
154 | #define NDPOPT_RDNSS_LEN 24 | |
155 | ||
156 | /* | |
157 | * Definition of type and code field values. | |
158 | * Per https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xml | |
159 | * Last Updated 2012-11-12 | |
160 | */ | |
161 | ||
162 | /* Errors */ | |
163 | #define ICMP6_UNREACH 1 /* Destination Unreachable */ | |
164 | #define ICMP6_UNREACH_NO_ROUTE 0 /* no route to dest */ | |
165 | #define ICMP6_UNREACH_DEST_PROHIB 1 /* com with dest prohibited */ | |
166 | #define ICMP6_UNREACH_SCOPE 2 /* beyond scope of src addr */ | |
167 | #define ICMP6_UNREACH_ADDRESS 3 /* address unreachable */ | |
168 | #define ICMP6_UNREACH_PORT 4 /* port unreachable */ | |
169 | #define ICMP6_UNREACH_SRC_FAIL 5 /* src addr failed */ | |
170 | #define ICMP6_UNREACH_REJECT_ROUTE 6 /* reject route to dest */ | |
171 | #define ICMP6_UNREACH_SRC_HDR_ERROR 7 /* error in src routing header */ | |
172 | #define ICMP6_TOOBIG 2 /* Packet Too Big */ | |
173 | #define ICMP6_TIMXCEED 3 /* Time Exceeded */ | |
174 | #define ICMP6_TIMXCEED_INTRANS 0 /* hop limit exceeded in transit */ | |
175 | #define ICMP6_TIMXCEED_REASS 1 /* ttl=0 in reass */ | |
176 | #define ICMP6_PARAMPROB 4 /* Parameter Problem */ | |
177 | #define ICMP6_PARAMPROB_HDR_FIELD 0 /* err header field */ | |
178 | #define ICMP6_PARAMPROB_NXTHDR_TYPE 1 /* unrecognized Next Header type */ | |
179 | #define ICMP6_PARAMPROB_IPV6_OPT 2 /* unrecognized IPv6 option */ | |
180 | ||
181 | /* Informational Messages */ | |
182 | #define ICMP6_ECHO_REQUEST 128 /* Echo Request */ | |
183 | #define ICMP6_ECHO_REPLY 129 /* Echo Reply */ | |
184 | #define ICMP6_NDP_RS 133 /* Router Solicitation (NDP) */ | |
185 | #define ICMP6_NDP_RA 134 /* Router Advertisement (NDP) */ | |
186 | #define ICMP6_NDP_NS 135 /* Neighbor Solicitation (NDP) */ | |
187 | #define ICMP6_NDP_NA 136 /* Neighbor Advertisement (NDP) */ | |
188 | #define ICMP6_NDP_REDIRECT 137 /* Redirect Message (NDP) */ | |
189 | ||
190 | /* | |
191 | * Router Configuration Variables (rfc4861#section-6) | |
192 | */ | |
193 | #define NDP_IsRouter 1 | |
194 | #define NDP_AdvSendAdvertisements 1 | |
195 | #define NDP_MaxRtrAdvInterval 600000 | |
196 | #define NDP_MinRtrAdvInterval \ | |
197 | ((NDP_MaxRtrAdvInterval >= 9) ? NDP_MaxRtrAdvInterval / 3 : \ | |
198 | NDP_MaxRtrAdvInterval) | |
199 | #define NDP_AdvManagedFlag 0 | |
200 | #define NDP_AdvOtherConfigFlag 0 | |
201 | #define NDP_AdvLinkMTU 0 | |
202 | #define NDP_AdvReachableTime 0 | |
203 | #define NDP_AdvRetransTime 0 | |
204 | #define NDP_AdvCurHopLimit 64 | |
205 | #define NDP_AdvDefaultLifetime ((3 * NDP_MaxRtrAdvInterval) / 1000) | |
206 | #define NDP_AdvValidLifetime 86400 | |
207 | #define NDP_AdvOnLinkFlag 1 | |
208 | #define NDP_AdvPrefLifetime 14400 | |
209 | #define NDP_AdvAutonomousFlag 1 | |
210 | ||
211 | void icmp6_init(Slirp *slirp); | |
212 | void icmp6_cleanup(Slirp *slirp); | |
213 | void icmp6_input(struct mbuf *); | |
214 | void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code); | |
215 | void ndp_send_ra(Slirp *slirp); | |
216 | void ndp_send_ns(Slirp *slirp, struct in6_addr addr); | |
217 | ||
218 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #include "slirp.h" | |
7 | #include "ip6_icmp.h" | |
8 | ||
9 | /* | |
10 | * IP initialization: fill in IP protocol switch table. | |
11 | * All protocols not implemented in kernel go to raw IP protocol handler. | |
12 | */ | |
13 | void ip6_init(Slirp *slirp) | |
14 | { | |
15 | icmp6_init(slirp); | |
16 | } | |
17 | ||
18 | void ip6_cleanup(Slirp *slirp) | |
19 | { | |
20 | icmp6_cleanup(slirp); | |
21 | } | |
22 | ||
23 | void ip6_input(struct mbuf *m) | |
24 | { | |
25 | struct ip6 *ip6; | |
26 | Slirp *slirp = m->slirp; | |
27 | ||
28 | if (!slirp->in6_enabled) { | |
29 | goto bad; | |
30 | } | |
31 | ||
32 | DEBUG_CALL("ip6_input"); | |
33 | DEBUG_ARG("m = %p", m); | |
34 | DEBUG_ARG("m_len = %d", m->m_len); | |
35 | ||
36 | if (m->m_len < sizeof(struct ip6)) { | |
37 | goto bad; | |
38 | } | |
39 | ||
40 | ip6 = mtod(m, struct ip6 *); | |
41 | ||
42 | if (ip6->ip_v != IP6VERSION) { | |
43 | goto bad; | |
44 | } | |
45 | ||
46 | if (ntohs(ip6->ip_pl) > slirp->if_mtu) { | |
47 | icmp6_send_error(m, ICMP6_TOOBIG, 0); | |
48 | goto bad; | |
49 | } | |
50 | ||
51 | /* check ip_ttl for a correct ICMP reply */ | |
52 | if (ip6->ip_hl == 0) { | |
53 | icmp6_send_error(m, ICMP6_TIMXCEED, ICMP6_TIMXCEED_INTRANS); | |
54 | goto bad; | |
55 | } | |
56 | ||
57 | /* | |
58 | * Switch out to protocol's input routine. | |
59 | */ | |
60 | switch (ip6->ip_nh) { | |
61 | case IPPROTO_TCP: | |
62 | NTOHS(ip6->ip_pl); | |
63 | tcp_input(m, sizeof(struct ip6), (struct socket *)NULL, AF_INET6); | |
64 | break; | |
65 | case IPPROTO_UDP: | |
66 | udp6_input(m); | |
67 | break; | |
68 | case IPPROTO_ICMPV6: | |
69 | icmp6_input(m); | |
70 | break; | |
71 | default: | |
72 | m_free(m); | |
73 | } | |
74 | return; | |
75 | bad: | |
76 | m_free(m); | |
77 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #include "slirp.h" | |
7 | ||
8 | /* Number of packets queued before we start sending | |
9 | * (to prevent allocing too many mbufs) */ | |
10 | #define IF6_THRESH 10 | |
11 | ||
12 | /* | |
13 | * IPv6 output. The packet in mbuf chain m contains a IP header | |
14 | */ | |
15 | int ip6_output(struct socket *so, struct mbuf *m, int fast) | |
16 | { | |
17 | struct ip6 *ip = mtod(m, struct ip6 *); | |
18 | ||
19 | DEBUG_CALL("ip6_output"); | |
20 | DEBUG_ARG("so = %p", so); | |
21 | DEBUG_ARG("m = %p", m); | |
22 | ||
23 | /* Fill IPv6 header */ | |
24 | ip->ip_v = IP6VERSION; | |
25 | ip->ip_hl = IP6_HOP_LIMIT; | |
26 | ip->ip_tc_hi = 0; | |
27 | ip->ip_tc_lo = 0; | |
28 | ip->ip_fl_hi = 0; | |
29 | ip->ip_fl_lo = 0; | |
30 | ||
31 | if (fast) { | |
32 | if_encap(m->slirp, m); | |
33 | } else { | |
34 | if_output(so, m); | |
35 | } | |
36 | ||
37 | return 0; | |
38 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94 | |
30 | * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp | |
31 | */ | |
32 | ||
33 | #include "slirp.h" | |
34 | #include "ip_icmp.h" | |
35 | ||
36 | #ifndef WITH_ICMP_ERROR_MSG | |
37 | #define WITH_ICMP_ERROR_MSG 0 | |
38 | #endif | |
39 | ||
40 | /* The message sent when emulating PING */ | |
41 | /* Be nice and tell them it's just a pseudo-ping packet */ | |
42 | static const char icmp_ping_msg[] = | |
43 | "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST " | |
44 | "packets.\n"; | |
45 | ||
46 | /* list of actions for icmp_send_error() on RX of an icmp message */ | |
47 | static const int icmp_flush[19] = { | |
48 | /* ECHO REPLY (0) */ 0, | |
49 | 1, | |
50 | 1, | |
51 | /* DEST UNREACH (3) */ 1, | |
52 | /* SOURCE QUENCH (4)*/ 1, | |
53 | /* REDIRECT (5) */ 1, | |
54 | 1, | |
55 | 1, | |
56 | /* ECHO (8) */ 0, | |
57 | /* ROUTERADVERT (9) */ 1, | |
58 | /* ROUTERSOLICIT (10) */ 1, | |
59 | /* TIME EXCEEDED (11) */ 1, | |
60 | /* PARAMETER PROBLEM (12) */ 1, | |
61 | /* TIMESTAMP (13) */ 0, | |
62 | /* TIMESTAMP REPLY (14) */ 0, | |
63 | /* INFO (15) */ 0, | |
64 | /* INFO REPLY (16) */ 0, | |
65 | /* ADDR MASK (17) */ 0, | |
66 | /* ADDR MASK REPLY (18) */ 0 | |
67 | }; | |
68 | ||
69 | void icmp_init(Slirp *slirp) | |
70 | { | |
71 | slirp->icmp.so_next = slirp->icmp.so_prev = &slirp->icmp; | |
72 | slirp->icmp_last_so = &slirp->icmp; | |
73 | } | |
74 | ||
75 | void icmp_cleanup(Slirp *slirp) | |
76 | { | |
77 | while (slirp->icmp.so_next != &slirp->icmp) { | |
78 | icmp_detach(slirp->icmp.so_next); | |
79 | } | |
80 | } | |
81 | ||
82 | static int icmp_send(struct socket *so, struct mbuf *m, int hlen) | |
83 | { | |
84 | struct ip *ip = mtod(m, struct ip *); | |
85 | struct sockaddr_in addr; | |
86 | ||
87 | so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); | |
88 | if (so->s == -1) { | |
89 | return -1; | |
90 | } | |
91 | ||
92 | so->so_m = m; | |
93 | so->so_faddr = ip->ip_dst; | |
94 | so->so_laddr = ip->ip_src; | |
95 | so->so_iptos = ip->ip_tos; | |
96 | so->so_type = IPPROTO_ICMP; | |
97 | so->so_state = SS_ISFCONNECTED; | |
98 | so->so_expire = curtime + SO_EXPIRE; | |
99 | ||
100 | addr.sin_family = AF_INET; | |
101 | addr.sin_addr = so->so_faddr; | |
102 | ||
103 | insque(so, &so->slirp->icmp); | |
104 | ||
105 | if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, | |
106 | (struct sockaddr *)&addr, sizeof(addr)) == -1) { | |
107 | DEBUG_MISC("icmp_input icmp sendto tx errno = %d-%s", errno, | |
108 | strerror(errno)); | |
109 | icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); | |
110 | icmp_detach(so); | |
111 | } | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
116 | void icmp_detach(struct socket *so) | |
117 | { | |
118 | so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); | |
119 | closesocket(so->s); | |
120 | sofree(so); | |
121 | } | |
122 | ||
123 | /* | |
124 | * Process a received ICMP message. | |
125 | */ | |
126 | void icmp_input(struct mbuf *m, int hlen) | |
127 | { | |
128 | register struct icmp *icp; | |
129 | register struct ip *ip = mtod(m, struct ip *); | |
130 | int icmplen = ip->ip_len; | |
131 | Slirp *slirp = m->slirp; | |
132 | ||
133 | DEBUG_CALL("icmp_input"); | |
134 | DEBUG_ARG("m = %p", m); | |
135 | DEBUG_ARG("m_len = %d", m->m_len); | |
136 | ||
137 | /* | |
138 | * Locate icmp structure in mbuf, and check | |
139 | * that its not corrupted and of at least minimum length. | |
140 | */ | |
141 | if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */ | |
142 | freeit: | |
143 | m_free(m); | |
144 | goto end_error; | |
145 | } | |
146 | ||
147 | m->m_len -= hlen; | |
148 | m->m_data += hlen; | |
149 | icp = mtod(m, struct icmp *); | |
150 | if (cksum(m, icmplen)) { | |
151 | goto freeit; | |
152 | } | |
153 | m->m_len += hlen; | |
154 | m->m_data -= hlen; | |
155 | ||
156 | DEBUG_ARG("icmp_type = %d", icp->icmp_type); | |
157 | switch (icp->icmp_type) { | |
158 | case ICMP_ECHO: | |
159 | ip->ip_len += hlen; /* since ip_input subtracts this */ | |
160 | if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || | |
161 | ip->ip_dst.s_addr == slirp->vnameserver_addr.s_addr) { | |
162 | icmp_reflect(m); | |
163 | } else if (slirp->restricted) { | |
164 | goto freeit; | |
165 | } else { | |
166 | struct socket *so; | |
167 | struct sockaddr_storage addr; | |
168 | so = socreate(slirp); | |
169 | if (icmp_send(so, m, hlen) == 0) { | |
170 | return; | |
171 | } | |
172 | if (udp_attach(so, AF_INET) == -1) { | |
173 | DEBUG_MISC("icmp_input udp_attach errno = %d-%s", errno, | |
174 | strerror(errno)); | |
175 | sofree(so); | |
176 | m_free(m); | |
177 | goto end_error; | |
178 | } | |
179 | so->so_m = m; | |
180 | so->so_ffamily = AF_INET; | |
181 | so->so_faddr = ip->ip_dst; | |
182 | so->so_fport = htons(7); | |
183 | so->so_lfamily = AF_INET; | |
184 | so->so_laddr = ip->ip_src; | |
185 | so->so_lport = htons(9); | |
186 | so->so_iptos = ip->ip_tos; | |
187 | so->so_type = IPPROTO_ICMP; | |
188 | so->so_state = SS_ISFCONNECTED; | |
189 | ||
190 | /* Send the packet */ | |
191 | addr = so->fhost.ss; | |
192 | if (sotranslate_out(so, &addr) < 0) { | |
193 | icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, | |
194 | strerror(errno)); | |
195 | udp_detach(so); | |
196 | return; | |
197 | } | |
198 | ||
199 | if (sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, | |
200 | (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) { | |
201 | DEBUG_MISC("icmp_input udp sendto tx errno = %d-%s", errno, | |
202 | strerror(errno)); | |
203 | icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, | |
204 | strerror(errno)); | |
205 | udp_detach(so); | |
206 | } | |
207 | } /* if ip->ip_dst.s_addr == alias_addr.s_addr */ | |
208 | break; | |
209 | case ICMP_UNREACH: | |
210 | /* XXX? report error? close socket? */ | |
211 | case ICMP_TIMXCEED: | |
212 | case ICMP_PARAMPROB: | |
213 | case ICMP_SOURCEQUENCH: | |
214 | case ICMP_TSTAMP: | |
215 | case ICMP_MASKREQ: | |
216 | case ICMP_REDIRECT: | |
217 | m_free(m); | |
218 | break; | |
219 | ||
220 | default: | |
221 | m_free(m); | |
222 | } /* swith */ | |
223 | ||
224 | end_error: | |
225 | /* m is m_free()'d xor put in a socket xor or given to ip_send */ | |
226 | return; | |
227 | } | |
228 | ||
229 | ||
230 | /* | |
231 | * Send an ICMP message in response to a situation | |
232 | * | |
233 | * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. | |
234 | *MAY send more (we do). MUST NOT change this header information. MUST NOT reply | |
235 | *to a multicast/broadcast IP address. MUST NOT reply to a multicast/broadcast | |
236 | *MAC address. MUST reply to only the first fragment. | |
237 | */ | |
238 | /* | |
239 | * Send ICMP_UNREACH back to the source regarding msrc. | |
240 | * mbuf *msrc is used as a template, but is NOT m_free()'d. | |
241 | * It is reported as the bad ip packet. The header should | |
242 | * be fully correct and in host byte order. | |
243 | * ICMP fragmentation is illegal. All machines must accept 576 bytes in one | |
244 | * packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548 | |
245 | */ | |
246 | ||
247 | #define ICMP_MAXDATALEN (IP_MSS - 28) | |
248 | void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, | |
249 | const char *message) | |
250 | { | |
251 | unsigned hlen, shlen, s_ip_len; | |
252 | register struct ip *ip; | |
253 | register struct icmp *icp; | |
254 | register struct mbuf *m; | |
255 | ||
256 | DEBUG_CALL("icmp_send_error"); | |
257 | DEBUG_ARG("msrc = %p", msrc); | |
258 | DEBUG_ARG("msrc_len = %d", msrc->m_len); | |
259 | ||
260 | if (type != ICMP_UNREACH && type != ICMP_TIMXCEED) | |
261 | goto end_error; | |
262 | ||
263 | /* check msrc */ | |
264 | if (!msrc) | |
265 | goto end_error; | |
266 | ip = mtod(msrc, struct ip *); | |
267 | if (slirp_debug & DBG_MISC) { | |
268 | char bufa[20], bufb[20]; | |
269 | strcpy(bufa, inet_ntoa(ip->ip_src)); | |
270 | strcpy(bufb, inet_ntoa(ip->ip_dst)); | |
271 | DEBUG_MISC(" %.16s to %.16s", bufa, bufb); | |
272 | } | |
273 | if (ip->ip_off & IP_OFFMASK) | |
274 | goto end_error; /* Only reply to fragment 0 */ | |
275 | ||
276 | /* Do not reply to source-only IPs */ | |
277 | if ((ip->ip_src.s_addr & htonl(~(0xf << 28))) == 0) { | |
278 | goto end_error; | |
279 | } | |
280 | ||
281 | shlen = ip->ip_hl << 2; | |
282 | s_ip_len = ip->ip_len; | |
283 | if (ip->ip_p == IPPROTO_ICMP) { | |
284 | icp = (struct icmp *)((char *)ip + shlen); | |
285 | /* | |
286 | * Assume any unknown ICMP type is an error. This isn't | |
287 | * specified by the RFC, but think about it.. | |
288 | */ | |
289 | if (icp->icmp_type > 18 || icmp_flush[icp->icmp_type]) | |
290 | goto end_error; | |
291 | } | |
292 | ||
293 | /* make a copy */ | |
294 | m = m_get(msrc->slirp); | |
295 | if (!m) { | |
296 | goto end_error; | |
297 | } | |
298 | ||
299 | { | |
300 | int new_m_size; | |
301 | new_m_size = | |
302 | sizeof(struct ip) + ICMP_MINLEN + msrc->m_len + ICMP_MAXDATALEN; | |
303 | if (new_m_size > m->m_size) | |
304 | m_inc(m, new_m_size); | |
305 | } | |
306 | memcpy(m->m_data, msrc->m_data, msrc->m_len); | |
307 | m->m_len = msrc->m_len; /* copy msrc to m */ | |
308 | ||
309 | /* make the header of the reply packet */ | |
310 | ip = mtod(m, struct ip *); | |
311 | hlen = sizeof(struct ip); /* no options in reply */ | |
312 | ||
313 | /* fill in icmp */ | |
314 | m->m_data += hlen; | |
315 | m->m_len -= hlen; | |
316 | ||
317 | icp = mtod(m, struct icmp *); | |
318 | ||
319 | if (minsize) | |
320 | s_ip_len = shlen + ICMP_MINLEN; /* return header+8b only */ | |
321 | else if (s_ip_len > ICMP_MAXDATALEN) /* maximum size */ | |
322 | s_ip_len = ICMP_MAXDATALEN; | |
323 | ||
324 | m->m_len = ICMP_MINLEN + s_ip_len; /* 8 bytes ICMP header */ | |
325 | ||
326 | /* min. size = 8+sizeof(struct ip)+8 */ | |
327 | ||
328 | icp->icmp_type = type; | |
329 | icp->icmp_code = code; | |
330 | icp->icmp_id = 0; | |
331 | icp->icmp_seq = 0; | |
332 | ||
333 | memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */ | |
334 | HTONS(icp->icmp_ip.ip_len); | |
335 | HTONS(icp->icmp_ip.ip_id); | |
336 | HTONS(icp->icmp_ip.ip_off); | |
337 | ||
338 | if (message && WITH_ICMP_ERROR_MSG) { /* append message to ICMP packet */ | |
339 | int message_len; | |
340 | char *cpnt; | |
341 | message_len = strlen(message); | |
342 | if (message_len > ICMP_MAXDATALEN) | |
343 | message_len = ICMP_MAXDATALEN; | |
344 | cpnt = (char *)m->m_data + m->m_len; | |
345 | memcpy(cpnt, message, message_len); | |
346 | m->m_len += message_len; | |
347 | } | |
348 | ||
349 | icp->icmp_cksum = 0; | |
350 | icp->icmp_cksum = cksum(m, m->m_len); | |
351 | ||
352 | m->m_data -= hlen; | |
353 | m->m_len += hlen; | |
354 | ||
355 | /* fill in ip */ | |
356 | ip->ip_hl = hlen >> 2; | |
357 | ip->ip_len = m->m_len; | |
358 | ||
359 | ip->ip_tos = ((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */ | |
360 | ||
361 | ip->ip_ttl = MAXTTL; | |
362 | ip->ip_p = IPPROTO_ICMP; | |
363 | ip->ip_dst = ip->ip_src; /* ip addresses */ | |
364 | ip->ip_src = m->slirp->vhost_addr; | |
365 | ||
366 | (void)ip_output((struct socket *)NULL, m); | |
367 | ||
368 | end_error: | |
369 | return; | |
370 | } | |
371 | #undef ICMP_MAXDATALEN | |
372 | ||
373 | /* | |
374 | * Reflect the ip packet back to the source | |
375 | */ | |
376 | void icmp_reflect(struct mbuf *m) | |
377 | { | |
378 | register struct ip *ip = mtod(m, struct ip *); | |
379 | int hlen = ip->ip_hl << 2; | |
380 | int optlen = hlen - sizeof(struct ip); | |
381 | register struct icmp *icp; | |
382 | ||
383 | /* | |
384 | * Send an icmp packet back to the ip level, | |
385 | * after supplying a checksum. | |
386 | */ | |
387 | m->m_data += hlen; | |
388 | m->m_len -= hlen; | |
389 | icp = mtod(m, struct icmp *); | |
390 | ||
391 | icp->icmp_type = ICMP_ECHOREPLY; | |
392 | icp->icmp_cksum = 0; | |
393 | icp->icmp_cksum = cksum(m, ip->ip_len - hlen); | |
394 | ||
395 | m->m_data -= hlen; | |
396 | m->m_len += hlen; | |
397 | ||
398 | /* fill in ip */ | |
399 | if (optlen > 0) { | |
400 | /* | |
401 | * Strip out original options by copying rest of first | |
402 | * mbuf's data back, and adjust the IP length. | |
403 | */ | |
404 | memmove((char *)(ip + 1), (char *)ip + hlen, | |
405 | (unsigned)(m->m_len - hlen)); | |
406 | hlen -= optlen; | |
407 | ip->ip_hl = hlen >> 2; | |
408 | ip->ip_len -= optlen; | |
409 | m->m_len -= optlen; | |
410 | } | |
411 | ||
412 | ip->ip_ttl = MAXTTL; | |
413 | { /* swap */ | |
414 | struct in_addr icmp_dst; | |
415 | icmp_dst = ip->ip_dst; | |
416 | ip->ip_dst = ip->ip_src; | |
417 | ip->ip_src = icmp_dst; | |
418 | } | |
419 | ||
420 | (void)ip_output((struct socket *)NULL, m); | |
421 | } | |
422 | ||
423 | void icmp_receive(struct socket *so) | |
424 | { | |
425 | struct mbuf *m = so->so_m; | |
426 | struct ip *ip = mtod(m, struct ip *); | |
427 | int hlen = ip->ip_hl << 2; | |
428 | uint8_t error_code; | |
429 | struct icmp *icp; | |
430 | int id, len; | |
431 | ||
432 | m->m_data += hlen; | |
433 | m->m_len -= hlen; | |
434 | icp = mtod(m, struct icmp *); | |
435 | ||
436 | id = icp->icmp_id; | |
437 | len = recv(so->s, icp, M_ROOM(m), 0); | |
438 | /* | |
439 | * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent | |
440 | * between host OSes. On Linux, only the ICMP header and payload is | |
441 | * included. On macOS/Darwin, the socket acts like a raw socket and | |
442 | * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP | |
443 | * sockets aren't supported at all, so we treat them like raw sockets. It | |
444 | * isn't possible to detect this difference at runtime, so we must use an | |
445 | * #ifdef to determine if we need to remove the IP header. | |
446 | */ | |
447 | #ifdef CONFIG_BSD | |
448 | if (len >= sizeof(struct ip)) { | |
449 | struct ip *inner_ip = mtod(m, struct ip *); | |
450 | int inner_hlen = inner_ip->ip_hl << 2; | |
451 | if (inner_hlen > len) { | |
452 | len = -1; | |
453 | errno = -EINVAL; | |
454 | } else { | |
455 | len -= inner_hlen; | |
456 | memmove(icp, (unsigned char *)icp + inner_hlen, len); | |
457 | } | |
458 | } else { | |
459 | len = -1; | |
460 | errno = -EINVAL; | |
461 | } | |
462 | #endif | |
463 | icp->icmp_id = id; | |
464 | ||
465 | m->m_data -= hlen; | |
466 | m->m_len += hlen; | |
467 | ||
468 | if (len == -1 || len == 0) { | |
469 | if (errno == ENETUNREACH) { | |
470 | error_code = ICMP_UNREACH_NET; | |
471 | } else { | |
472 | error_code = ICMP_UNREACH_HOST; | |
473 | } | |
474 | DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, strerror(errno)); | |
475 | icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); | |
476 | } else { | |
477 | icmp_reflect(so->so_m); | |
478 | so->so_m = NULL; /* Don't m_free() it again! */ | |
479 | } | |
480 | icmp_detach(so); | |
481 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93 | |
30 | * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp | |
31 | */ | |
32 | ||
33 | #ifndef NETINET_IP_ICMP_H | |
34 | #define NETINET_IP_ICMP_H | |
35 | ||
36 | /* | |
37 | * Interface Control Message Protocol Definitions. | |
38 | * Per RFC 792, September 1981. | |
39 | */ | |
40 | ||
41 | typedef uint32_t n_time; | |
42 | ||
43 | /* | |
44 | * Structure of an icmp header. | |
45 | */ | |
46 | struct icmp { | |
47 | uint8_t icmp_type; /* type of message, see below */ | |
48 | uint8_t icmp_code; /* type sub code */ | |
49 | uint16_t icmp_cksum; /* ones complement cksum of struct */ | |
50 | union { | |
51 | uint8_t ih_pptr; /* ICMP_PARAMPROB */ | |
52 | struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ | |
53 | struct ih_idseq { | |
54 | uint16_t icd_id; | |
55 | uint16_t icd_seq; | |
56 | } ih_idseq; | |
57 | int ih_void; | |
58 | ||
59 | /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ | |
60 | struct ih_pmtu { | |
61 | uint16_t ipm_void; | |
62 | uint16_t ipm_nextmtu; | |
63 | } ih_pmtu; | |
64 | } icmp_hun; | |
65 | #define icmp_pptr icmp_hun.ih_pptr | |
66 | #define icmp_gwaddr icmp_hun.ih_gwaddr | |
67 | #define icmp_id icmp_hun.ih_idseq.icd_id | |
68 | #define icmp_seq icmp_hun.ih_idseq.icd_seq | |
69 | #define icmp_void icmp_hun.ih_void | |
70 | #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void | |
71 | #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu | |
72 | union { | |
73 | struct id_ts { | |
74 | n_time its_otime; | |
75 | n_time its_rtime; | |
76 | n_time its_ttime; | |
77 | } id_ts; | |
78 | struct id_ip { | |
79 | struct ip idi_ip; | |
80 | /* options and then 64 bits of data */ | |
81 | } id_ip; | |
82 | uint32_t id_mask; | |
83 | char id_data[1]; | |
84 | } icmp_dun; | |
85 | #define icmp_otime icmp_dun.id_ts.its_otime | |
86 | #define icmp_rtime icmp_dun.id_ts.its_rtime | |
87 | #define icmp_ttime icmp_dun.id_ts.its_ttime | |
88 | #define icmp_ip icmp_dun.id_ip.idi_ip | |
89 | #define icmp_mask icmp_dun.id_mask | |
90 | #define icmp_data icmp_dun.id_data | |
91 | }; | |
92 | ||
93 | /* | |
94 | * Lower bounds on packet lengths for various types. | |
95 | * For the error advice packets must first ensure that the | |
96 | * packet is large enough to contain the returned ip header. | |
97 | * Only then can we do the check to see if 64 bits of packet | |
98 | * data have been returned, since we need to check the returned | |
99 | * ip header length. | |
100 | */ | |
101 | #define ICMP_MINLEN 8 /* abs minimum */ | |
102 | #define ICMP_TSLEN (8 + 3 * sizeof(n_time)) /* timestamp */ | |
103 | #define ICMP_MASKLEN 12 /* address mask */ | |
104 | #define ICMP_ADVLENMIN (8 + sizeof(struct ip) + 8) /* min */ | |
105 | #define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8) | |
106 | /* N.B.: must separately check that ip_hl >= 5 */ | |
107 | ||
108 | /* | |
109 | * Definition of type and code field values. | |
110 | */ | |
111 | #define ICMP_ECHOREPLY 0 /* echo reply */ | |
112 | #define ICMP_UNREACH 3 /* dest unreachable, codes: */ | |
113 | #define ICMP_UNREACH_NET 0 /* bad net */ | |
114 | #define ICMP_UNREACH_HOST 1 /* bad host */ | |
115 | #define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */ | |
116 | #define ICMP_UNREACH_PORT 3 /* bad port */ | |
117 | #define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */ | |
118 | #define ICMP_UNREACH_SRCFAIL 5 /* src route failed */ | |
119 | #define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */ | |
120 | #define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */ | |
121 | #define ICMP_UNREACH_ISOLATED 8 /* src host isolated */ | |
122 | #define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */ | |
123 | #define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */ | |
124 | #define ICMP_UNREACH_TOSNET 11 /* bad tos for net */ | |
125 | #define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */ | |
126 | #define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */ | |
127 | #define ICMP_REDIRECT 5 /* shorter route, codes: */ | |
128 | #define ICMP_REDIRECT_NET 0 /* for network */ | |
129 | #define ICMP_REDIRECT_HOST 1 /* for host */ | |
130 | #define ICMP_REDIRECT_TOSNET 2 /* for tos and net */ | |
131 | #define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */ | |
132 | #define ICMP_ECHO 8 /* echo service */ | |
133 | #define ICMP_ROUTERADVERT 9 /* router advertisement */ | |
134 | #define ICMP_ROUTERSOLICIT 10 /* router solicitation */ | |
135 | #define ICMP_TIMXCEED 11 /* time exceeded, code: */ | |
136 | #define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */ | |
137 | #define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */ | |
138 | #define ICMP_PARAMPROB 12 /* ip header bad */ | |
139 | #define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */ | |
140 | #define ICMP_TSTAMP 13 /* timestamp request */ | |
141 | #define ICMP_TSTAMPREPLY 14 /* timestamp reply */ | |
142 | #define ICMP_IREQ 15 /* information request */ | |
143 | #define ICMP_IREQREPLY 16 /* information reply */ | |
144 | #define ICMP_MASKREQ 17 /* address mask request */ | |
145 | #define ICMP_MASKREPLY 18 /* address mask reply */ | |
146 | ||
147 | #define ICMP_MAXTYPE 18 | |
148 | ||
149 | #define ICMP_INFOTYPE(type) \ | |
150 | ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \ | |
151 | (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \ | |
152 | (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \ | |
153 | (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \ | |
154 | (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY) | |
155 | ||
156 | void icmp_init(Slirp *slirp); | |
157 | void icmp_cleanup(Slirp *slirp); | |
158 | void icmp_input(struct mbuf *, int); | |
159 | void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, | |
160 | const char *message); | |
161 | void icmp_reflect(struct mbuf *); | |
162 | void icmp_receive(struct socket *so); | |
163 | void icmp_detach(struct socket *so); | |
164 | ||
165 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 | |
30 | * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP are | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | */ | |
37 | ||
38 | #include "slirp.h" | |
39 | #include "ip_icmp.h" | |
40 | ||
41 | static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp); | |
42 | static void ip_freef(Slirp *slirp, struct ipq *fp); | |
43 | static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev); | |
44 | static void ip_deq(register struct ipasfrag *p); | |
45 | ||
46 | /* | |
47 | * IP initialization: fill in IP protocol switch table. | |
48 | * All protocols not implemented in kernel go to raw IP protocol handler. | |
49 | */ | |
50 | void ip_init(Slirp *slirp) | |
51 | { | |
52 | slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link; | |
53 | udp_init(slirp); | |
54 | tcp_init(slirp); | |
55 | icmp_init(slirp); | |
56 | } | |
57 | ||
58 | void ip_cleanup(Slirp *slirp) | |
59 | { | |
60 | udp_cleanup(slirp); | |
61 | tcp_cleanup(slirp); | |
62 | icmp_cleanup(slirp); | |
63 | } | |
64 | ||
65 | /* | |
66 | * Ip input routine. Checksum and byte swap header. If fragmented | |
67 | * try to reassemble. Process options. Pass to next level. | |
68 | */ | |
69 | void ip_input(struct mbuf *m) | |
70 | { | |
71 | Slirp *slirp = m->slirp; | |
72 | register struct ip *ip; | |
73 | int hlen; | |
74 | ||
75 | if (!slirp->in_enabled) { | |
76 | goto bad; | |
77 | } | |
78 | ||
79 | DEBUG_CALL("ip_input"); | |
80 | DEBUG_ARG("m = %p", m); | |
81 | DEBUG_ARG("m_len = %d", m->m_len); | |
82 | ||
83 | if (m->m_len < sizeof(struct ip)) { | |
84 | goto bad; | |
85 | } | |
86 | ||
87 | ip = mtod(m, struct ip *); | |
88 | ||
89 | if (ip->ip_v != IPVERSION) { | |
90 | goto bad; | |
91 | } | |
92 | ||
93 | hlen = ip->ip_hl << 2; | |
94 | if (hlen < sizeof(struct ip) || hlen > m->m_len) { /* min header length */ | |
95 | goto bad; /* or packet too short */ | |
96 | } | |
97 | ||
98 | /* keep ip header intact for ICMP reply | |
99 | * ip->ip_sum = cksum(m, hlen); | |
100 | * if (ip->ip_sum) { | |
101 | */ | |
102 | if (cksum(m, hlen)) { | |
103 | goto bad; | |
104 | } | |
105 | ||
106 | /* | |
107 | * Convert fields to host representation. | |
108 | */ | |
109 | NTOHS(ip->ip_len); | |
110 | if (ip->ip_len < hlen) { | |
111 | goto bad; | |
112 | } | |
113 | NTOHS(ip->ip_id); | |
114 | NTOHS(ip->ip_off); | |
115 | ||
116 | /* | |
117 | * Check that the amount of data in the buffers | |
118 | * is as at least much as the IP header would have us expect. | |
119 | * Trim mbufs if longer than we expect. | |
120 | * Drop packet if shorter than we expect. | |
121 | */ | |
122 | if (m->m_len < ip->ip_len) { | |
123 | goto bad; | |
124 | } | |
125 | ||
126 | /* Should drop packet if mbuf too long? hmmm... */ | |
127 | if (m->m_len > ip->ip_len) | |
128 | m_adj(m, ip->ip_len - m->m_len); | |
129 | ||
130 | /* check ip_ttl for a correct ICMP reply */ | |
131 | if (ip->ip_ttl == 0) { | |
132 | icmp_send_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl"); | |
133 | goto bad; | |
134 | } | |
135 | ||
136 | /* | |
137 | * If offset or IP_MF are set, must reassemble. | |
138 | * Otherwise, nothing need be done. | |
139 | * (We could look in the reassembly queue to see | |
140 | * if the packet was previously fragmented, | |
141 | * but it's not worth the time; just let them time out.) | |
142 | * | |
143 | * XXX This should fail, don't fragment yet | |
144 | */ | |
145 | if (ip->ip_off & ~IP_DF) { | |
146 | register struct ipq *fp; | |
147 | struct qlink *l; | |
148 | /* | |
149 | * Look for queue of fragments | |
150 | * of this datagram. | |
151 | */ | |
152 | for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link; | |
153 | l = l->next) { | |
154 | fp = container_of(l, struct ipq, ip_link); | |
155 | if (ip->ip_id == fp->ipq_id && | |
156 | ip->ip_src.s_addr == fp->ipq_src.s_addr && | |
157 | ip->ip_dst.s_addr == fp->ipq_dst.s_addr && | |
158 | ip->ip_p == fp->ipq_p) | |
159 | goto found; | |
160 | } | |
161 | fp = NULL; | |
162 | found: | |
163 | ||
164 | /* | |
165 | * Adjust ip_len to not reflect header, | |
166 | * set ip_mff if more fragments are expected, | |
167 | * convert offset of this to bytes. | |
168 | */ | |
169 | ip->ip_len -= hlen; | |
170 | if (ip->ip_off & IP_MF) | |
171 | ip->ip_tos |= 1; | |
172 | else | |
173 | ip->ip_tos &= ~1; | |
174 | ||
175 | ip->ip_off <<= 3; | |
176 | ||
177 | /* | |
178 | * If datagram marked as having more fragments | |
179 | * or if this is not the first fragment, | |
180 | * attempt reassembly; if it succeeds, proceed. | |
181 | */ | |
182 | if (ip->ip_tos & 1 || ip->ip_off) { | |
183 | ip = ip_reass(slirp, ip, fp); | |
184 | if (ip == NULL) | |
185 | return; | |
186 | m = dtom(slirp, ip); | |
187 | } else if (fp) | |
188 | ip_freef(slirp, fp); | |
189 | ||
190 | } else | |
191 | ip->ip_len -= hlen; | |
192 | ||
193 | /* | |
194 | * Switch out to protocol's input routine. | |
195 | */ | |
196 | switch (ip->ip_p) { | |
197 | case IPPROTO_TCP: | |
198 | tcp_input(m, hlen, (struct socket *)NULL, AF_INET); | |
199 | break; | |
200 | case IPPROTO_UDP: | |
201 | udp_input(m, hlen); | |
202 | break; | |
203 | case IPPROTO_ICMP: | |
204 | icmp_input(m, hlen); | |
205 | break; | |
206 | default: | |
207 | m_free(m); | |
208 | } | |
209 | return; | |
210 | bad: | |
211 | m_free(m); | |
212 | } | |
213 | ||
214 | #define iptofrag(P) ((struct ipasfrag *)(((char *)(P)) - sizeof(struct qlink))) | |
215 | #define fragtoip(P) ((struct ip *)(((char *)(P)) + sizeof(struct qlink))) | |
216 | /* | |
217 | * Take incoming datagram fragment and try to | |
218 | * reassemble it into whole datagram. If a chain for | |
219 | * reassembly of this datagram already exists, then it | |
220 | * is given as fp; otherwise have to make a chain. | |
221 | */ | |
222 | static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp) | |
223 | { | |
224 | register struct mbuf *m = dtom(slirp, ip); | |
225 | register struct ipasfrag *q; | |
226 | int hlen = ip->ip_hl << 2; | |
227 | int i, next; | |
228 | ||
229 | DEBUG_CALL("ip_reass"); | |
230 | DEBUG_ARG("ip = %p", ip); | |
231 | DEBUG_ARG("fp = %p", fp); | |
232 | DEBUG_ARG("m = %p", m); | |
233 | ||
234 | /* | |
235 | * Presence of header sizes in mbufs | |
236 | * would confuse code below. | |
237 | * Fragment m_data is concatenated. | |
238 | */ | |
239 | m->m_data += hlen; | |
240 | m->m_len -= hlen; | |
241 | ||
242 | /* | |
243 | * If first fragment to arrive, create a reassembly queue. | |
244 | */ | |
245 | if (fp == NULL) { | |
246 | struct mbuf *t = m_get(slirp); | |
247 | ||
248 | if (t == NULL) { | |
249 | goto dropfrag; | |
250 | } | |
251 | fp = mtod(t, struct ipq *); | |
252 | insque(&fp->ip_link, &slirp->ipq.ip_link); | |
253 | fp->ipq_ttl = IPFRAGTTL; | |
254 | fp->ipq_p = ip->ip_p; | |
255 | fp->ipq_id = ip->ip_id; | |
256 | fp->frag_link.next = fp->frag_link.prev = &fp->frag_link; | |
257 | fp->ipq_src = ip->ip_src; | |
258 | fp->ipq_dst = ip->ip_dst; | |
259 | q = (struct ipasfrag *)fp; | |
260 | goto insert; | |
261 | } | |
262 | ||
263 | /* | |
264 | * Find a segment which begins after this one does. | |
265 | */ | |
266 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; | |
267 | q = q->ipf_next) | |
268 | if (q->ipf_off > ip->ip_off) | |
269 | break; | |
270 | ||
271 | /* | |
272 | * If there is a preceding segment, it may provide some of | |
273 | * our data already. If so, drop the data from the incoming | |
274 | * segment. If it provides all of our data, drop us. | |
275 | */ | |
276 | if (q->ipf_prev != &fp->frag_link) { | |
277 | struct ipasfrag *pq = q->ipf_prev; | |
278 | i = pq->ipf_off + pq->ipf_len - ip->ip_off; | |
279 | if (i > 0) { | |
280 | if (i >= ip->ip_len) | |
281 | goto dropfrag; | |
282 | m_adj(dtom(slirp, ip), i); | |
283 | ip->ip_off += i; | |
284 | ip->ip_len -= i; | |
285 | } | |
286 | } | |
287 | ||
288 | /* | |
289 | * While we overlap succeeding segments trim them or, | |
290 | * if they are completely covered, dequeue them. | |
291 | */ | |
292 | while (q != (struct ipasfrag *)&fp->frag_link && | |
293 | ip->ip_off + ip->ip_len > q->ipf_off) { | |
294 | i = (ip->ip_off + ip->ip_len) - q->ipf_off; | |
295 | if (i < q->ipf_len) { | |
296 | q->ipf_len -= i; | |
297 | q->ipf_off += i; | |
298 | m_adj(dtom(slirp, q), i); | |
299 | break; | |
300 | } | |
301 | q = q->ipf_next; | |
302 | m_free(dtom(slirp, q->ipf_prev)); | |
303 | ip_deq(q->ipf_prev); | |
304 | } | |
305 | ||
306 | insert: | |
307 | /* | |
308 | * Stick new segment in its place; | |
309 | * check for complete reassembly. | |
310 | */ | |
311 | ip_enq(iptofrag(ip), q->ipf_prev); | |
312 | next = 0; | |
313 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; | |
314 | q = q->ipf_next) { | |
315 | if (q->ipf_off != next) | |
316 | return NULL; | |
317 | next += q->ipf_len; | |
318 | } | |
319 | if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1) | |
320 | return NULL; | |
321 | ||
322 | /* | |
323 | * Reassembly is complete; concatenate fragments. | |
324 | */ | |
325 | q = fp->frag_link.next; | |
326 | m = dtom(slirp, q); | |
327 | ||
328 | int was_ext = m->m_flags & M_EXT; | |
329 | ||
330 | q = (struct ipasfrag *)q->ipf_next; | |
331 | while (q != (struct ipasfrag *)&fp->frag_link) { | |
332 | struct mbuf *t = dtom(slirp, q); | |
333 | q = (struct ipasfrag *)q->ipf_next; | |
334 | m_cat(m, t); | |
335 | } | |
336 | ||
337 | /* | |
338 | * Create header for new ip packet by | |
339 | * modifying header of first packet; | |
340 | * dequeue and discard fragment reassembly header. | |
341 | * Make header visible. | |
342 | */ | |
343 | q = fp->frag_link.next; | |
344 | ||
345 | /* | |
346 | * If the fragments concatenated to an mbuf that's bigger than the total | |
347 | * size of the fragment and the mbuf was not already using an m_ext buffer, | |
348 | * then an m_ext buffer was alloced. But fp->ipq_next points to the old | |
349 | * buffer (in the mbuf), so we must point ip into the new buffer. | |
350 | */ | |
351 | if (!was_ext && m->m_flags & M_EXT) { | |
352 | int delta = (char *)q - m->m_dat; | |
353 | q = (struct ipasfrag *)(m->m_ext + delta); | |
354 | } | |
355 | ||
356 | ip = fragtoip(q); | |
357 | ip->ip_len = next; | |
358 | ip->ip_tos &= ~1; | |
359 | ip->ip_src = fp->ipq_src; | |
360 | ip->ip_dst = fp->ipq_dst; | |
361 | remque(&fp->ip_link); | |
362 | (void)m_free(dtom(slirp, fp)); | |
363 | m->m_len += (ip->ip_hl << 2); | |
364 | m->m_data -= (ip->ip_hl << 2); | |
365 | ||
366 | return ip; | |
367 | ||
368 | dropfrag: | |
369 | m_free(m); | |
370 | return NULL; | |
371 | } | |
372 | ||
373 | /* | |
374 | * Free a fragment reassembly header and all | |
375 | * associated datagrams. | |
376 | */ | |
377 | static void ip_freef(Slirp *slirp, struct ipq *fp) | |
378 | { | |
379 | register struct ipasfrag *q, *p; | |
380 | ||
381 | for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; | |
382 | q = p) { | |
383 | p = q->ipf_next; | |
384 | ip_deq(q); | |
385 | m_free(dtom(slirp, q)); | |
386 | } | |
387 | remque(&fp->ip_link); | |
388 | (void)m_free(dtom(slirp, fp)); | |
389 | } | |
390 | ||
391 | /* | |
392 | * Put an ip fragment on a reassembly chain. | |
393 | * Like insque, but pointers in middle of structure. | |
394 | */ | |
395 | static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev) | |
396 | { | |
397 | DEBUG_CALL("ip_enq"); | |
398 | DEBUG_ARG("prev = %p", prev); | |
399 | p->ipf_prev = prev; | |
400 | p->ipf_next = prev->ipf_next; | |
401 | ((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p; | |
402 | prev->ipf_next = p; | |
403 | } | |
404 | ||
405 | /* | |
406 | * To ip_enq as remque is to insque. | |
407 | */ | |
408 | static void ip_deq(register struct ipasfrag *p) | |
409 | { | |
410 | ((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next; | |
411 | ((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev; | |
412 | } | |
413 | ||
414 | /* | |
415 | * IP timer processing; | |
416 | * if a timer expires on a reassembly | |
417 | * queue, discard it. | |
418 | */ | |
419 | void ip_slowtimo(Slirp *slirp) | |
420 | { | |
421 | struct qlink *l; | |
422 | ||
423 | DEBUG_CALL("ip_slowtimo"); | |
424 | ||
425 | l = slirp->ipq.ip_link.next; | |
426 | ||
427 | if (l == NULL) | |
428 | return; | |
429 | ||
430 | while (l != &slirp->ipq.ip_link) { | |
431 | struct ipq *fp = container_of(l, struct ipq, ip_link); | |
432 | l = l->next; | |
433 | if (--fp->ipq_ttl == 0) { | |
434 | ip_freef(slirp, fp); | |
435 | } | |
436 | } | |
437 | } | |
438 | ||
439 | /* | |
440 | * Strip out IP options, at higher | |
441 | * level protocol in the kernel. | |
442 | * Second argument is buffer to which options | |
443 | * will be moved, and return value is their length. | |
444 | * (XXX) should be deleted; last arg currently ignored. | |
445 | */ | |
446 | void ip_stripoptions(register struct mbuf *m, struct mbuf *mopt) | |
447 | { | |
448 | register int i; | |
449 | struct ip *ip = mtod(m, struct ip *); | |
450 | register char *opts; | |
451 | int olen; | |
452 | ||
453 | olen = (ip->ip_hl << 2) - sizeof(struct ip); | |
454 | opts = (char *)(ip + 1); | |
455 | i = m->m_len - (sizeof(struct ip) + olen); | |
456 | memcpy(opts, opts + olen, (unsigned)i); | |
457 | m->m_len -= olen; | |
458 | ||
459 | ip->ip_hl = sizeof(struct ip) >> 2; | |
460 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 | |
30 | * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP are | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | */ | |
37 | ||
38 | #include "slirp.h" | |
39 | ||
40 | /* Number of packets queued before we start sending | |
41 | * (to prevent allocing too many mbufs) */ | |
42 | #define IF_THRESH 10 | |
43 | ||
44 | /* | |
45 | * IP output. The packet in mbuf chain m contains a skeletal IP | |
46 | * header (with len, off, ttl, proto, tos, src, dst). | |
47 | * The mbuf chain containing the packet will be freed. | |
48 | * The mbuf opt, if present, will not be freed. | |
49 | */ | |
50 | int ip_output(struct socket *so, struct mbuf *m0) | |
51 | { | |
52 | Slirp *slirp = m0->slirp; | |
53 | register struct ip *ip; | |
54 | register struct mbuf *m = m0; | |
55 | register int hlen = sizeof(struct ip); | |
56 | int len, off, error = 0; | |
57 | ||
58 | DEBUG_CALL("ip_output"); | |
59 | DEBUG_ARG("so = %p", so); | |
60 | DEBUG_ARG("m0 = %p", m0); | |
61 | ||
62 | ip = mtod(m, struct ip *); | |
63 | /* | |
64 | * Fill in IP header. | |
65 | */ | |
66 | ip->ip_v = IPVERSION; | |
67 | ip->ip_off &= IP_DF; | |
68 | ip->ip_id = htons(slirp->ip_id++); | |
69 | ip->ip_hl = hlen >> 2; | |
70 | ||
71 | /* | |
72 | * If small enough for interface, can just send directly. | |
73 | */ | |
74 | if ((uint16_t)ip->ip_len <= slirp->if_mtu) { | |
75 | ip->ip_len = htons((uint16_t)ip->ip_len); | |
76 | ip->ip_off = htons((uint16_t)ip->ip_off); | |
77 | ip->ip_sum = 0; | |
78 | ip->ip_sum = cksum(m, hlen); | |
79 | ||
80 | if_output(so, m); | |
81 | goto done; | |
82 | } | |
83 | ||
84 | /* | |
85 | * Too large for interface; fragment if possible. | |
86 | * Must be able to put at least 8 bytes per fragment. | |
87 | */ | |
88 | if (ip->ip_off & IP_DF) { | |
89 | error = -1; | |
90 | goto bad; | |
91 | } | |
92 | ||
93 | len = (slirp->if_mtu - hlen) & ~7; /* ip databytes per packet */ | |
94 | if (len < 8) { | |
95 | error = -1; | |
96 | goto bad; | |
97 | } | |
98 | ||
99 | { | |
100 | int mhlen, firstlen = len; | |
101 | struct mbuf **mnext = &m->m_nextpkt; | |
102 | ||
103 | /* | |
104 | * Loop through length of segment after first fragment, | |
105 | * make new header and copy data of each part and link onto chain. | |
106 | */ | |
107 | m0 = m; | |
108 | mhlen = sizeof(struct ip); | |
109 | for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) { | |
110 | register struct ip *mhip; | |
111 | m = m_get(slirp); | |
112 | if (m == NULL) { | |
113 | error = -1; | |
114 | goto sendorfree; | |
115 | } | |
116 | m->m_data += IF_MAXLINKHDR; | |
117 | mhip = mtod(m, struct ip *); | |
118 | *mhip = *ip; | |
119 | ||
120 | m->m_len = mhlen; | |
121 | mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); | |
122 | if (ip->ip_off & IP_MF) | |
123 | mhip->ip_off |= IP_MF; | |
124 | if (off + len >= (uint16_t)ip->ip_len) | |
125 | len = (uint16_t)ip->ip_len - off; | |
126 | else | |
127 | mhip->ip_off |= IP_MF; | |
128 | mhip->ip_len = htons((uint16_t)(len + mhlen)); | |
129 | ||
130 | if (m_copy(m, m0, off, len) < 0) { | |
131 | error = -1; | |
132 | goto sendorfree; | |
133 | } | |
134 | ||
135 | mhip->ip_off = htons((uint16_t)mhip->ip_off); | |
136 | mhip->ip_sum = 0; | |
137 | mhip->ip_sum = cksum(m, mhlen); | |
138 | *mnext = m; | |
139 | mnext = &m->m_nextpkt; | |
140 | } | |
141 | /* | |
142 | * Update first fragment by trimming what's been copied out | |
143 | * and updating header, then send each fragment (in order). | |
144 | */ | |
145 | m = m0; | |
146 | m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len); | |
147 | ip->ip_len = htons((uint16_t)m->m_len); | |
148 | ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF)); | |
149 | ip->ip_sum = 0; | |
150 | ip->ip_sum = cksum(m, hlen); | |
151 | sendorfree: | |
152 | for (m = m0; m; m = m0) { | |
153 | m0 = m->m_nextpkt; | |
154 | m->m_nextpkt = NULL; | |
155 | if (error == 0) | |
156 | if_output(so, m); | |
157 | else | |
158 | m_free(m); | |
159 | } | |
160 | } | |
161 | ||
162 | done: | |
163 | return (error); | |
164 | ||
165 | bad: | |
166 | m_free(m0); | |
167 | goto done; | |
168 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | #ifndef LIBSLIRP_VERSION_H_ | |
2 | #define LIBSLIRP_VERSION_H_ | |
3 | ||
4 | #ifdef __cplusplus | |
5 | extern "C" { | |
6 | #endif | |
7 | ||
8 | #define SLIRP_MAJOR_VERSION 4 | |
9 | #define SLIRP_MINOR_VERSION 0 | |
10 | #define SLIRP_MICRO_VERSION 0 | |
11 | ||
12 | #define SLIRP_CHECK_VERSION(major,minor,micro) \ | |
13 | (SLIRP_MAJOR_VERSION > (major) || \ | |
14 | (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION > (minor)) || \ | |
15 | (SLIRP_MAJOR_VERSION == (major) && SLIRP_MINOR_VERSION == (minor) && \ | |
16 | SLIRP_MICRO_VERSION >= (micro))) | |
17 | ||
18 | #ifdef __cplusplus | |
19 | } /* extern "C" */ | |
20 | #endif | |
21 | ||
22 | #endif /* LIBSLIRP_VERSION_H_ */ |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | #ifndef LIBSLIRP_H | |
2 | #define LIBSLIRP_H | |
3 | ||
4 | #include <stdint.h> | |
5 | #include <stdbool.h> | |
6 | #include <sys/types.h> | |
7 | ||
8 | #ifdef _WIN32 | |
9 | #include <winsock2.h> | |
10 | #include <in6addr.h> | |
11 | #else | |
12 | #include <netinet/in.h> | |
13 | #include <arpa/inet.h> | |
14 | #endif | |
15 | ||
16 | #include "libslirp-version.h" | |
17 | ||
18 | #ifdef __cplusplus | |
19 | extern "C" { | |
20 | #endif | |
21 | ||
22 | typedef struct Slirp Slirp; | |
23 | ||
24 | enum { | |
25 | SLIRP_POLL_IN = 1 << 0, | |
26 | SLIRP_POLL_OUT = 1 << 1, | |
27 | SLIRP_POLL_PRI = 1 << 2, | |
28 | SLIRP_POLL_ERR = 1 << 3, | |
29 | SLIRP_POLL_HUP = 1 << 4, | |
30 | }; | |
31 | ||
32 | typedef ssize_t (*SlirpReadCb)(void *buf, size_t len, void *opaque); | |
33 | typedef ssize_t (*SlirpWriteCb)(const void *buf, size_t len, void *opaque); | |
34 | typedef void (*SlirpTimerCb)(void *opaque); | |
35 | typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque); | |
36 | typedef int (*SlirpGetREventsCb)(int idx, void *opaque); | |
37 | ||
38 | /* | |
39 | * Callbacks from slirp | |
40 | */ | |
41 | typedef struct SlirpCb { | |
42 | /* | |
43 | * Send an ethernet frame to the guest network. The opaque | |
44 | * parameter is the one given to slirp_init(). The function | |
45 | * doesn't need to send all the data and may return <len (no | |
46 | * buffering is done on libslirp side, so the data will be dropped | |
47 | * in this case). <0 reports an IO error. | |
48 | */ | |
49 | SlirpWriteCb send_packet; | |
50 | /* Print a message for an error due to guest misbehavior. */ | |
51 | void (*guest_error)(const char *msg, void *opaque); | |
52 | /* Return the virtual clock value in nanoseconds */ | |
53 | int64_t (*clock_get_ns)(void *opaque); | |
54 | /* Create a new timer with the given callback and opaque data */ | |
55 | void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque); | |
56 | /* Remove and free a timer */ | |
57 | void (*timer_free)(void *timer, void *opaque); | |
58 | /* Modify a timer to expire at @expire_time */ | |
59 | void (*timer_mod)(void *timer, int64_t expire_time, void *opaque); | |
60 | /* Register a fd for future polling */ | |
61 | void (*register_poll_fd)(int fd, void *opaque); | |
62 | /* Unregister a fd */ | |
63 | void (*unregister_poll_fd)(int fd, void *opaque); | |
64 | /* Kick the io-thread, to signal that new events may be processed */ | |
65 | void (*notify)(void *opaque); | |
66 | } SlirpCb; | |
67 | ||
68 | #define SLIRP_CONFIG_VERSION_MIN 1 | |
69 | #define SLIRP_CONFIG_VERSION_MAX 1 | |
70 | ||
71 | typedef struct SlirpConfig { | |
72 | /* Version must be provided */ | |
73 | uint32_t version; | |
74 | /* | |
75 | * Fields introduced in SlirpConfig version 1 begin | |
76 | */ | |
77 | int restricted; | |
78 | bool in_enabled; | |
79 | struct in_addr vnetwork; | |
80 | struct in_addr vnetmask; | |
81 | struct in_addr vhost; | |
82 | bool in6_enabled; | |
83 | struct in6_addr vprefix_addr6; | |
84 | uint8_t vprefix_len; | |
85 | struct in6_addr vhost6; | |
86 | const char *vhostname; | |
87 | const char *tftp_server_name; | |
88 | const char *tftp_path; | |
89 | const char *bootfile; | |
90 | struct in_addr vdhcp_start; | |
91 | struct in_addr vnameserver; | |
92 | struct in6_addr vnameserver6; | |
93 | const char **vdnssearch; | |
94 | const char *vdomainname; | |
95 | /* Default: IF_MTU_DEFAULT */ | |
96 | size_t if_mtu; | |
97 | /* Default: IF_MRU_DEFAULT */ | |
98 | size_t if_mru; | |
99 | /* Prohibit connecting to 127.0.0.1:* */ | |
100 | bool disable_host_loopback; | |
101 | /* | |
102 | * Enable emulation code (*warning*: this code isn't safe, it is not | |
103 | * recommended to enable it) | |
104 | */ | |
105 | bool enable_emu; | |
106 | /* | |
107 | * Fields introduced in SlirpConfig version 2 begin | |
108 | */ | |
109 | } SlirpConfig; | |
110 | ||
111 | Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, | |
112 | void *opaque); | |
113 | /* slirp_init is deprecated in favor of slirp_new */ | |
114 | Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, | |
115 | struct in_addr vnetmask, struct in_addr vhost, | |
116 | bool in6_enabled, struct in6_addr vprefix_addr6, | |
117 | uint8_t vprefix_len, struct in6_addr vhost6, | |
118 | const char *vhostname, const char *tftp_server_name, | |
119 | const char *tftp_path, const char *bootfile, | |
120 | struct in_addr vdhcp_start, struct in_addr vnameserver, | |
121 | struct in6_addr vnameserver6, const char **vdnssearch, | |
122 | const char *vdomainname, const SlirpCb *callbacks, | |
123 | void *opaque); | |
124 | void slirp_cleanup(Slirp *slirp); | |
125 | ||
126 | void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout, | |
127 | SlirpAddPollCb add_poll, void *opaque); | |
128 | ||
129 | void slirp_pollfds_poll(Slirp *slirp, int select_error, | |
130 | SlirpGetREventsCb get_revents, void *opaque); | |
131 | ||
132 | void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); | |
133 | ||
134 | int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, | |
135 | int host_port, struct in_addr guest_addr, int guest_port); | |
136 | int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, | |
137 | int host_port); | |
138 | int slirp_add_exec(Slirp *slirp, const char *cmdline, | |
139 | struct in_addr *guest_addr, int guest_port); | |
140 | int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque, | |
141 | struct in_addr *guest_addr, int guest_port); | |
142 | ||
143 | char *slirp_connection_info(Slirp *slirp); | |
144 | ||
145 | void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, | |
146 | const uint8_t *buf, int size); | |
147 | size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, | |
148 | int guest_port); | |
149 | ||
150 | void slirp_state_save(Slirp *s, SlirpWriteCb write_cb, void *opaque); | |
151 | ||
152 | int slirp_state_load(Slirp *s, int version_id, SlirpReadCb read_cb, | |
153 | void *opaque); | |
154 | ||
155 | int slirp_state_version(void); | |
156 | ||
157 | const char *slirp_version_string(void); | |
158 | ||
159 | #ifdef __cplusplus | |
160 | } /* extern "C" */ | |
161 | #endif | |
162 | ||
163 | #endif /* LIBSLIRP_H */ |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef SLIRP_MAIN_H | |
6 | #define SLIRP_MAIN_H | |
7 | ||
8 | extern unsigned curtime; | |
9 | extern struct in_addr loopback_addr; | |
10 | extern unsigned long loopback_mask; | |
11 | ||
12 | int if_encap(Slirp *slirp, struct mbuf *ifm); | |
13 | ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags); | |
14 | ||
15 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski | |
3 | */ | |
4 | ||
5 | /* | |
6 | * mbuf's in SLiRP are much simpler than the real mbufs in | |
7 | * FreeBSD. They are fixed size, determined by the MTU, | |
8 | * so that one whole packet can fit. Mbuf's cannot be | |
9 | * chained together. If there's more data than the mbuf | |
10 | * could hold, an external g_malloced buffer is pointed to | |
11 | * by m_ext (and the data pointers) and M_EXT is set in | |
12 | * the flags | |
13 | */ | |
14 | ||
15 | #include "slirp.h" | |
16 | ||
17 | #define MBUF_THRESH 30 | |
18 | ||
19 | /* | |
20 | * Find a nice value for msize | |
21 | */ | |
22 | #define SLIRP_MSIZE(mtu) \ | |
23 | (offsetof(struct mbuf, m_dat) + IF_MAXLINKHDR + TCPIPHDR_DELTA + (mtu)) | |
24 | ||
25 | void m_init(Slirp *slirp) | |
26 | { | |
27 | slirp->m_freelist.qh_link = slirp->m_freelist.qh_rlink = &slirp->m_freelist; | |
28 | slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist; | |
29 | } | |
30 | ||
31 | void m_cleanup(Slirp *slirp) | |
32 | { | |
33 | struct mbuf *m, *next; | |
34 | ||
35 | m = (struct mbuf *)slirp->m_usedlist.qh_link; | |
36 | while ((struct quehead *)m != &slirp->m_usedlist) { | |
37 | next = m->m_next; | |
38 | if (m->m_flags & M_EXT) { | |
39 | g_free(m->m_ext); | |
40 | } | |
41 | g_free(m); | |
42 | m = next; | |
43 | } | |
44 | m = (struct mbuf *)slirp->m_freelist.qh_link; | |
45 | while ((struct quehead *)m != &slirp->m_freelist) { | |
46 | next = m->m_next; | |
47 | g_free(m); | |
48 | m = next; | |
49 | } | |
50 | } | |
51 | ||
52 | /* | |
53 | * Get an mbuf from the free list, if there are none | |
54 | * allocate one | |
55 | * | |
56 | * Because fragmentation can occur if we alloc new mbufs and | |
57 | * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE, | |
58 | * which tells m_free to actually g_free() it | |
59 | */ | |
60 | struct mbuf *m_get(Slirp *slirp) | |
61 | { | |
62 | register struct mbuf *m; | |
63 | int flags = 0; | |
64 | ||
65 | DEBUG_CALL("m_get"); | |
66 | ||
67 | if (slirp->m_freelist.qh_link == &slirp->m_freelist) { | |
68 | m = g_malloc(SLIRP_MSIZE(slirp->if_mtu)); | |
69 | slirp->mbuf_alloced++; | |
70 | if (slirp->mbuf_alloced > MBUF_THRESH) | |
71 | flags = M_DOFREE; | |
72 | m->slirp = slirp; | |
73 | } else { | |
74 | m = (struct mbuf *)slirp->m_freelist.qh_link; | |
75 | remque(m); | |
76 | } | |
77 | ||
78 | /* Insert it in the used list */ | |
79 | insque(m, &slirp->m_usedlist); | |
80 | m->m_flags = (flags | M_USEDLIST); | |
81 | ||
82 | /* Initialise it */ | |
83 | m->m_size = SLIRP_MSIZE(slirp->if_mtu) - offsetof(struct mbuf, m_dat); | |
84 | m->m_data = m->m_dat; | |
85 | m->m_len = 0; | |
86 | m->m_nextpkt = NULL; | |
87 | m->m_prevpkt = NULL; | |
88 | m->resolution_requested = false; | |
89 | m->expiration_date = (uint64_t)-1; | |
90 | DEBUG_ARG("m = %p", m); | |
91 | return m; | |
92 | } | |
93 | ||
94 | void m_free(struct mbuf *m) | |
95 | { | |
96 | DEBUG_CALL("m_free"); | |
97 | DEBUG_ARG("m = %p", m); | |
98 | ||
99 | if (m) { | |
100 | /* Remove from m_usedlist */ | |
101 | if (m->m_flags & M_USEDLIST) | |
102 | remque(m); | |
103 | ||
104 | /* If it's M_EXT, free() it */ | |
105 | if (m->m_flags & M_EXT) { | |
106 | g_free(m->m_ext); | |
107 | } | |
108 | /* | |
109 | * Either free() it or put it on the free list | |
110 | */ | |
111 | if (m->m_flags & M_DOFREE) { | |
112 | m->slirp->mbuf_alloced--; | |
113 | g_free(m); | |
114 | } else if ((m->m_flags & M_FREELIST) == 0) { | |
115 | insque(m, &m->slirp->m_freelist); | |
116 | m->m_flags = M_FREELIST; /* Clobber other flags */ | |
117 | } | |
118 | } /* if(m) */ | |
119 | } | |
120 | ||
121 | /* | |
122 | * Copy data from one mbuf to the end of | |
123 | * the other.. if result is too big for one mbuf, allocate | |
124 | * an M_EXT data segment | |
125 | */ | |
126 | void m_cat(struct mbuf *m, struct mbuf *n) | |
127 | { | |
128 | /* | |
129 | * If there's no room, realloc | |
130 | */ | |
131 | if (M_FREEROOM(m) < n->m_len) | |
132 | m_inc(m, m->m_len + n->m_len); | |
133 | ||
134 | memcpy(m->m_data + m->m_len, n->m_data, n->m_len); | |
135 | m->m_len += n->m_len; | |
136 | ||
137 | m_free(n); | |
138 | } | |
139 | ||
140 | ||
141 | /* make m 'size' bytes large from m_data */ | |
142 | void m_inc(struct mbuf *m, int size) | |
143 | { | |
144 | int gapsize; | |
145 | ||
146 | /* some compilers throw up on gotos. This one we can fake. */ | |
147 | if (M_ROOM(m) > size) { | |
148 | return; | |
149 | } | |
150 | ||
151 | if (m->m_flags & M_EXT) { | |
152 | gapsize = m->m_data - m->m_ext; | |
153 | m->m_ext = g_realloc(m->m_ext, size + gapsize); | |
154 | } else { | |
155 | gapsize = m->m_data - m->m_dat; | |
156 | m->m_ext = g_malloc(size + gapsize); | |
157 | memcpy(m->m_ext, m->m_dat, m->m_size); | |
158 | m->m_flags |= M_EXT; | |
159 | } | |
160 | ||
161 | m->m_data = m->m_ext + gapsize; | |
162 | m->m_size = size + gapsize; | |
163 | } | |
164 | ||
165 | ||
166 | void m_adj(struct mbuf *m, int len) | |
167 | { | |
168 | if (m == NULL) | |
169 | return; | |
170 | if (len >= 0) { | |
171 | /* Trim from head */ | |
172 | m->m_data += len; | |
173 | m->m_len -= len; | |
174 | } else { | |
175 | /* Trim from tail */ | |
176 | len = -len; | |
177 | m->m_len -= len; | |
178 | } | |
179 | } | |
180 | ||
181 | ||
182 | /* | |
183 | * Copy len bytes from m, starting off bytes into n | |
184 | */ | |
185 | int m_copy(struct mbuf *n, struct mbuf *m, int off, int len) | |
186 | { | |
187 | if (len > M_FREEROOM(n)) | |
188 | return -1; | |
189 | ||
190 | memcpy((n->m_data + n->m_len), (m->m_data + off), len); | |
191 | n->m_len += len; | |
192 | return 0; | |
193 | } | |
194 | ||
195 | ||
196 | /* | |
197 | * Given a pointer into an mbuf, return the mbuf | |
198 | * XXX This is a kludge, I should eliminate the need for it | |
199 | * Fortunately, it's not used often | |
200 | */ | |
201 | struct mbuf *dtom(Slirp *slirp, void *dat) | |
202 | { | |
203 | struct mbuf *m; | |
204 | ||
205 | DEBUG_CALL("dtom"); | |
206 | DEBUG_ARG("dat = %p", dat); | |
207 | ||
208 | /* bug corrected for M_EXT buffers */ | |
209 | for (m = (struct mbuf *)slirp->m_usedlist.qh_link; | |
210 | (struct quehead *)m != &slirp->m_usedlist; m = m->m_next) { | |
211 | if (m->m_flags & M_EXT) { | |
212 | if ((char *)dat >= m->m_ext && (char *)dat < (m->m_ext + m->m_size)) | |
213 | return m; | |
214 | } else { | |
215 | if ((char *)dat >= m->m_dat && (char *)dat < (m->m_dat + m->m_size)) | |
216 | return m; | |
217 | } | |
218 | } | |
219 | ||
220 | DEBUG_ERROR("dtom failed"); | |
221 | ||
222 | return (struct mbuf *)0; | |
223 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)mbuf.h 8.3 (Berkeley) 1/21/94 | |
30 | * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp | |
31 | */ | |
32 | ||
33 | #ifndef MBUF_H | |
34 | #define MBUF_H | |
35 | ||
36 | /* | |
37 | * Macros for type conversion | |
38 | * mtod(m,t) - convert mbuf pointer to data pointer of correct type | |
39 | */ | |
40 | #define mtod(m, t) ((t)(m)->m_data) | |
41 | ||
42 | /* XXX About mbufs for slirp: | |
43 | * Only one mbuf is ever used in a chain, for each "cell" of data. | |
44 | * m_nextpkt points to the next packet, if fragmented. | |
45 | * If the data is too large, the M_EXT is used, and a larger block | |
46 | * is alloced. Therefore, m_free[m] must check for M_EXT and if set | |
47 | * free the m_ext. This is inefficient memory-wise, but who cares. | |
48 | */ | |
49 | ||
50 | /* | |
51 | * mbufs allow to have a gap between the start of the allocated buffer (m_ext if | |
52 | * M_EXT is set, m_dat otherwise) and the in-use data: | |
53 | * | |
54 | * |--gapsize----->|---m_len-------> | |
55 | * |----------m_size------------------------------> | |
56 | * |----M_ROOM--------------------> | |
57 | * |-M_FREEROOM--> | |
58 | * | |
59 | * ^ ^ ^ | |
60 | * m_dat/m_ext m_data end of buffer | |
61 | */ | |
62 | ||
63 | /* | |
64 | * How much room is in the mbuf, from m_data to the end of the mbuf | |
65 | */ | |
66 | #define M_ROOM(m) \ | |
67 | ((m->m_flags & M_EXT) ? (((m)->m_ext + (m)->m_size) - (m)->m_data) : \ | |
68 | (((m)->m_dat + (m)->m_size) - (m)->m_data)) | |
69 | ||
70 | /* | |
71 | * How much free room there is | |
72 | */ | |
73 | #define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len) | |
74 | ||
75 | struct mbuf { | |
76 | /* XXX should union some of these! */ | |
77 | /* header at beginning of each mbuf: */ | |
78 | struct mbuf *m_next; /* Linked list of mbufs */ | |
79 | struct mbuf *m_prev; | |
80 | struct mbuf *m_nextpkt; /* Next packet in queue/record */ | |
81 | struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ | |
82 | int m_flags; /* Misc flags */ | |
83 | ||
84 | int m_size; /* Size of mbuf, from m_dat or m_ext */ | |
85 | struct socket *m_so; | |
86 | ||
87 | char *m_data; /* Current location of data */ | |
88 | int m_len; /* Amount of data in this mbuf, from m_data */ | |
89 | ||
90 | Slirp *slirp; | |
91 | bool resolution_requested; | |
92 | uint64_t expiration_date; | |
93 | char *m_ext; | |
94 | /* start of dynamic buffer area, must be last element */ | |
95 | char m_dat[]; | |
96 | }; | |
97 | ||
98 | #define ifq_prev m_prev | |
99 | #define ifq_next m_next | |
100 | #define ifs_prev m_prevpkt | |
101 | #define ifs_next m_nextpkt | |
102 | #define ifq_so m_so | |
103 | ||
104 | #define M_EXT 0x01 /* m_ext points to more (malloced) data */ | |
105 | #define M_FREELIST 0x02 /* mbuf is on free list */ | |
106 | #define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */ | |
107 | #define M_DOFREE \ | |
108 | 0x08 /* when m_free is called on the mbuf, free() \ | |
109 | * it rather than putting it on the free list */ | |
110 | ||
111 | void m_init(Slirp *); | |
112 | void m_cleanup(Slirp *slirp); | |
113 | struct mbuf *m_get(Slirp *); | |
114 | void m_free(struct mbuf *); | |
115 | void m_cat(register struct mbuf *, register struct mbuf *); | |
116 | void m_inc(struct mbuf *, int); | |
117 | void m_adj(struct mbuf *, int); | |
118 | int m_copy(struct mbuf *, struct mbuf *, int, int); | |
119 | struct mbuf *dtom(Slirp *, void *); | |
120 | ||
121 | static inline void ifs_init(struct mbuf *ifm) | |
122 | { | |
123 | ifm->ifs_next = ifm->ifs_prev = ifm; | |
124 | } | |
125 | ||
126 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #include "slirp.h" | |
6 | ||
7 | inline void insque(void *a, void *b) | |
8 | { | |
9 | register struct quehead *element = (struct quehead *)a; | |
10 | register struct quehead *head = (struct quehead *)b; | |
11 | element->qh_link = head->qh_link; | |
12 | head->qh_link = (struct quehead *)element; | |
13 | element->qh_rlink = (struct quehead *)head; | |
14 | ((struct quehead *)(element->qh_link))->qh_rlink = | |
15 | (struct quehead *)element; | |
16 | } | |
17 | ||
18 | inline void remque(void *a) | |
19 | { | |
20 | register struct quehead *element = (struct quehead *)a; | |
21 | ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; | |
22 | ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; | |
23 | element->qh_rlink = NULL; | |
24 | } | |
25 | ||
26 | /* TODO: IPv6 */ | |
27 | struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb, | |
28 | void *opaque, struct in_addr addr, int port) | |
29 | { | |
30 | struct gfwd_list *f = g_new0(struct gfwd_list, 1); | |
31 | ||
32 | f->write_cb = write_cb; | |
33 | f->opaque = opaque; | |
34 | f->ex_fport = port; | |
35 | f->ex_addr = addr; | |
36 | f->ex_next = *ex_ptr; | |
37 | *ex_ptr = f; | |
38 | ||
39 | return f; | |
40 | } | |
41 | ||
42 | struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline, | |
43 | struct in_addr addr, int port) | |
44 | { | |
45 | struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port); | |
46 | ||
47 | f->ex_exec = g_strdup(cmdline); | |
48 | ||
49 | return f; | |
50 | } | |
51 | ||
52 | static int slirp_socketpair_with_oob(int sv[2]) | |
53 | { | |
54 | struct sockaddr_in addr = { | |
55 | .sin_family = AF_INET, | |
56 | .sin_port = 0, | |
57 | .sin_addr.s_addr = INADDR_ANY, | |
58 | }; | |
59 | socklen_t addrlen = sizeof(addr); | |
60 | int ret, s; | |
61 | ||
62 | sv[1] = -1; | |
63 | s = slirp_socket(AF_INET, SOCK_STREAM, 0); | |
64 | if (s < 0 || bind(s, (struct sockaddr *)&addr, addrlen) < 0 || | |
65 | listen(s, 1) < 0 || | |
66 | getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) { | |
67 | goto err; | |
68 | } | |
69 | ||
70 | sv[1] = slirp_socket(AF_INET, SOCK_STREAM, 0); | |
71 | if (sv[1] < 0) { | |
72 | goto err; | |
73 | } | |
74 | /* | |
75 | * This connect won't block because we've already listen()ed on | |
76 | * the server end (even though we won't accept() the connection | |
77 | * until later on). | |
78 | */ | |
79 | do { | |
80 | ret = connect(sv[1], (struct sockaddr *)&addr, addrlen); | |
81 | } while (ret < 0 && errno == EINTR); | |
82 | if (ret < 0) { | |
83 | goto err; | |
84 | } | |
85 | ||
86 | do { | |
87 | sv[0] = accept(s, (struct sockaddr *)&addr, &addrlen); | |
88 | } while (sv[0] < 0 && errno == EINTR); | |
89 | if (sv[0] < 0) { | |
90 | goto err; | |
91 | } | |
92 | ||
93 | closesocket(s); | |
94 | return 0; | |
95 | ||
96 | err: | |
97 | g_critical("slirp_socketpair(): %s", strerror(errno)); | |
98 | if (s >= 0) { | |
99 | closesocket(s); | |
100 | } | |
101 | if (sv[1] >= 0) { | |
102 | closesocket(sv[1]); | |
103 | } | |
104 | return -1; | |
105 | } | |
106 | ||
107 | static void fork_exec_child_setup(gpointer data) | |
108 | { | |
109 | #ifndef _WIN32 | |
110 | setsid(); | |
111 | #endif | |
112 | } | |
113 | ||
114 | #pragma GCC diagnostic push | |
115 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |
116 | ||
117 | #if !GLIB_CHECK_VERSION(2, 58, 0) | |
118 | typedef struct SlirpGSpawnFds { | |
119 | GSpawnChildSetupFunc child_setup; | |
120 | gpointer user_data; | |
121 | gint stdin_fd; | |
122 | gint stdout_fd; | |
123 | gint stderr_fd; | |
124 | } SlirpGSpawnFds; | |
125 | ||
126 | static inline void slirp_gspawn_fds_setup(gpointer user_data) | |
127 | { | |
128 | SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data; | |
129 | ||
130 | dup2(q->stdin_fd, 0); | |
131 | dup2(q->stdout_fd, 1); | |
132 | dup2(q->stderr_fd, 2); | |
133 | q->child_setup(q->user_data); | |
134 | } | |
135 | #endif | |
136 | ||
137 | static inline gboolean | |
138 | g_spawn_async_with_fds_slirp(const gchar *working_directory, gchar **argv, | |
139 | gchar **envp, GSpawnFlags flags, | |
140 | GSpawnChildSetupFunc child_setup, | |
141 | gpointer user_data, GPid *child_pid, gint stdin_fd, | |
142 | gint stdout_fd, gint stderr_fd, GError **error) | |
143 | { | |
144 | #if GLIB_CHECK_VERSION(2, 58, 0) | |
145 | return g_spawn_async_with_fds(working_directory, argv, envp, flags, | |
146 | child_setup, user_data, child_pid, stdin_fd, | |
147 | stdout_fd, stderr_fd, error); | |
148 | #else | |
149 | SlirpGSpawnFds setup = { | |
150 | .child_setup = child_setup, | |
151 | .user_data = user_data, | |
152 | .stdin_fd = stdin_fd, | |
153 | .stdout_fd = stdout_fd, | |
154 | .stderr_fd = stderr_fd, | |
155 | }; | |
156 | ||
157 | return g_spawn_async(working_directory, argv, envp, flags, | |
158 | slirp_gspawn_fds_setup, &setup, child_pid, error); | |
159 | #endif | |
160 | } | |
161 | ||
162 | #define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \ | |
163 | g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) | |
164 | ||
165 | #pragma GCC diagnostic pop | |
166 | ||
167 | int fork_exec(struct socket *so, const char *ex) | |
168 | { | |
169 | GError *err = NULL; | |
170 | char **argv; | |
171 | int opt, sp[2]; | |
172 | ||
173 | DEBUG_CALL("fork_exec"); | |
174 | DEBUG_ARG("so = %p", so); | |
175 | DEBUG_ARG("ex = %p", ex); | |
176 | ||
177 | if (slirp_socketpair_with_oob(sp) < 0) { | |
178 | return 0; | |
179 | } | |
180 | ||
181 | argv = g_strsplit(ex, " ", -1); | |
182 | g_spawn_async_with_fds(NULL /* cwd */, argv, NULL /* env */, | |
183 | G_SPAWN_SEARCH_PATH, fork_exec_child_setup, | |
184 | NULL /* data */, NULL /* child_pid */, sp[1], sp[1], | |
185 | sp[1], &err); | |
186 | g_strfreev(argv); | |
187 | ||
188 | if (err) { | |
189 | g_critical("fork_exec: %s", err->message); | |
190 | g_error_free(err); | |
191 | closesocket(sp[0]); | |
192 | closesocket(sp[1]); | |
193 | return 0; | |
194 | } | |
195 | ||
196 | so->s = sp[0]; | |
197 | closesocket(sp[1]); | |
198 | slirp_socket_set_fast_reuse(so->s); | |
199 | opt = 1; | |
200 | setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); | |
201 | slirp_set_nonblock(so->s); | |
202 | so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); | |
203 | return 1; | |
204 | } | |
205 | ||
206 | char *slirp_connection_info(Slirp *slirp) | |
207 | { | |
208 | GString *str = g_string_new(NULL); | |
209 | const char *const tcpstates[] = { | |
210 | [TCPS_CLOSED] = "CLOSED", [TCPS_LISTEN] = "LISTEN", | |
211 | [TCPS_SYN_SENT] = "SYN_SENT", [TCPS_SYN_RECEIVED] = "SYN_RCVD", | |
212 | [TCPS_ESTABLISHED] = "ESTABLISHED", [TCPS_CLOSE_WAIT] = "CLOSE_WAIT", | |
213 | [TCPS_FIN_WAIT_1] = "FIN_WAIT_1", [TCPS_CLOSING] = "CLOSING", | |
214 | [TCPS_LAST_ACK] = "LAST_ACK", [TCPS_FIN_WAIT_2] = "FIN_WAIT_2", | |
215 | [TCPS_TIME_WAIT] = "TIME_WAIT", | |
216 | }; | |
217 | struct in_addr dst_addr; | |
218 | struct sockaddr_in src; | |
219 | socklen_t src_len; | |
220 | uint16_t dst_port; | |
221 | struct socket *so; | |
222 | const char *state; | |
223 | char buf[20]; | |
224 | ||
225 | g_string_append_printf(str, | |
226 | " Protocol[State] FD Source Address Port " | |
227 | "Dest. Address Port RecvQ SendQ\n"); | |
228 | ||
229 | /* TODO: IPv6 */ | |
230 | ||
231 | for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { | |
232 | if (so->so_state & SS_HOSTFWD) { | |
233 | state = "HOST_FORWARD"; | |
234 | } else if (so->so_tcpcb) { | |
235 | state = tcpstates[so->so_tcpcb->t_state]; | |
236 | } else { | |
237 | state = "NONE"; | |
238 | } | |
239 | if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { | |
240 | src_len = sizeof(src); | |
241 | getsockname(so->s, (struct sockaddr *)&src, &src_len); | |
242 | dst_addr = so->so_laddr; | |
243 | dst_port = so->so_lport; | |
244 | } else { | |
245 | src.sin_addr = so->so_laddr; | |
246 | src.sin_port = so->so_lport; | |
247 | dst_addr = so->so_faddr; | |
248 | dst_port = so->so_fport; | |
249 | } | |
250 | snprintf(buf, sizeof(buf), " TCP[%s]", state); | |
251 | g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, | |
252 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : | |
253 | "*", | |
254 | ntohs(src.sin_port)); | |
255 | g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr), | |
256 | ntohs(dst_port), so->so_rcv.sb_cc, | |
257 | so->so_snd.sb_cc); | |
258 | } | |
259 | ||
260 | for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { | |
261 | if (so->so_state & SS_HOSTFWD) { | |
262 | snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]"); | |
263 | src_len = sizeof(src); | |
264 | getsockname(so->s, (struct sockaddr *)&src, &src_len); | |
265 | dst_addr = so->so_laddr; | |
266 | dst_port = so->so_lport; | |
267 | } else { | |
268 | snprintf(buf, sizeof(buf), " UDP[%d sec]", | |
269 | (so->so_expire - curtime) / 1000); | |
270 | src.sin_addr = so->so_laddr; | |
271 | src.sin_port = so->so_lport; | |
272 | dst_addr = so->so_faddr; | |
273 | dst_port = so->so_fport; | |
274 | } | |
275 | g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, | |
276 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : | |
277 | "*", | |
278 | ntohs(src.sin_port)); | |
279 | g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr), | |
280 | ntohs(dst_port), so->so_rcv.sb_cc, | |
281 | so->so_snd.sb_cc); | |
282 | } | |
283 | ||
284 | for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { | |
285 | snprintf(buf, sizeof(buf), " ICMP[%d sec]", | |
286 | (so->so_expire - curtime) / 1000); | |
287 | src.sin_addr = so->so_laddr; | |
288 | dst_addr = so->so_faddr; | |
289 | g_string_append_printf(str, "%-19s %3d %15s - ", buf, so->s, | |
290 | src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : | |
291 | "*"); | |
292 | g_string_append_printf(str, "%15s - %5d %5d\n", inet_ntoa(dst_addr), | |
293 | so->so_rcv.sb_cc, so->so_snd.sb_cc); | |
294 | } | |
295 | ||
296 | return g_string_free(str, FALSE); | |
297 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef MISC_H | |
6 | #define MISC_H | |
7 | ||
8 | #include "libslirp.h" | |
9 | ||
10 | struct gfwd_list { | |
11 | SlirpWriteCb write_cb; | |
12 | void *opaque; | |
13 | struct in_addr ex_addr; /* Server address */ | |
14 | int ex_fport; /* Port to telnet to */ | |
15 | char *ex_exec; /* Command line of what to exec */ | |
16 | struct gfwd_list *ex_next; | |
17 | }; | |
18 | ||
19 | #define EMU_NONE 0x0 | |
20 | ||
21 | /* TCP emulations */ | |
22 | #define EMU_CTL 0x1 | |
23 | #define EMU_FTP 0x2 | |
24 | #define EMU_KSH 0x3 | |
25 | #define EMU_IRC 0x4 | |
26 | #define EMU_REALAUDIO 0x5 | |
27 | #define EMU_RLOGIN 0x6 | |
28 | #define EMU_IDENT 0x7 | |
29 | ||
30 | #define EMU_NOCONNECT 0x10 /* Don't connect */ | |
31 | ||
32 | struct tos_t { | |
33 | uint16_t lport; | |
34 | uint16_t fport; | |
35 | uint8_t tos; | |
36 | uint8_t emu; | |
37 | }; | |
38 | ||
39 | struct emu_t { | |
40 | uint16_t lport; | |
41 | uint16_t fport; | |
42 | uint8_t tos; | |
43 | uint8_t emu; | |
44 | struct emu_t *next; | |
45 | }; | |
46 | ||
47 | struct slirp_quehead { | |
48 | struct slirp_quehead *qh_link; | |
49 | struct slirp_quehead *qh_rlink; | |
50 | }; | |
51 | ||
52 | void slirp_insque(void *, void *); | |
53 | void slirp_remque(void *); | |
54 | int fork_exec(struct socket *so, const char *ex); | |
55 | ||
56 | struct gfwd_list *add_guestfwd(struct gfwd_list **ex_ptr, SlirpWriteCb write_cb, | |
57 | void *opaque, struct in_addr addr, int port); | |
58 | ||
59 | struct gfwd_list *add_exec(struct gfwd_list **ex_ptr, const char *cmdline, | |
60 | struct in_addr addr, int port); | |
61 | ||
62 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright Gavin Shan, IBM Corporation 2016. | |
3 | * | |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * | |
8 | * 1. Redistributions of source code must retain the above | |
9 | * copyright notice, this list of conditions and the following | |
10 | * disclaimer. | |
11 | * | |
12 | * 2. Redistributions in binary form must reproduce the above | |
13 | * copyright notice, this list of conditions and the following | |
14 | * disclaimer in the documentation and/or other materials provided | |
15 | * with the distribution. | |
16 | * | |
17 | * 3. Neither the name of the copyright holder nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
26 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
32 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #ifndef NCSI_PKT_H | |
36 | #define NCSI_PKT_H | |
37 | ||
38 | /* from linux/net/ncsi/ncsi-pkt.h */ | |
39 | #define __be32 uint32_t | |
40 | #define __be16 uint16_t | |
41 | ||
42 | struct ncsi_pkt_hdr { | |
43 | unsigned char mc_id; /* Management controller ID */ | |
44 | unsigned char revision; /* NCSI version - 0x01 */ | |
45 | unsigned char reserved; /* Reserved */ | |
46 | unsigned char id; /* Packet sequence number */ | |
47 | unsigned char type; /* Packet type */ | |
48 | unsigned char channel; /* Network controller ID */ | |
49 | __be16 length; /* Payload length */ | |
50 | __be32 reserved1[2]; /* Reserved */ | |
51 | }; | |
52 | ||
53 | struct ncsi_cmd_pkt_hdr { | |
54 | struct ncsi_pkt_hdr common; /* Common NCSI packet header */ | |
55 | }; | |
56 | ||
57 | struct ncsi_rsp_pkt_hdr { | |
58 | struct ncsi_pkt_hdr common; /* Common NCSI packet header */ | |
59 | __be16 code; /* Response code */ | |
60 | __be16 reason; /* Response reason */ | |
61 | }; | |
62 | ||
63 | struct ncsi_aen_pkt_hdr { | |
64 | struct ncsi_pkt_hdr common; /* Common NCSI packet header */ | |
65 | unsigned char reserved2[3]; /* Reserved */ | |
66 | unsigned char type; /* AEN packet type */ | |
67 | }; | |
68 | ||
69 | /* NCSI common command packet */ | |
70 | struct ncsi_cmd_pkt { | |
71 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
72 | __be32 checksum; /* Checksum */ | |
73 | unsigned char pad[26]; | |
74 | }; | |
75 | ||
76 | struct ncsi_rsp_pkt { | |
77 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
78 | __be32 checksum; /* Checksum */ | |
79 | unsigned char pad[22]; | |
80 | }; | |
81 | ||
82 | /* Select Package */ | |
83 | struct ncsi_cmd_sp_pkt { | |
84 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
85 | unsigned char reserved[3]; /* Reserved */ | |
86 | unsigned char hw_arbitration; /* HW arbitration */ | |
87 | __be32 checksum; /* Checksum */ | |
88 | unsigned char pad[22]; | |
89 | }; | |
90 | ||
91 | /* Disable Channel */ | |
92 | struct ncsi_cmd_dc_pkt { | |
93 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
94 | unsigned char reserved[3]; /* Reserved */ | |
95 | unsigned char ald; /* Allow link down */ | |
96 | __be32 checksum; /* Checksum */ | |
97 | unsigned char pad[22]; | |
98 | }; | |
99 | ||
100 | /* Reset Channel */ | |
101 | struct ncsi_cmd_rc_pkt { | |
102 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
103 | __be32 reserved; /* Reserved */ | |
104 | __be32 checksum; /* Checksum */ | |
105 | unsigned char pad[22]; | |
106 | }; | |
107 | ||
108 | /* AEN Enable */ | |
109 | struct ncsi_cmd_ae_pkt { | |
110 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
111 | unsigned char reserved[3]; /* Reserved */ | |
112 | unsigned char mc_id; /* MC ID */ | |
113 | __be32 mode; /* AEN working mode */ | |
114 | __be32 checksum; /* Checksum */ | |
115 | unsigned char pad[18]; | |
116 | }; | |
117 | ||
118 | /* Set Link */ | |
119 | struct ncsi_cmd_sl_pkt { | |
120 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
121 | __be32 mode; /* Link working mode */ | |
122 | __be32 oem_mode; /* OEM link mode */ | |
123 | __be32 checksum; /* Checksum */ | |
124 | unsigned char pad[18]; | |
125 | }; | |
126 | ||
127 | /* Set VLAN Filter */ | |
128 | struct ncsi_cmd_svf_pkt { | |
129 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
130 | __be16 reserved; /* Reserved */ | |
131 | __be16 vlan; /* VLAN ID */ | |
132 | __be16 reserved1; /* Reserved */ | |
133 | unsigned char index; /* VLAN table index */ | |
134 | unsigned char enable; /* Enable or disable */ | |
135 | __be32 checksum; /* Checksum */ | |
136 | unsigned char pad[14]; | |
137 | }; | |
138 | ||
139 | /* Enable VLAN */ | |
140 | struct ncsi_cmd_ev_pkt { | |
141 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
142 | unsigned char reserved[3]; /* Reserved */ | |
143 | unsigned char mode; /* VLAN filter mode */ | |
144 | __be32 checksum; /* Checksum */ | |
145 | unsigned char pad[22]; | |
146 | }; | |
147 | ||
148 | /* Set MAC Address */ | |
149 | struct ncsi_cmd_sma_pkt { | |
150 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
151 | unsigned char mac[6]; /* MAC address */ | |
152 | unsigned char index; /* MAC table index */ | |
153 | unsigned char at_e; /* Addr type and operation */ | |
154 | __be32 checksum; /* Checksum */ | |
155 | unsigned char pad[18]; | |
156 | }; | |
157 | ||
158 | /* Enable Broadcast Filter */ | |
159 | struct ncsi_cmd_ebf_pkt { | |
160 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
161 | __be32 mode; /* Filter mode */ | |
162 | __be32 checksum; /* Checksum */ | |
163 | unsigned char pad[22]; | |
164 | }; | |
165 | ||
166 | /* Enable Global Multicast Filter */ | |
167 | struct ncsi_cmd_egmf_pkt { | |
168 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
169 | __be32 mode; /* Global MC mode */ | |
170 | __be32 checksum; /* Checksum */ | |
171 | unsigned char pad[22]; | |
172 | }; | |
173 | ||
174 | /* Set NCSI Flow Control */ | |
175 | struct ncsi_cmd_snfc_pkt { | |
176 | struct ncsi_cmd_pkt_hdr cmd; /* Command header */ | |
177 | unsigned char reserved[3]; /* Reserved */ | |
178 | unsigned char mode; /* Flow control mode */ | |
179 | __be32 checksum; /* Checksum */ | |
180 | unsigned char pad[22]; | |
181 | }; | |
182 | ||
183 | /* Get Link Status */ | |
184 | struct ncsi_rsp_gls_pkt { | |
185 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
186 | __be32 status; /* Link status */ | |
187 | __be32 other; /* Other indications */ | |
188 | __be32 oem_status; /* OEM link status */ | |
189 | __be32 checksum; | |
190 | unsigned char pad[10]; | |
191 | }; | |
192 | ||
193 | /* Get Version ID */ | |
194 | struct ncsi_rsp_gvi_pkt { | |
195 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
196 | __be32 ncsi_version; /* NCSI version */ | |
197 | unsigned char reserved[3]; /* Reserved */ | |
198 | unsigned char alpha2; /* NCSI version */ | |
199 | unsigned char fw_name[12]; /* f/w name string */ | |
200 | __be32 fw_version; /* f/w version */ | |
201 | __be16 pci_ids[4]; /* PCI IDs */ | |
202 | __be32 mf_id; /* Manufacture ID */ | |
203 | __be32 checksum; | |
204 | }; | |
205 | ||
206 | /* Get Capabilities */ | |
207 | struct ncsi_rsp_gc_pkt { | |
208 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
209 | __be32 cap; /* Capabilities */ | |
210 | __be32 bc_cap; /* Broadcast cap */ | |
211 | __be32 mc_cap; /* Multicast cap */ | |
212 | __be32 buf_cap; /* Buffering cap */ | |
213 | __be32 aen_cap; /* AEN cap */ | |
214 | unsigned char vlan_cnt; /* VLAN filter count */ | |
215 | unsigned char mixed_cnt; /* Mix filter count */ | |
216 | unsigned char mc_cnt; /* MC filter count */ | |
217 | unsigned char uc_cnt; /* UC filter count */ | |
218 | unsigned char reserved[2]; /* Reserved */ | |
219 | unsigned char vlan_mode; /* VLAN mode */ | |
220 | unsigned char channel_cnt; /* Channel count */ | |
221 | __be32 checksum; /* Checksum */ | |
222 | }; | |
223 | ||
224 | /* Get Parameters */ | |
225 | struct ncsi_rsp_gp_pkt { | |
226 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
227 | unsigned char mac_cnt; /* Number of MAC addr */ | |
228 | unsigned char reserved[2]; /* Reserved */ | |
229 | unsigned char mac_enable; /* MAC addr enable flags */ | |
230 | unsigned char vlan_cnt; /* VLAN tag count */ | |
231 | unsigned char reserved1; /* Reserved */ | |
232 | __be16 vlan_enable; /* VLAN tag enable flags */ | |
233 | __be32 link_mode; /* Link setting */ | |
234 | __be32 bc_mode; /* BC filter mode */ | |
235 | __be32 valid_modes; /* Valid mode parameters */ | |
236 | unsigned char vlan_mode; /* VLAN mode */ | |
237 | unsigned char fc_mode; /* Flow control mode */ | |
238 | unsigned char reserved2[2]; /* Reserved */ | |
239 | __be32 aen_mode; /* AEN mode */ | |
240 | unsigned char mac[6]; /* Supported MAC addr */ | |
241 | __be16 vlan; /* Supported VLAN tags */ | |
242 | __be32 checksum; /* Checksum */ | |
243 | }; | |
244 | ||
245 | /* Get Controller Packet Statistics */ | |
246 | struct ncsi_rsp_gcps_pkt { | |
247 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
248 | __be32 cnt_hi; /* Counter cleared */ | |
249 | __be32 cnt_lo; /* Counter cleared */ | |
250 | __be32 rx_bytes; /* Rx bytes */ | |
251 | __be32 tx_bytes; /* Tx bytes */ | |
252 | __be32 rx_uc_pkts; /* Rx UC packets */ | |
253 | __be32 rx_mc_pkts; /* Rx MC packets */ | |
254 | __be32 rx_bc_pkts; /* Rx BC packets */ | |
255 | __be32 tx_uc_pkts; /* Tx UC packets */ | |
256 | __be32 tx_mc_pkts; /* Tx MC packets */ | |
257 | __be32 tx_bc_pkts; /* Tx BC packets */ | |
258 | __be32 fcs_err; /* FCS errors */ | |
259 | __be32 align_err; /* Alignment errors */ | |
260 | __be32 false_carrier; /* False carrier detection */ | |
261 | __be32 runt_pkts; /* Rx runt packets */ | |
262 | __be32 jabber_pkts; /* Rx jabber packets */ | |
263 | __be32 rx_pause_xon; /* Rx pause XON frames */ | |
264 | __be32 rx_pause_xoff; /* Rx XOFF frames */ | |
265 | __be32 tx_pause_xon; /* Tx XON frames */ | |
266 | __be32 tx_pause_xoff; /* Tx XOFF frames */ | |
267 | __be32 tx_s_collision; /* Single collision frames */ | |
268 | __be32 tx_m_collision; /* Multiple collision frames */ | |
269 | __be32 l_collision; /* Late collision frames */ | |
270 | __be32 e_collision; /* Excessive collision frames */ | |
271 | __be32 rx_ctl_frames; /* Rx control frames */ | |
272 | __be32 rx_64_frames; /* Rx 64-bytes frames */ | |
273 | __be32 rx_127_frames; /* Rx 65-127 bytes frames */ | |
274 | __be32 rx_255_frames; /* Rx 128-255 bytes frames */ | |
275 | __be32 rx_511_frames; /* Rx 256-511 bytes frames */ | |
276 | __be32 rx_1023_frames; /* Rx 512-1023 bytes frames */ | |
277 | __be32 rx_1522_frames; /* Rx 1024-1522 bytes frames */ | |
278 | __be32 rx_9022_frames; /* Rx 1523-9022 bytes frames */ | |
279 | __be32 tx_64_frames; /* Tx 64-bytes frames */ | |
280 | __be32 tx_127_frames; /* Tx 65-127 bytes frames */ | |
281 | __be32 tx_255_frames; /* Tx 128-255 bytes frames */ | |
282 | __be32 tx_511_frames; /* Tx 256-511 bytes frames */ | |
283 | __be32 tx_1023_frames; /* Tx 512-1023 bytes frames */ | |
284 | __be32 tx_1522_frames; /* Tx 1024-1522 bytes frames */ | |
285 | __be32 tx_9022_frames; /* Tx 1523-9022 bytes frames */ | |
286 | __be32 rx_valid_bytes; /* Rx valid bytes */ | |
287 | __be32 rx_runt_pkts; /* Rx error runt packets */ | |
288 | __be32 rx_jabber_pkts; /* Rx error jabber packets */ | |
289 | __be32 checksum; /* Checksum */ | |
290 | }; | |
291 | ||
292 | /* Get NCSI Statistics */ | |
293 | struct ncsi_rsp_gns_pkt { | |
294 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
295 | __be32 rx_cmds; /* Rx NCSI commands */ | |
296 | __be32 dropped_cmds; /* Dropped commands */ | |
297 | __be32 cmd_type_errs; /* Command type errors */ | |
298 | __be32 cmd_csum_errs; /* Command checksum errors */ | |
299 | __be32 rx_pkts; /* Rx NCSI packets */ | |
300 | __be32 tx_pkts; /* Tx NCSI packets */ | |
301 | __be32 tx_aen_pkts; /* Tx AEN packets */ | |
302 | __be32 checksum; /* Checksum */ | |
303 | }; | |
304 | ||
305 | /* Get NCSI Pass-through Statistics */ | |
306 | struct ncsi_rsp_gnpts_pkt { | |
307 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
308 | __be32 tx_pkts; /* Tx packets */ | |
309 | __be32 tx_dropped; /* Tx dropped packets */ | |
310 | __be32 tx_channel_err; /* Tx channel errors */ | |
311 | __be32 tx_us_err; /* Tx undersize errors */ | |
312 | __be32 rx_pkts; /* Rx packets */ | |
313 | __be32 rx_dropped; /* Rx dropped packets */ | |
314 | __be32 rx_channel_err; /* Rx channel errors */ | |
315 | __be32 rx_us_err; /* Rx undersize errors */ | |
316 | __be32 rx_os_err; /* Rx oversize errors */ | |
317 | __be32 checksum; /* Checksum */ | |
318 | }; | |
319 | ||
320 | /* Get package status */ | |
321 | struct ncsi_rsp_gps_pkt { | |
322 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
323 | __be32 status; /* Hardware arbitration status */ | |
324 | __be32 checksum; | |
325 | }; | |
326 | ||
327 | /* Get package UUID */ | |
328 | struct ncsi_rsp_gpuuid_pkt { | |
329 | struct ncsi_rsp_pkt_hdr rsp; /* Response header */ | |
330 | unsigned char uuid[16]; /* UUID */ | |
331 | __be32 checksum; | |
332 | }; | |
333 | ||
334 | /* AEN: Link State Change */ | |
335 | struct ncsi_aen_lsc_pkt { | |
336 | struct ncsi_aen_pkt_hdr aen; /* AEN header */ | |
337 | __be32 status; /* Link status */ | |
338 | __be32 oem_status; /* OEM link status */ | |
339 | __be32 checksum; /* Checksum */ | |
340 | unsigned char pad[14]; | |
341 | }; | |
342 | ||
343 | /* AEN: Configuration Required */ | |
344 | struct ncsi_aen_cr_pkt { | |
345 | struct ncsi_aen_pkt_hdr aen; /* AEN header */ | |
346 | __be32 checksum; /* Checksum */ | |
347 | unsigned char pad[22]; | |
348 | }; | |
349 | ||
350 | /* AEN: Host Network Controller Driver Status Change */ | |
351 | struct ncsi_aen_hncdsc_pkt { | |
352 | struct ncsi_aen_pkt_hdr aen; /* AEN header */ | |
353 | __be32 status; /* Status */ | |
354 | __be32 checksum; /* Checksum */ | |
355 | unsigned char pad[18]; | |
356 | }; | |
357 | ||
358 | /* NCSI packet revision */ | |
359 | #define NCSI_PKT_REVISION 0x01 | |
360 | ||
361 | /* NCSI packet commands */ | |
362 | #define NCSI_PKT_CMD_CIS 0x00 /* Clear Initial State */ | |
363 | #define NCSI_PKT_CMD_SP 0x01 /* Select Package */ | |
364 | #define NCSI_PKT_CMD_DP 0x02 /* Deselect Package */ | |
365 | #define NCSI_PKT_CMD_EC 0x03 /* Enable Channel */ | |
366 | #define NCSI_PKT_CMD_DC 0x04 /* Disable Channel */ | |
367 | #define NCSI_PKT_CMD_RC 0x05 /* Reset Channel */ | |
368 | #define NCSI_PKT_CMD_ECNT 0x06 /* Enable Channel Network Tx */ | |
369 | #define NCSI_PKT_CMD_DCNT 0x07 /* Disable Channel Network Tx */ | |
370 | #define NCSI_PKT_CMD_AE 0x08 /* AEN Enable */ | |
371 | #define NCSI_PKT_CMD_SL 0x09 /* Set Link */ | |
372 | #define NCSI_PKT_CMD_GLS 0x0a /* Get Link */ | |
373 | #define NCSI_PKT_CMD_SVF 0x0b /* Set VLAN Filter */ | |
374 | #define NCSI_PKT_CMD_EV 0x0c /* Enable VLAN */ | |
375 | #define NCSI_PKT_CMD_DV 0x0d /* Disable VLAN */ | |
376 | #define NCSI_PKT_CMD_SMA 0x0e /* Set MAC address */ | |
377 | #define NCSI_PKT_CMD_EBF 0x10 /* Enable Broadcast Filter */ | |
378 | #define NCSI_PKT_CMD_DBF 0x11 /* Disable Broadcast Filter */ | |
379 | #define NCSI_PKT_CMD_EGMF 0x12 /* Enable Global Multicast Filter */ | |
380 | #define NCSI_PKT_CMD_DGMF 0x13 /* Disable Global Multicast Filter */ | |
381 | #define NCSI_PKT_CMD_SNFC 0x14 /* Set NCSI Flow Control */ | |
382 | #define NCSI_PKT_CMD_GVI 0x15 /* Get Version ID */ | |
383 | #define NCSI_PKT_CMD_GC 0x16 /* Get Capabilities */ | |
384 | #define NCSI_PKT_CMD_GP 0x17 /* Get Parameters */ | |
385 | #define NCSI_PKT_CMD_GCPS 0x18 /* Get Controller Packet Statistics */ | |
386 | #define NCSI_PKT_CMD_GNS 0x19 /* Get NCSI Statistics */ | |
387 | #define NCSI_PKT_CMD_GNPTS 0x1a /* Get NCSI Pass-throu Statistics */ | |
388 | #define NCSI_PKT_CMD_GPS 0x1b /* Get package status */ | |
389 | #define NCSI_PKT_CMD_OEM 0x50 /* OEM */ | |
390 | #define NCSI_PKT_CMD_PLDM 0x51 /* PLDM request over NCSI over RBT */ | |
391 | #define NCSI_PKT_CMD_GPUUID 0x52 /* Get package UUID */ | |
392 | ||
393 | /* NCSI packet responses */ | |
394 | #define NCSI_PKT_RSP_CIS (NCSI_PKT_CMD_CIS + 0x80) | |
395 | #define NCSI_PKT_RSP_SP (NCSI_PKT_CMD_SP + 0x80) | |
396 | #define NCSI_PKT_RSP_DP (NCSI_PKT_CMD_DP + 0x80) | |
397 | #define NCSI_PKT_RSP_EC (NCSI_PKT_CMD_EC + 0x80) | |
398 | #define NCSI_PKT_RSP_DC (NCSI_PKT_CMD_DC + 0x80) | |
399 | #define NCSI_PKT_RSP_RC (NCSI_PKT_CMD_RC + 0x80) | |
400 | #define NCSI_PKT_RSP_ECNT (NCSI_PKT_CMD_ECNT + 0x80) | |
401 | #define NCSI_PKT_RSP_DCNT (NCSI_PKT_CMD_DCNT + 0x80) | |
402 | #define NCSI_PKT_RSP_AE (NCSI_PKT_CMD_AE + 0x80) | |
403 | #define NCSI_PKT_RSP_SL (NCSI_PKT_CMD_SL + 0x80) | |
404 | #define NCSI_PKT_RSP_GLS (NCSI_PKT_CMD_GLS + 0x80) | |
405 | #define NCSI_PKT_RSP_SVF (NCSI_PKT_CMD_SVF + 0x80) | |
406 | #define NCSI_PKT_RSP_EV (NCSI_PKT_CMD_EV + 0x80) | |
407 | #define NCSI_PKT_RSP_DV (NCSI_PKT_CMD_DV + 0x80) | |
408 | #define NCSI_PKT_RSP_SMA (NCSI_PKT_CMD_SMA + 0x80) | |
409 | #define NCSI_PKT_RSP_EBF (NCSI_PKT_CMD_EBF + 0x80) | |
410 | #define NCSI_PKT_RSP_DBF (NCSI_PKT_CMD_DBF + 0x80) | |
411 | #define NCSI_PKT_RSP_EGMF (NCSI_PKT_CMD_EGMF + 0x80) | |
412 | #define NCSI_PKT_RSP_DGMF (NCSI_PKT_CMD_DGMF + 0x80) | |
413 | #define NCSI_PKT_RSP_SNFC (NCSI_PKT_CMD_SNFC + 0x80) | |
414 | #define NCSI_PKT_RSP_GVI (NCSI_PKT_CMD_GVI + 0x80) | |
415 | #define NCSI_PKT_RSP_GC (NCSI_PKT_CMD_GC + 0x80) | |
416 | #define NCSI_PKT_RSP_GP (NCSI_PKT_CMD_GP + 0x80) | |
417 | #define NCSI_PKT_RSP_GCPS (NCSI_PKT_CMD_GCPS + 0x80) | |
418 | #define NCSI_PKT_RSP_GNS (NCSI_PKT_CMD_GNS + 0x80) | |
419 | #define NCSI_PKT_RSP_GNPTS (NCSI_PKT_CMD_GNPTS + 0x80) | |
420 | #define NCSI_PKT_RSP_GPS (NCSI_PKT_CMD_GPS + 0x80) | |
421 | #define NCSI_PKT_RSP_OEM (NCSI_PKT_CMD_OEM + 0x80) | |
422 | #define NCSI_PKT_RSP_PLDM (NCSI_PKT_CMD_PLDM + 0x80) | |
423 | #define NCSI_PKT_RSP_GPUUID (NCSI_PKT_CMD_GPUUID + 0x80) | |
424 | ||
425 | /* NCSI response code/reason */ | |
426 | #define NCSI_PKT_RSP_C_COMPLETED 0x0000 /* Command Completed */ | |
427 | #define NCSI_PKT_RSP_C_FAILED 0x0001 /* Command Failed */ | |
428 | #define NCSI_PKT_RSP_C_UNAVAILABLE 0x0002 /* Command Unavailable */ | |
429 | #define NCSI_PKT_RSP_C_UNSUPPORTED 0x0003 /* Command Unsupported */ | |
430 | #define NCSI_PKT_RSP_R_NO_ERROR 0x0000 /* No Error */ | |
431 | #define NCSI_PKT_RSP_R_INTERFACE 0x0001 /* Interface not ready */ | |
432 | #define NCSI_PKT_RSP_R_PARAM 0x0002 /* Invalid Parameter */ | |
433 | #define NCSI_PKT_RSP_R_CHANNEL 0x0003 /* Channel not Ready */ | |
434 | #define NCSI_PKT_RSP_R_PACKAGE 0x0004 /* Package not Ready */ | |
435 | #define NCSI_PKT_RSP_R_LENGTH 0x0005 /* Invalid payload length */ | |
436 | #define NCSI_PKT_RSP_R_UNKNOWN 0x7fff /* Command type unsupported */ | |
437 | ||
438 | /* NCSI AEN packet type */ | |
439 | #define NCSI_PKT_AEN 0xFF /* AEN Packet */ | |
440 | #define NCSI_PKT_AEN_LSC 0x00 /* Link status change */ | |
441 | #define NCSI_PKT_AEN_CR 0x01 /* Configuration required */ | |
442 | #define NCSI_PKT_AEN_HNCDSC 0x02 /* HNC driver status change */ | |
443 | ||
444 | #endif /* NCSI_PKT_H */ |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * NC-SI (Network Controller Sideband Interface) "echo" model | |
3 | * | |
4 | * Copyright (C) 2016-2018 IBM Corp. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the above | |
11 | * copyright notice, this list of conditions and the following | |
12 | * disclaimer. | |
13 | * | |
14 | * 2. Redistributions in binary form must reproduce the above | |
15 | * copyright notice, this list of conditions and the following | |
16 | * disclaimer in the documentation and/or other materials provided | |
17 | * with the distribution. | |
18 | * | |
19 | * 3. Neither the name of the copyright holder nor the names of its | |
20 | * contributors may be used to endorse or promote products derived | |
21 | * from this software without specific prior written permission. | |
22 | * | |
23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
34 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
35 | */ | |
36 | #include "slirp.h" | |
37 | ||
38 | #include "ncsi-pkt.h" | |
39 | ||
40 | static uint32_t ncsi_calculate_checksum(uint16_t *data, int len) | |
41 | { | |
42 | uint32_t checksum = 0; | |
43 | int i; | |
44 | ||
45 | /* | |
46 | * 32-bit unsigned sum of the NC-SI packet header and NC-SI packet | |
47 | * payload interpreted as a series of 16-bit unsigned integer values. | |
48 | */ | |
49 | for (i = 0; i < len; i++) { | |
50 | checksum += htons(data[i]); | |
51 | } | |
52 | ||
53 | checksum = (~checksum + 1); | |
54 | return checksum; | |
55 | } | |
56 | ||
57 | /* Get Capabilities */ | |
58 | static int ncsi_rsp_handler_gc(struct ncsi_rsp_pkt_hdr *rnh) | |
59 | { | |
60 | struct ncsi_rsp_gc_pkt *rsp = (struct ncsi_rsp_gc_pkt *)rnh; | |
61 | ||
62 | rsp->cap = htonl(~0); | |
63 | rsp->bc_cap = htonl(~0); | |
64 | rsp->mc_cap = htonl(~0); | |
65 | rsp->buf_cap = htonl(~0); | |
66 | rsp->aen_cap = htonl(~0); | |
67 | rsp->vlan_mode = 0xff; | |
68 | rsp->uc_cnt = 2; | |
69 | return 0; | |
70 | } | |
71 | ||
72 | /* Get Link status */ | |
73 | static int ncsi_rsp_handler_gls(struct ncsi_rsp_pkt_hdr *rnh) | |
74 | { | |
75 | struct ncsi_rsp_gls_pkt *rsp = (struct ncsi_rsp_gls_pkt *)rnh; | |
76 | ||
77 | rsp->status = htonl(0x1); | |
78 | return 0; | |
79 | } | |
80 | ||
81 | /* Get Parameters */ | |
82 | static int ncsi_rsp_handler_gp(struct ncsi_rsp_pkt_hdr *rnh) | |
83 | { | |
84 | struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *)rnh; | |
85 | ||
86 | /* no MAC address filters or VLAN filters on the channel */ | |
87 | rsp->mac_cnt = 0; | |
88 | rsp->mac_enable = 0; | |
89 | rsp->vlan_cnt = 0; | |
90 | rsp->vlan_enable = 0; | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static const struct ncsi_rsp_handler { | |
96 | unsigned char type; | |
97 | int payload; | |
98 | int (*handler)(struct ncsi_rsp_pkt_hdr *rnh); | |
99 | } ncsi_rsp_handlers[] = { { NCSI_PKT_RSP_CIS, 4, NULL }, | |
100 | { NCSI_PKT_RSP_SP, 4, NULL }, | |
101 | { NCSI_PKT_RSP_DP, 4, NULL }, | |
102 | { NCSI_PKT_RSP_EC, 4, NULL }, | |
103 | { NCSI_PKT_RSP_DC, 4, NULL }, | |
104 | { NCSI_PKT_RSP_RC, 4, NULL }, | |
105 | { NCSI_PKT_RSP_ECNT, 4, NULL }, | |
106 | { NCSI_PKT_RSP_DCNT, 4, NULL }, | |
107 | { NCSI_PKT_RSP_AE, 4, NULL }, | |
108 | { NCSI_PKT_RSP_SL, 4, NULL }, | |
109 | { NCSI_PKT_RSP_GLS, 16, ncsi_rsp_handler_gls }, | |
110 | { NCSI_PKT_RSP_SVF, 4, NULL }, | |
111 | { NCSI_PKT_RSP_EV, 4, NULL }, | |
112 | { NCSI_PKT_RSP_DV, 4, NULL }, | |
113 | { NCSI_PKT_RSP_SMA, 4, NULL }, | |
114 | { NCSI_PKT_RSP_EBF, 4, NULL }, | |
115 | { NCSI_PKT_RSP_DBF, 4, NULL }, | |
116 | { NCSI_PKT_RSP_EGMF, 4, NULL }, | |
117 | { NCSI_PKT_RSP_DGMF, 4, NULL }, | |
118 | { NCSI_PKT_RSP_SNFC, 4, NULL }, | |
119 | { NCSI_PKT_RSP_GVI, 40, NULL }, | |
120 | { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, | |
121 | { NCSI_PKT_RSP_GP, 40, ncsi_rsp_handler_gp }, | |
122 | { NCSI_PKT_RSP_GCPS, 172, NULL }, | |
123 | { NCSI_PKT_RSP_GNS, 172, NULL }, | |
124 | { NCSI_PKT_RSP_GNPTS, 172, NULL }, | |
125 | { NCSI_PKT_RSP_GPS, 8, NULL }, | |
126 | { NCSI_PKT_RSP_OEM, 0, NULL }, | |
127 | { NCSI_PKT_RSP_PLDM, 0, NULL }, | |
128 | { NCSI_PKT_RSP_GPUUID, 20, NULL } }; | |
129 | ||
130 | /* | |
131 | * packet format : ncsi header + payload + checksum | |
132 | */ | |
133 | #define NCSI_MAX_PAYLOAD 172 | |
134 | #define NCSI_MAX_LEN (sizeof(struct ncsi_pkt_hdr) + NCSI_MAX_PAYLOAD + 4) | |
135 | ||
136 | void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) | |
137 | { | |
138 | struct ncsi_pkt_hdr *nh = (struct ncsi_pkt_hdr *)(pkt + ETH_HLEN); | |
139 | uint8_t ncsi_reply[ETH_HLEN + NCSI_MAX_LEN]; | |
140 | struct ethhdr *reh = (struct ethhdr *)ncsi_reply; | |
141 | struct ncsi_rsp_pkt_hdr *rnh = | |
142 | (struct ncsi_rsp_pkt_hdr *)(ncsi_reply + ETH_HLEN); | |
143 | const struct ncsi_rsp_handler *handler = NULL; | |
144 | int i; | |
145 | int ncsi_rsp_len = sizeof(*nh); | |
146 | uint32_t checksum; | |
147 | uint32_t *pchecksum; | |
148 | ||
149 | memset(ncsi_reply, 0, sizeof(ncsi_reply)); | |
150 | ||
151 | memset(reh->h_dest, 0xff, ETH_ALEN); | |
152 | memset(reh->h_source, 0xff, ETH_ALEN); | |
153 | reh->h_proto = htons(ETH_P_NCSI); | |
154 | ||
155 | for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_handlers); i++) { | |
156 | if (ncsi_rsp_handlers[i].type == nh->type + 0x80) { | |
157 | handler = &ncsi_rsp_handlers[i]; | |
158 | break; | |
159 | } | |
160 | } | |
161 | ||
162 | rnh->common.mc_id = nh->mc_id; | |
163 | rnh->common.revision = NCSI_PKT_REVISION; | |
164 | rnh->common.id = nh->id; | |
165 | rnh->common.type = nh->type + 0x80; | |
166 | rnh->common.channel = nh->channel; | |
167 | ||
168 | if (handler) { | |
169 | rnh->common.length = htons(handler->payload); | |
170 | rnh->code = htons(NCSI_PKT_RSP_C_COMPLETED); | |
171 | rnh->reason = htons(NCSI_PKT_RSP_R_NO_ERROR); | |
172 | ||
173 | if (handler->handler) { | |
174 | /* TODO: handle errors */ | |
175 | handler->handler(rnh); | |
176 | } | |
177 | ncsi_rsp_len += handler->payload; | |
178 | } else { | |
179 | rnh->common.length = 0; | |
180 | rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); | |
181 | rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); | |
182 | } | |
183 | ||
184 | /* Add the optional checksum at the end of the frame. */ | |
185 | checksum = ncsi_calculate_checksum((uint16_t *)rnh, ncsi_rsp_len); | |
186 | pchecksum = (uint32_t *)((void *)rnh + ncsi_rsp_len); | |
187 | *pchecksum = htonl(checksum); | |
188 | ncsi_rsp_len += 4; | |
189 | ||
190 | slirp_send_packet_all(slirp, ncsi_reply, ETH_HLEN + ncsi_rsp_len); | |
191 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. | |
4 | */ | |
5 | ||
6 | #include "slirp.h" | |
7 | ||
8 | void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, | |
9 | uint8_t ethaddr[ETH_ALEN]) | |
10 | { | |
11 | char addrstr[INET6_ADDRSTRLEN]; | |
12 | NdpTable *ndp_table = &slirp->ndp_table; | |
13 | int i; | |
14 | ||
15 | inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); | |
16 | ||
17 | DEBUG_CALL("ndp_table_add"); | |
18 | DEBUG_ARG("ip = %s", addrstr); | |
19 | DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", ethaddr[0], ethaddr[1], | |
20 | ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5]); | |
21 | ||
22 | if (IN6_IS_ADDR_MULTICAST(&ip_addr) || in6_zero(&ip_addr)) { | |
23 | /* Do not register multicast or unspecified addresses */ | |
24 | DEBUG_CALL(" abort: do not register multicast or unspecified address"); | |
25 | return; | |
26 | } | |
27 | ||
28 | /* Search for an entry */ | |
29 | for (i = 0; i < NDP_TABLE_SIZE; i++) { | |
30 | if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { | |
31 | DEBUG_CALL(" already in table: update the entry"); | |
32 | /* Update the entry */ | |
33 | memcpy(ndp_table->table[i].eth_addr, ethaddr, ETH_ALEN); | |
34 | return; | |
35 | } | |
36 | } | |
37 | ||
38 | /* No entry found, create a new one */ | |
39 | DEBUG_CALL(" create new entry"); | |
40 | ndp_table->table[ndp_table->next_victim].ip_addr = ip_addr; | |
41 | memcpy(ndp_table->table[ndp_table->next_victim].eth_addr, ethaddr, | |
42 | ETH_ALEN); | |
43 | ndp_table->next_victim = (ndp_table->next_victim + 1) % NDP_TABLE_SIZE; | |
44 | } | |
45 | ||
46 | bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, | |
47 | uint8_t out_ethaddr[ETH_ALEN]) | |
48 | { | |
49 | char addrstr[INET6_ADDRSTRLEN]; | |
50 | NdpTable *ndp_table = &slirp->ndp_table; | |
51 | int i; | |
52 | ||
53 | inet_ntop(AF_INET6, &(ip_addr), addrstr, INET6_ADDRSTRLEN); | |
54 | ||
55 | DEBUG_CALL("ndp_table_search"); | |
56 | DEBUG_ARG("ip = %s", addrstr); | |
57 | ||
58 | assert(!in6_zero(&ip_addr)); | |
59 | ||
60 | /* Multicast address: fec0::abcd:efgh/8 -> 33:33:ab:cd:ef:gh */ | |
61 | if (IN6_IS_ADDR_MULTICAST(&ip_addr)) { | |
62 | out_ethaddr[0] = 0x33; | |
63 | out_ethaddr[1] = 0x33; | |
64 | out_ethaddr[2] = ip_addr.s6_addr[12]; | |
65 | out_ethaddr[3] = ip_addr.s6_addr[13]; | |
66 | out_ethaddr[4] = ip_addr.s6_addr[14]; | |
67 | out_ethaddr[5] = ip_addr.s6_addr[15]; | |
68 | DEBUG_ARG("multicast addr = %02x:%02x:%02x:%02x:%02x:%02x", | |
69 | out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], | |
70 | out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); | |
71 | return 1; | |
72 | } | |
73 | ||
74 | for (i = 0; i < NDP_TABLE_SIZE; i++) { | |
75 | if (in6_equal(&ndp_table->table[i].ip_addr, &ip_addr)) { | |
76 | memcpy(out_ethaddr, ndp_table->table[i].eth_addr, ETH_ALEN); | |
77 | DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x", | |
78 | out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], | |
79 | out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); | |
80 | return 1; | |
81 | } | |
82 | } | |
83 | ||
84 | DEBUG_CALL(" ip not found in table"); | |
85 | return 0; | |
86 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #include "slirp.h" | |
6 | ||
7 | static void sbappendsb(struct sbuf *sb, struct mbuf *m); | |
8 | ||
9 | void sbfree(struct sbuf *sb) | |
10 | { | |
11 | free(sb->sb_data); | |
12 | } | |
13 | ||
14 | bool sbdrop(struct sbuf *sb, int num) | |
15 | { | |
16 | int limit = sb->sb_datalen / 2; | |
17 | ||
18 | /* | |
19 | * We can only drop how much we have | |
20 | * This should never succeed | |
21 | */ | |
22 | if (num > sb->sb_cc) | |
23 | num = sb->sb_cc; | |
24 | sb->sb_cc -= num; | |
25 | sb->sb_rptr += num; | |
26 | if (sb->sb_rptr >= sb->sb_data + sb->sb_datalen) | |
27 | sb->sb_rptr -= sb->sb_datalen; | |
28 | ||
29 | if (sb->sb_cc < limit && sb->sb_cc + num >= limit) { | |
30 | return true; | |
31 | } | |
32 | ||
33 | return false; | |
34 | } | |
35 | ||
36 | void sbreserve(struct sbuf *sb, int size) | |
37 | { | |
38 | if (sb->sb_data) { | |
39 | /* Already alloced, realloc if necessary */ | |
40 | if (sb->sb_datalen != size) { | |
41 | sb->sb_wptr = sb->sb_rptr = sb->sb_data = | |
42 | (char *)realloc(sb->sb_data, size); | |
43 | sb->sb_cc = 0; | |
44 | if (sb->sb_wptr) | |
45 | sb->sb_datalen = size; | |
46 | else | |
47 | sb->sb_datalen = 0; | |
48 | } | |
49 | } else { | |
50 | sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size); | |
51 | sb->sb_cc = 0; | |
52 | if (sb->sb_wptr) | |
53 | sb->sb_datalen = size; | |
54 | else | |
55 | sb->sb_datalen = 0; | |
56 | } | |
57 | } | |
58 | ||
59 | /* | |
60 | * Try and write() to the socket, whatever doesn't get written | |
61 | * append to the buffer... for a host with a fast net connection, | |
62 | * this prevents an unnecessary copy of the data | |
63 | * (the socket is non-blocking, so we won't hang) | |
64 | */ | |
65 | void sbappend(struct socket *so, struct mbuf *m) | |
66 | { | |
67 | int ret = 0; | |
68 | ||
69 | DEBUG_CALL("sbappend"); | |
70 | DEBUG_ARG("so = %p", so); | |
71 | DEBUG_ARG("m = %p", m); | |
72 | DEBUG_ARG("m->m_len = %d", m->m_len); | |
73 | ||
74 | /* Shouldn't happen, but... e.g. foreign host closes connection */ | |
75 | if (m->m_len <= 0) { | |
76 | m_free(m); | |
77 | return; | |
78 | } | |
79 | ||
80 | /* | |
81 | * If there is urgent data, call sosendoob | |
82 | * if not all was sent, sowrite will take care of the rest | |
83 | * (The rest of this function is just an optimisation) | |
84 | */ | |
85 | if (so->so_urgc) { | |
86 | sbappendsb(&so->so_rcv, m); | |
87 | m_free(m); | |
88 | (void)sosendoob(so); | |
89 | return; | |
90 | } | |
91 | ||
92 | /* | |
93 | * We only write if there's nothing in the buffer, | |
94 | * ottherwise it'll arrive out of order, and hence corrupt | |
95 | */ | |
96 | if (!so->so_rcv.sb_cc) | |
97 | ret = slirp_send(so, m->m_data, m->m_len, 0); | |
98 | ||
99 | if (ret <= 0) { | |
100 | /* | |
101 | * Nothing was written | |
102 | * It's possible that the socket has closed, but | |
103 | * we don't need to check because if it has closed, | |
104 | * it will be detected in the normal way by soread() | |
105 | */ | |
106 | sbappendsb(&so->so_rcv, m); | |
107 | } else if (ret != m->m_len) { | |
108 | /* | |
109 | * Something was written, but not everything.. | |
110 | * sbappendsb the rest | |
111 | */ | |
112 | m->m_len -= ret; | |
113 | m->m_data += ret; | |
114 | sbappendsb(&so->so_rcv, m); | |
115 | } /* else */ | |
116 | /* Whatever happened, we free the mbuf */ | |
117 | m_free(m); | |
118 | } | |
119 | ||
120 | /* | |
121 | * Copy the data from m into sb | |
122 | * The caller is responsible to make sure there's enough room | |
123 | */ | |
124 | static void sbappendsb(struct sbuf *sb, struct mbuf *m) | |
125 | { | |
126 | int len, n, nn; | |
127 | ||
128 | len = m->m_len; | |
129 | ||
130 | if (sb->sb_wptr < sb->sb_rptr) { | |
131 | n = sb->sb_rptr - sb->sb_wptr; | |
132 | if (n > len) | |
133 | n = len; | |
134 | memcpy(sb->sb_wptr, m->m_data, n); | |
135 | } else { | |
136 | /* Do the right edge first */ | |
137 | n = sb->sb_data + sb->sb_datalen - sb->sb_wptr; | |
138 | if (n > len) | |
139 | n = len; | |
140 | memcpy(sb->sb_wptr, m->m_data, n); | |
141 | len -= n; | |
142 | if (len) { | |
143 | /* Now the left edge */ | |
144 | nn = sb->sb_rptr - sb->sb_data; | |
145 | if (nn > len) | |
146 | nn = len; | |
147 | memcpy(sb->sb_data, m->m_data + n, nn); | |
148 | n += nn; | |
149 | } | |
150 | } | |
151 | ||
152 | sb->sb_cc += n; | |
153 | sb->sb_wptr += n; | |
154 | if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen) | |
155 | sb->sb_wptr -= sb->sb_datalen; | |
156 | } | |
157 | ||
158 | /* | |
159 | * Copy data from sbuf to a normal, straight buffer | |
160 | * Don't update the sbuf rptr, this will be | |
161 | * done in sbdrop when the data is acked | |
162 | */ | |
163 | void sbcopy(struct sbuf *sb, int off, int len, char *to) | |
164 | { | |
165 | char *from; | |
166 | ||
167 | from = sb->sb_rptr + off; | |
168 | if (from >= sb->sb_data + sb->sb_datalen) | |
169 | from -= sb->sb_datalen; | |
170 | ||
171 | if (from < sb->sb_wptr) { | |
172 | if (len > sb->sb_cc) | |
173 | len = sb->sb_cc; | |
174 | memcpy(to, from, len); | |
175 | } else { | |
176 | /* re-use off */ | |
177 | off = (sb->sb_data + sb->sb_datalen) - from; | |
178 | if (off > len) | |
179 | off = len; | |
180 | memcpy(to, from, off); | |
181 | len -= off; | |
182 | if (len) | |
183 | memcpy(to + off, sb->sb_data, len); | |
184 | } | |
185 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef SBUF_H | |
6 | #define SBUF_H | |
7 | ||
8 | #define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) | |
9 | ||
10 | struct sbuf { | |
11 | uint32_t sb_cc; /* actual chars in buffer */ | |
12 | uint32_t sb_datalen; /* Length of data */ | |
13 | char *sb_wptr; /* write pointer. points to where the next | |
14 | * bytes should be written in the sbuf */ | |
15 | char *sb_rptr; /* read pointer. points to where the next | |
16 | * byte should be read from the sbuf */ | |
17 | char *sb_data; /* Actual data */ | |
18 | }; | |
19 | ||
20 | void sbfree(struct sbuf *); | |
21 | bool sbdrop(struct sbuf *, int); | |
22 | void sbreserve(struct sbuf *, int); | |
23 | void sbappend(struct socket *, struct mbuf *); | |
24 | void sbcopy(struct sbuf *, int, int, char *); | |
25 | ||
26 | #endif |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * libslirp glue | |
3 | * | |
4 | * Copyright (c) 2004-2008 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "slirp.h" | |
25 | ||
26 | ||
27 | #ifndef _WIN32 | |
28 | #include <net/if.h> | |
29 | #endif | |
30 | ||
31 | int slirp_debug; | |
32 | ||
33 | /* Define to 1 if you want KEEPALIVE timers */ | |
34 | bool slirp_do_keepalive; | |
35 | ||
36 | /* host loopback address */ | |
37 | struct in_addr loopback_addr; | |
38 | /* host loopback network mask */ | |
39 | unsigned long loopback_mask; | |
40 | ||
41 | /* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ | |
42 | static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00, | |
43 | 0x00, 0x00, 0x00 }; | |
44 | ||
45 | unsigned curtime; | |
46 | ||
47 | static struct in_addr dns_addr; | |
48 | #ifndef _WIN32 | |
49 | static struct in6_addr dns6_addr; | |
50 | #endif | |
51 | static unsigned dns_addr_time; | |
52 | #ifndef _WIN32 | |
53 | static unsigned dns6_addr_time; | |
54 | #endif | |
55 | ||
56 | #define TIMEOUT_FAST 2 /* milliseconds */ | |
57 | #define TIMEOUT_SLOW 499 /* milliseconds */ | |
58 | /* for the aging of certain requests like DNS */ | |
59 | #define TIMEOUT_DEFAULT 1000 /* milliseconds */ | |
60 | ||
61 | #ifdef _WIN32 | |
62 | ||
63 | int get_dns_addr(struct in_addr *pdns_addr) | |
64 | { | |
65 | FIXED_INFO *FixedInfo = NULL; | |
66 | ULONG BufLen; | |
67 | DWORD ret; | |
68 | IP_ADDR_STRING *pIPAddr; | |
69 | struct in_addr tmp_addr; | |
70 | ||
71 | if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { | |
72 | *pdns_addr = dns_addr; | |
73 | return 0; | |
74 | } | |
75 | ||
76 | FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); | |
77 | BufLen = sizeof(FIXED_INFO); | |
78 | ||
79 | if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { | |
80 | if (FixedInfo) { | |
81 | GlobalFree(FixedInfo); | |
82 | FixedInfo = NULL; | |
83 | } | |
84 | FixedInfo = GlobalAlloc(GPTR, BufLen); | |
85 | } | |
86 | ||
87 | if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { | |
88 | printf("GetNetworkParams failed. ret = %08x\n", (unsigned)ret); | |
89 | if (FixedInfo) { | |
90 | GlobalFree(FixedInfo); | |
91 | FixedInfo = NULL; | |
92 | } | |
93 | return -1; | |
94 | } | |
95 | ||
96 | pIPAddr = &(FixedInfo->DnsServerList); | |
97 | inet_aton(pIPAddr->IpAddress.String, &tmp_addr); | |
98 | *pdns_addr = tmp_addr; | |
99 | dns_addr = tmp_addr; | |
100 | dns_addr_time = curtime; | |
101 | if (FixedInfo) { | |
102 | GlobalFree(FixedInfo); | |
103 | FixedInfo = NULL; | |
104 | } | |
105 | return 0; | |
106 | } | |
107 | ||
108 | int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) | |
109 | { | |
110 | return -1; | |
111 | } | |
112 | ||
113 | static void winsock_cleanup(void) | |
114 | { | |
115 | WSACleanup(); | |
116 | } | |
117 | ||
118 | #else | |
119 | ||
120 | static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, | |
121 | socklen_t addrlen, struct stat *cached_stat, | |
122 | unsigned *cached_time) | |
123 | { | |
124 | struct stat old_stat; | |
125 | if (curtime - *cached_time < TIMEOUT_DEFAULT) { | |
126 | memcpy(pdns_addr, cached_addr, addrlen); | |
127 | return 0; | |
128 | } | |
129 | old_stat = *cached_stat; | |
130 | if (stat("/etc/resolv.conf", cached_stat) != 0) { | |
131 | return -1; | |
132 | } | |
133 | if (cached_stat->st_dev == old_stat.st_dev && | |
134 | cached_stat->st_ino == old_stat.st_ino && | |
135 | cached_stat->st_size == old_stat.st_size && | |
136 | cached_stat->st_mtime == old_stat.st_mtime) { | |
137 | memcpy(pdns_addr, cached_addr, addrlen); | |
138 | return 0; | |
139 | } | |
140 | return 1; | |
141 | } | |
142 | ||
143 | static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr, | |
144 | socklen_t addrlen, uint32_t *scope_id, | |
145 | unsigned *cached_time) | |
146 | { | |
147 | char buff[512]; | |
148 | char buff2[257]; | |
149 | FILE *f; | |
150 | int found = 0; | |
151 | void *tmp_addr = alloca(addrlen); | |
152 | unsigned if_index; | |
153 | ||
154 | f = fopen("/etc/resolv.conf", "r"); | |
155 | if (!f) | |
156 | return -1; | |
157 | ||
158 | DEBUG_MISC("IP address of your DNS(s):"); | |
159 | while (fgets(buff, 512, f) != NULL) { | |
160 | if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { | |
161 | char *c = strchr(buff2, '%'); | |
162 | if (c) { | |
163 | if_index = if_nametoindex(c + 1); | |
164 | *c = '\0'; | |
165 | } else { | |
166 | if_index = 0; | |
167 | } | |
168 | ||
169 | if (!inet_pton(af, buff2, tmp_addr)) { | |
170 | continue; | |
171 | } | |
172 | /* If it's the first one, set it to dns_addr */ | |
173 | if (!found) { | |
174 | memcpy(pdns_addr, tmp_addr, addrlen); | |
175 | memcpy(cached_addr, tmp_addr, addrlen); | |
176 | if (scope_id) { | |
177 | *scope_id = if_index; | |
178 | } | |
179 | *cached_time = curtime; | |
180 | } | |
181 | ||
182 | if (++found > 3) { | |
183 | DEBUG_MISC(" (more)"); | |
184 | break; | |
185 | } else if (slirp_debug & DBG_MISC) { | |
186 | char s[INET6_ADDRSTRLEN]; | |
187 | const char *res = inet_ntop(af, tmp_addr, s, sizeof(s)); | |
188 | if (!res) { | |
189 | res = " (string conversion error)"; | |
190 | } | |
191 | DEBUG_MISC(" %s", res); | |
192 | } | |
193 | } | |
194 | } | |
195 | fclose(f); | |
196 | if (!found) | |
197 | return -1; | |
198 | return 0; | |
199 | } | |
200 | ||
201 | int get_dns_addr(struct in_addr *pdns_addr) | |
202 | { | |
203 | static struct stat dns_addr_stat; | |
204 | ||
205 | if (dns_addr.s_addr != 0) { | |
206 | int ret; | |
207 | ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr), | |
208 | &dns_addr_stat, &dns_addr_time); | |
209 | if (ret <= 0) { | |
210 | return ret; | |
211 | } | |
212 | } | |
213 | return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr, | |
214 | sizeof(dns_addr), NULL, &dns_addr_time); | |
215 | } | |
216 | ||
217 | int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) | |
218 | { | |
219 | static struct stat dns6_addr_stat; | |
220 | ||
221 | if (!in6_zero(&dns6_addr)) { | |
222 | int ret; | |
223 | ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), | |
224 | &dns6_addr_stat, &dns6_addr_time); | |
225 | if (ret <= 0) { | |
226 | return ret; | |
227 | } | |
228 | } | |
229 | return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr, | |
230 | sizeof(dns6_addr), scope_id, | |
231 | &dns6_addr_time); | |
232 | } | |
233 | ||
234 | #endif | |
235 | ||
236 | static void slirp_init_once(void) | |
237 | { | |
238 | static int initialized; | |
239 | const char *debug; | |
240 | #ifdef _WIN32 | |
241 | WSADATA Data; | |
242 | #endif | |
243 | ||
244 | if (initialized) { | |
245 | return; | |
246 | } | |
247 | initialized = 1; | |
248 | ||
249 | #ifdef _WIN32 | |
250 | WSAStartup(MAKEWORD(2, 0), &Data); | |
251 | atexit(winsock_cleanup); | |
252 | #endif | |
253 | ||
254 | loopback_addr.s_addr = htonl(INADDR_LOOPBACK); | |
255 | loopback_mask = htonl(IN_CLASSA_NET); | |
256 | ||
257 | debug = g_getenv("SLIRP_DEBUG"); | |
258 | if (debug) { | |
259 | const GDebugKey keys[] = { | |
260 | { "call", DBG_CALL }, | |
261 | { "misc", DBG_MISC }, | |
262 | { "error", DBG_ERROR }, | |
263 | { "tftp", DBG_TFTP }, | |
264 | }; | |
265 | slirp_debug = g_parse_debug_string(debug, keys, G_N_ELEMENTS(keys)); | |
266 | } | |
267 | } | |
268 | ||
269 | Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque) | |
270 | { | |
271 | Slirp *slirp; | |
272 | ||
273 | g_return_val_if_fail(cfg != NULL, NULL); | |
274 | g_return_val_if_fail(cfg->version >= SLIRP_CONFIG_VERSION_MIN, NULL); | |
275 | g_return_val_if_fail(cfg->version <= SLIRP_CONFIG_VERSION_MAX, NULL); | |
276 | g_return_val_if_fail(cfg->if_mtu >= IF_MTU_MIN || cfg->if_mtu == 0, NULL); | |
277 | g_return_val_if_fail(cfg->if_mtu <= IF_MTU_MAX, NULL); | |
278 | g_return_val_if_fail(cfg->if_mru >= IF_MRU_MIN || cfg->if_mru == 0, NULL); | |
279 | g_return_val_if_fail(cfg->if_mru <= IF_MRU_MAX, NULL); | |
280 | ||
281 | slirp = g_malloc0(sizeof(Slirp)); | |
282 | ||
283 | slirp_init_once(); | |
284 | ||
285 | slirp->opaque = opaque; | |
286 | slirp->cb = callbacks; | |
287 | slirp->grand = g_rand_new(); | |
288 | slirp->restricted = cfg->restricted; | |
289 | ||
290 | slirp->in_enabled = cfg->in_enabled; | |
291 | slirp->in6_enabled = cfg->in6_enabled; | |
292 | ||
293 | if_init(slirp); | |
294 | ip_init(slirp); | |
295 | ip6_init(slirp); | |
296 | ||
297 | m_init(slirp); | |
298 | ||
299 | slirp->vnetwork_addr = cfg->vnetwork; | |
300 | slirp->vnetwork_mask = cfg->vnetmask; | |
301 | slirp->vhost_addr = cfg->vhost; | |
302 | slirp->vprefix_addr6 = cfg->vprefix_addr6; | |
303 | slirp->vprefix_len = cfg->vprefix_len; | |
304 | slirp->vhost_addr6 = cfg->vhost6; | |
305 | if (cfg->vhostname) { | |
306 | slirp_pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), | |
307 | cfg->vhostname); | |
308 | } | |
309 | slirp->tftp_prefix = g_strdup(cfg->tftp_path); | |
310 | slirp->bootp_filename = g_strdup(cfg->bootfile); | |
311 | slirp->vdomainname = g_strdup(cfg->vdomainname); | |
312 | slirp->vdhcp_startaddr = cfg->vdhcp_start; | |
313 | slirp->vnameserver_addr = cfg->vnameserver; | |
314 | slirp->vnameserver_addr6 = cfg->vnameserver6; | |
315 | slirp->tftp_server_name = g_strdup(cfg->tftp_server_name); | |
316 | ||
317 | if (cfg->vdnssearch) { | |
318 | translate_dnssearch(slirp, cfg->vdnssearch); | |
319 | } | |
320 | slirp->if_mtu = cfg->if_mtu == 0 ? IF_MTU_DEFAULT : cfg->if_mtu; | |
321 | slirp->if_mru = cfg->if_mru == 0 ? IF_MRU_DEFAULT : cfg->if_mru; | |
322 | slirp->disable_host_loopback = cfg->disable_host_loopback; | |
323 | slirp->enable_emu = cfg->enable_emu; | |
324 | ||
325 | return slirp; | |
326 | } | |
327 | ||
328 | Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, | |
329 | struct in_addr vnetmask, struct in_addr vhost, | |
330 | bool in6_enabled, struct in6_addr vprefix_addr6, | |
331 | uint8_t vprefix_len, struct in6_addr vhost6, | |
332 | const char *vhostname, const char *tftp_server_name, | |
333 | const char *tftp_path, const char *bootfile, | |
334 | struct in_addr vdhcp_start, struct in_addr vnameserver, | |
335 | struct in6_addr vnameserver6, const char **vdnssearch, | |
336 | const char *vdomainname, const SlirpCb *callbacks, | |
337 | void *opaque) | |
338 | { | |
339 | SlirpConfig cfg; | |
340 | memset(&cfg, 0, sizeof(cfg)); | |
341 | cfg.version = 1; | |
342 | cfg.restricted = restricted; | |
343 | cfg.in_enabled = in_enabled; | |
344 | cfg.vnetwork = vnetwork; | |
345 | cfg.vnetmask = vnetmask; | |
346 | cfg.vhost = vhost; | |
347 | cfg.in6_enabled = in6_enabled; | |
348 | cfg.vprefix_addr6 = vprefix_addr6; | |
349 | cfg.vprefix_len = vprefix_len; | |
350 | cfg.vhost6 = vhost6; | |
351 | cfg.vhostname = vhostname; | |
352 | cfg.tftp_server_name = tftp_server_name; | |
353 | cfg.tftp_path = tftp_path; | |
354 | cfg.bootfile = bootfile; | |
355 | cfg.vdhcp_start = vdhcp_start; | |
356 | cfg.vnameserver = vnameserver; | |
357 | cfg.vnameserver6 = vnameserver6; | |
358 | cfg.vdnssearch = vdnssearch; | |
359 | cfg.vdomainname = vdomainname; | |
360 | return slirp_new(&cfg, callbacks, opaque); | |
361 | } | |
362 | ||
363 | void slirp_cleanup(Slirp *slirp) | |
364 | { | |
365 | struct gfwd_list *e, *next; | |
366 | ||
367 | for (e = slirp->guestfwd_list; e; e = next) { | |
368 | next = e->ex_next; | |
369 | g_free(e->ex_exec); | |
370 | g_free(e); | |
371 | } | |
372 | ||
373 | ip_cleanup(slirp); | |
374 | ip6_cleanup(slirp); | |
375 | m_cleanup(slirp); | |
376 | ||
377 | g_rand_free(slirp->grand); | |
378 | ||
379 | g_free(slirp->vdnssearch); | |
380 | g_free(slirp->tftp_prefix); | |
381 | g_free(slirp->bootp_filename); | |
382 | g_free(slirp->vdomainname); | |
383 | g_free(slirp); | |
384 | } | |
385 | ||
386 | #define CONN_CANFSEND(so) \ | |
387 | (((so)->so_state & (SS_FCANTSENDMORE | SS_ISFCONNECTED)) == SS_ISFCONNECTED) | |
388 | #define CONN_CANFRCV(so) \ | |
389 | (((so)->so_state & (SS_FCANTRCVMORE | SS_ISFCONNECTED)) == SS_ISFCONNECTED) | |
390 | ||
391 | static void slirp_update_timeout(Slirp *slirp, uint32_t *timeout) | |
392 | { | |
393 | uint32_t t; | |
394 | ||
395 | if (*timeout <= TIMEOUT_FAST) { | |
396 | return; | |
397 | } | |
398 | ||
399 | t = MIN(1000, *timeout); | |
400 | ||
401 | /* If we have tcp timeout with slirp, then we will fill @timeout with | |
402 | * more precise value. | |
403 | */ | |
404 | if (slirp->time_fasttimo) { | |
405 | *timeout = TIMEOUT_FAST; | |
406 | return; | |
407 | } | |
408 | if (slirp->do_slowtimo) { | |
409 | t = MIN(TIMEOUT_SLOW, t); | |
410 | } | |
411 | *timeout = t; | |
412 | } | |
413 | ||
414 | void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout, | |
415 | SlirpAddPollCb add_poll, void *opaque) | |
416 | { | |
417 | struct socket *so, *so_next; | |
418 | ||
419 | /* | |
420 | * First, TCP sockets | |
421 | */ | |
422 | ||
423 | /* | |
424 | * *_slowtimo needs calling if there are IP fragments | |
425 | * in the fragment queue, or there are TCP connections active | |
426 | */ | |
427 | slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || | |
428 | (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); | |
429 | ||
430 | for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) { | |
431 | int events = 0; | |
432 | ||
433 | so_next = so->so_next; | |
434 | ||
435 | so->pollfds_idx = -1; | |
436 | ||
437 | /* | |
438 | * See if we need a tcp_fasttimo | |
439 | */ | |
440 | if (slirp->time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) { | |
441 | slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */ | |
442 | } | |
443 | ||
444 | /* | |
445 | * NOFDREF can include still connecting to local-host, | |
446 | * newly socreated() sockets etc. Don't want to select these. | |
447 | */ | |
448 | if (so->so_state & SS_NOFDREF || so->s == -1) { | |
449 | continue; | |
450 | } | |
451 | ||
452 | /* | |
453 | * Set for reading sockets which are accepting | |
454 | */ | |
455 | if (so->so_state & SS_FACCEPTCONN) { | |
456 | so->pollfds_idx = add_poll( | |
457 | so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); | |
458 | continue; | |
459 | } | |
460 | ||
461 | /* | |
462 | * Set for writing sockets which are connecting | |
463 | */ | |
464 | if (so->so_state & SS_ISFCONNECTING) { | |
465 | so->pollfds_idx = | |
466 | add_poll(so->s, SLIRP_POLL_OUT | SLIRP_POLL_ERR, opaque); | |
467 | continue; | |
468 | } | |
469 | ||
470 | /* | |
471 | * Set for writing if we are connected, can send more, and | |
472 | * we have something to send | |
473 | */ | |
474 | if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { | |
475 | events |= SLIRP_POLL_OUT | SLIRP_POLL_ERR; | |
476 | } | |
477 | ||
478 | /* | |
479 | * Set for reading (and urgent data) if we are connected, can | |
480 | * receive more, and we have room for it XXX /2 ? | |
481 | */ | |
482 | if (CONN_CANFRCV(so) && | |
483 | (so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) { | |
484 | events |= SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR | | |
485 | SLIRP_POLL_PRI; | |
486 | } | |
487 | ||
488 | if (events) { | |
489 | so->pollfds_idx = add_poll(so->s, events, opaque); | |
490 | } | |
491 | } | |
492 | ||
493 | /* | |
494 | * UDP sockets | |
495 | */ | |
496 | for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { | |
497 | so_next = so->so_next; | |
498 | ||
499 | so->pollfds_idx = -1; | |
500 | ||
501 | /* | |
502 | * See if it's timed out | |
503 | */ | |
504 | if (so->so_expire) { | |
505 | if (so->so_expire <= curtime) { | |
506 | udp_detach(so); | |
507 | continue; | |
508 | } else { | |
509 | slirp->do_slowtimo = true; /* Let socket expire */ | |
510 | } | |
511 | } | |
512 | ||
513 | /* | |
514 | * When UDP packets are received from over the | |
515 | * link, they're sendto()'d straight away, so | |
516 | * no need for setting for writing | |
517 | * Limit the number of packets queued by this session | |
518 | * to 4. Note that even though we try and limit this | |
519 | * to 4 packets, the session could have more queued | |
520 | * if the packets needed to be fragmented | |
521 | * (XXX <= 4 ?) | |
522 | */ | |
523 | if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { | |
524 | so->pollfds_idx = add_poll( | |
525 | so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); | |
526 | } | |
527 | } | |
528 | ||
529 | /* | |
530 | * ICMP sockets | |
531 | */ | |
532 | for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { | |
533 | so_next = so->so_next; | |
534 | ||
535 | so->pollfds_idx = -1; | |
536 | ||
537 | /* | |
538 | * See if it's timed out | |
539 | */ | |
540 | if (so->so_expire) { | |
541 | if (so->so_expire <= curtime) { | |
542 | icmp_detach(so); | |
543 | continue; | |
544 | } else { | |
545 | slirp->do_slowtimo = true; /* Let socket expire */ | |
546 | } | |
547 | } | |
548 | ||
549 | if (so->so_state & SS_ISFCONNECTED) { | |
550 | so->pollfds_idx = add_poll( | |
551 | so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); | |
552 | } | |
553 | } | |
554 | ||
555 | slirp_update_timeout(slirp, timeout); | |
556 | } | |
557 | ||
558 | void slirp_pollfds_poll(Slirp *slirp, int select_error, | |
559 | SlirpGetREventsCb get_revents, void *opaque) | |
560 | { | |
561 | struct socket *so, *so_next; | |
562 | int ret; | |
563 | ||
564 | curtime = slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS; | |
565 | ||
566 | /* | |
567 | * See if anything has timed out | |
568 | */ | |
569 | if (slirp->time_fasttimo && | |
570 | ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { | |
571 | tcp_fasttimo(slirp); | |
572 | slirp->time_fasttimo = 0; | |
573 | } | |
574 | if (slirp->do_slowtimo && | |
575 | ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { | |
576 | ip_slowtimo(slirp); | |
577 | tcp_slowtimo(slirp); | |
578 | slirp->last_slowtimo = curtime; | |
579 | } | |
580 | ||
581 | /* | |
582 | * Check sockets | |
583 | */ | |
584 | if (!select_error) { | |
585 | /* | |
586 | * Check TCP sockets | |
587 | */ | |
588 | for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) { | |
589 | int revents; | |
590 | ||
591 | so_next = so->so_next; | |
592 | ||
593 | revents = 0; | |
594 | if (so->pollfds_idx != -1) { | |
595 | revents = get_revents(so->pollfds_idx, opaque); | |
596 | } | |
597 | ||
598 | if (so->so_state & SS_NOFDREF || so->s == -1) { | |
599 | continue; | |
600 | } | |
601 | ||
602 | /* | |
603 | * Check for URG data | |
604 | * This will soread as well, so no need to | |
605 | * test for SLIRP_POLL_IN below if this succeeds | |
606 | */ | |
607 | if (revents & SLIRP_POLL_PRI) { | |
608 | ret = sorecvoob(so); | |
609 | if (ret < 0) { | |
610 | /* Socket error might have resulted in the socket being | |
611 | * removed, do not try to do anything more with it. */ | |
612 | continue; | |
613 | } | |
614 | } | |
615 | /* | |
616 | * Check sockets for reading | |
617 | */ | |
618 | else if (revents & | |
619 | (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR)) { | |
620 | /* | |
621 | * Check for incoming connections | |
622 | */ | |
623 | if (so->so_state & SS_FACCEPTCONN) { | |
624 | tcp_connect(so); | |
625 | continue; | |
626 | } /* else */ | |
627 | ret = soread(so); | |
628 | ||
629 | /* Output it if we read something */ | |
630 | if (ret > 0) { | |
631 | tcp_output(sototcpcb(so)); | |
632 | } | |
633 | if (ret < 0) { | |
634 | /* Socket error might have resulted in the socket being | |
635 | * removed, do not try to do anything more with it. */ | |
636 | continue; | |
637 | } | |
638 | } | |
639 | ||
640 | /* | |
641 | * Check sockets for writing | |
642 | */ | |
643 | if (!(so->so_state & SS_NOFDREF) && | |
644 | (revents & (SLIRP_POLL_OUT | SLIRP_POLL_ERR))) { | |
645 | /* | |
646 | * Check for non-blocking, still-connecting sockets | |
647 | */ | |
648 | if (so->so_state & SS_ISFCONNECTING) { | |
649 | /* Connected */ | |
650 | so->so_state &= ~SS_ISFCONNECTING; | |
651 | ||
652 | ret = send(so->s, (const void *)&ret, 0, 0); | |
653 | if (ret < 0) { | |
654 | /* XXXXX Must fix, zero bytes is a NOP */ | |
655 | if (errno == EAGAIN || errno == EWOULDBLOCK || | |
656 | errno == EINPROGRESS || errno == ENOTCONN) { | |
657 | continue; | |
658 | } | |
659 | ||
660 | /* else failed */ | |
661 | so->so_state &= SS_PERSISTENT_MASK; | |
662 | so->so_state |= SS_NOFDREF; | |
663 | } | |
664 | /* else so->so_state &= ~SS_ISFCONNECTING; */ | |
665 | ||
666 | /* | |
667 | * Continue tcp_input | |
668 | */ | |
669 | tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, | |
670 | so->so_ffamily); | |
671 | /* continue; */ | |
672 | } else { | |
673 | ret = sowrite(so); | |
674 | if (ret > 0) { | |
675 | /* Call tcp_output in case we need to send a window | |
676 | * update to the guest, otherwise it will be stuck | |
677 | * until it sends a window probe. */ | |
678 | tcp_output(sototcpcb(so)); | |
679 | } | |
680 | } | |
681 | } | |
682 | } | |
683 | ||
684 | /* | |
685 | * Now UDP sockets. | |
686 | * Incoming packets are sent straight away, they're not buffered. | |
687 | * Incoming UDP data isn't buffered either. | |
688 | */ | |
689 | for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { | |
690 | int revents; | |
691 | ||
692 | so_next = so->so_next; | |
693 | ||
694 | revents = 0; | |
695 | if (so->pollfds_idx != -1) { | |
696 | revents = get_revents(so->pollfds_idx, opaque); | |
697 | } | |
698 | ||
699 | if (so->s != -1 && | |
700 | (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { | |
701 | sorecvfrom(so); | |
702 | } | |
703 | } | |
704 | ||
705 | /* | |
706 | * Check incoming ICMP relies. | |
707 | */ | |
708 | for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { | |
709 | int revents; | |
710 | ||
711 | so_next = so->so_next; | |
712 | ||
713 | revents = 0; | |
714 | if (so->pollfds_idx != -1) { | |
715 | revents = get_revents(so->pollfds_idx, opaque); | |
716 | } | |
717 | ||
718 | if (so->s != -1 && | |
719 | (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { | |
720 | icmp_receive(so); | |
721 | } | |
722 | } | |
723 | } | |
724 | ||
725 | if_start(slirp); | |
726 | } | |
727 | ||
728 | static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) | |
729 | { | |
730 | struct slirp_arphdr *ah = (struct slirp_arphdr *)(pkt + ETH_HLEN); | |
731 | uint8_t arp_reply[MAX(ETH_HLEN + sizeof(struct slirp_arphdr), 64)]; | |
732 | struct ethhdr *reh = (struct ethhdr *)arp_reply; | |
733 | struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_reply + ETH_HLEN); | |
734 | int ar_op; | |
735 | struct gfwd_list *ex_ptr; | |
736 | ||
737 | if (!slirp->in_enabled) { | |
738 | return; | |
739 | } | |
740 | ||
741 | ar_op = ntohs(ah->ar_op); | |
742 | switch (ar_op) { | |
743 | case ARPOP_REQUEST: | |
744 | if (ah->ar_tip == ah->ar_sip) { | |
745 | /* Gratuitous ARP */ | |
746 | arp_table_add(slirp, ah->ar_sip, ah->ar_sha); | |
747 | return; | |
748 | } | |
749 | ||
750 | if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == | |
751 | slirp->vnetwork_addr.s_addr) { | |
752 | if (ah->ar_tip == slirp->vnameserver_addr.s_addr || | |
753 | ah->ar_tip == slirp->vhost_addr.s_addr) | |
754 | goto arp_ok; | |
755 | /* TODO: IPv6 */ | |
756 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; | |
757 | ex_ptr = ex_ptr->ex_next) { | |
758 | if (ex_ptr->ex_addr.s_addr == ah->ar_tip) | |
759 | goto arp_ok; | |
760 | } | |
761 | return; | |
762 | arp_ok: | |
763 | memset(arp_reply, 0, sizeof(arp_reply)); | |
764 | ||
765 | arp_table_add(slirp, ah->ar_sip, ah->ar_sha); | |
766 | ||
767 | /* ARP request for alias/dns mac address */ | |
768 | memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); | |
769 | memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); | |
770 | memcpy(&reh->h_source[2], &ah->ar_tip, 4); | |
771 | reh->h_proto = htons(ETH_P_ARP); | |
772 | ||
773 | rah->ar_hrd = htons(1); | |
774 | rah->ar_pro = htons(ETH_P_IP); | |
775 | rah->ar_hln = ETH_ALEN; | |
776 | rah->ar_pln = 4; | |
777 | rah->ar_op = htons(ARPOP_REPLY); | |
778 | memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); | |
779 | rah->ar_sip = ah->ar_tip; | |
780 | memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); | |
781 | rah->ar_tip = ah->ar_sip; | |
782 | slirp_send_packet_all(slirp, arp_reply, sizeof(arp_reply)); | |
783 | } | |
784 | break; | |
785 | case ARPOP_REPLY: | |
786 | arp_table_add(slirp, ah->ar_sip, ah->ar_sha); | |
787 | break; | |
788 | default: | |
789 | break; | |
790 | } | |
791 | } | |
792 | ||
793 | void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) | |
794 | { | |
795 | struct mbuf *m; | |
796 | int proto; | |
797 | ||
798 | if (pkt_len < ETH_HLEN) | |
799 | return; | |
800 | ||
801 | proto = (((uint16_t)pkt[12]) << 8) + pkt[13]; | |
802 | switch (proto) { | |
803 | case ETH_P_ARP: | |
804 | arp_input(slirp, pkt, pkt_len); | |
805 | break; | |
806 | case ETH_P_IP: | |
807 | case ETH_P_IPV6: | |
808 | m = m_get(slirp); | |
809 | if (!m) | |
810 | return; | |
811 | /* Note: we add 2 to align the IP header on 4 bytes, | |
812 | * and add the margin for the tcpiphdr overhead */ | |
813 | if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) { | |
814 | m_inc(m, pkt_len + TCPIPHDR_DELTA + 2); | |
815 | } | |
816 | m->m_len = pkt_len + TCPIPHDR_DELTA + 2; | |
817 | memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len); | |
818 | ||
819 | m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN; | |
820 | m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN; | |
821 | ||
822 | if (proto == ETH_P_IP) { | |
823 | ip_input(m); | |
824 | } else if (proto == ETH_P_IPV6) { | |
825 | ip6_input(m); | |
826 | } | |
827 | break; | |
828 | ||
829 | case ETH_P_NCSI: | |
830 | ncsi_input(slirp, pkt, pkt_len); | |
831 | break; | |
832 | ||
833 | default: | |
834 | break; | |
835 | } | |
836 | } | |
837 | ||
838 | /* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no | |
839 | * packet should be sent, 0 if the packet must be re-queued, 2 if the packet | |
840 | * is ready to go. | |
841 | */ | |
842 | static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, | |
843 | uint8_t ethaddr[ETH_ALEN]) | |
844 | { | |
845 | const struct ip *iph = (const struct ip *)ifm->m_data; | |
846 | ||
847 | if (iph->ip_dst.s_addr == 0) { | |
848 | /* 0.0.0.0 can not be a destination address, something went wrong, | |
849 | * avoid making it worse */ | |
850 | return 1; | |
851 | } | |
852 | if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { | |
853 | uint8_t arp_req[ETH_HLEN + sizeof(struct slirp_arphdr)]; | |
854 | struct ethhdr *reh = (struct ethhdr *)arp_req; | |
855 | struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_req + ETH_HLEN); | |
856 | ||
857 | if (!ifm->resolution_requested) { | |
858 | /* If the client addr is not known, send an ARP request */ | |
859 | memset(reh->h_dest, 0xff, ETH_ALEN); | |
860 | memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); | |
861 | memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); | |
862 | reh->h_proto = htons(ETH_P_ARP); | |
863 | rah->ar_hrd = htons(1); | |
864 | rah->ar_pro = htons(ETH_P_IP); | |
865 | rah->ar_hln = ETH_ALEN; | |
866 | rah->ar_pln = 4; | |
867 | rah->ar_op = htons(ARPOP_REQUEST); | |
868 | ||
869 | /* source hw addr */ | |
870 | memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); | |
871 | memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); | |
872 | ||
873 | /* source IP */ | |
874 | rah->ar_sip = slirp->vhost_addr.s_addr; | |
875 | ||
876 | /* target hw addr (none) */ | |
877 | memset(rah->ar_tha, 0, ETH_ALEN); | |
878 | ||
879 | /* target IP */ | |
880 | rah->ar_tip = iph->ip_dst.s_addr; | |
881 | slirp->client_ipaddr = iph->ip_dst; | |
882 | slirp_send_packet_all(slirp, arp_req, sizeof(arp_req)); | |
883 | ifm->resolution_requested = true; | |
884 | ||
885 | /* Expire request and drop outgoing packet after 1 second */ | |
886 | ifm->expiration_date = | |
887 | slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL; | |
888 | } | |
889 | return 0; | |
890 | } else { | |
891 | memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); | |
892 | /* XXX: not correct */ | |
893 | memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); | |
894 | eh->h_proto = htons(ETH_P_IP); | |
895 | ||
896 | /* Send this */ | |
897 | return 2; | |
898 | } | |
899 | } | |
900 | ||
901 | /* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no | |
902 | * packet should be sent, 0 if the packet must be re-queued, 2 if the packet | |
903 | * is ready to go. | |
904 | */ | |
905 | static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, | |
906 | uint8_t ethaddr[ETH_ALEN]) | |
907 | { | |
908 | const struct ip6 *ip6h = mtod(ifm, const struct ip6 *); | |
909 | if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) { | |
910 | if (!ifm->resolution_requested) { | |
911 | ndp_send_ns(slirp, ip6h->ip_dst); | |
912 | ifm->resolution_requested = true; | |
913 | ifm->expiration_date = | |
914 | slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL; | |
915 | } | |
916 | return 0; | |
917 | } else { | |
918 | eh->h_proto = htons(ETH_P_IPV6); | |
919 | in6_compute_ethaddr(ip6h->ip_src, eh->h_source); | |
920 | ||
921 | /* Send this */ | |
922 | return 2; | |
923 | } | |
924 | } | |
925 | ||
926 | /* Output the IP packet to the ethernet device. Returns 0 if the packet must be | |
927 | * re-queued. | |
928 | */ | |
929 | int if_encap(Slirp *slirp, struct mbuf *ifm) | |
930 | { | |
931 | uint8_t buf[IF_MTU_MAX + 100]; | |
932 | struct ethhdr *eh = (struct ethhdr *)buf; | |
933 | uint8_t ethaddr[ETH_ALEN]; | |
934 | const struct ip *iph = (const struct ip *)ifm->m_data; | |
935 | int ret; | |
936 | ||
937 | if (ifm->m_len + ETH_HLEN > sizeof(buf)) { | |
938 | return 1; | |
939 | } | |
940 | ||
941 | switch (iph->ip_v) { | |
942 | case IPVERSION: | |
943 | ret = if_encap4(slirp, ifm, eh, ethaddr); | |
944 | if (ret < 2) { | |
945 | return ret; | |
946 | } | |
947 | break; | |
948 | ||
949 | case IP6VERSION: | |
950 | ret = if_encap6(slirp, ifm, eh, ethaddr); | |
951 | if (ret < 2) { | |
952 | return ret; | |
953 | } | |
954 | break; | |
955 | ||
956 | default: | |
957 | g_assert_not_reached(); | |
958 | break; | |
959 | } | |
960 | ||
961 | memcpy(eh->h_dest, ethaddr, ETH_ALEN); | |
962 | DEBUG_ARG("src = %02x:%02x:%02x:%02x:%02x:%02x", eh->h_source[0], | |
963 | eh->h_source[1], eh->h_source[2], eh->h_source[3], | |
964 | eh->h_source[4], eh->h_source[5]); | |
965 | DEBUG_ARG("dst = %02x:%02x:%02x:%02x:%02x:%02x", eh->h_dest[0], | |
966 | eh->h_dest[1], eh->h_dest[2], eh->h_dest[3], eh->h_dest[4], | |
967 | eh->h_dest[5]); | |
968 | memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len); | |
969 | slirp_send_packet_all(slirp, buf, ifm->m_len + ETH_HLEN); | |
970 | return 1; | |
971 | } | |
972 | ||
973 | /* Drop host forwarding rule, return 0 if found. */ | |
974 | /* TODO: IPv6 */ | |
975 | int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, | |
976 | int host_port) | |
977 | { | |
978 | struct socket *so; | |
979 | struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb); | |
980 | struct sockaddr_in addr; | |
981 | int port = htons(host_port); | |
982 | socklen_t addr_len; | |
983 | ||
984 | for (so = head->so_next; so != head; so = so->so_next) { | |
985 | addr_len = sizeof(addr); | |
986 | if ((so->so_state & SS_HOSTFWD) && | |
987 | getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && | |
988 | addr.sin_addr.s_addr == host_addr.s_addr && addr.sin_port == port) { | |
989 | so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); | |
990 | closesocket(so->s); | |
991 | sofree(so); | |
992 | return 0; | |
993 | } | |
994 | } | |
995 | ||
996 | return -1; | |
997 | } | |
998 | ||
999 | /* TODO: IPv6 */ | |
1000 | int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, | |
1001 | int host_port, struct in_addr guest_addr, int guest_port) | |
1002 | { | |
1003 | if (!guest_addr.s_addr) { | |
1004 | guest_addr = slirp->vdhcp_startaddr; | |
1005 | } | |
1006 | if (is_udp) { | |
1007 | if (!udp_listen(slirp, host_addr.s_addr, htons(host_port), | |
1008 | guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) | |
1009 | return -1; | |
1010 | } else { | |
1011 | if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port), | |
1012 | guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) | |
1013 | return -1; | |
1014 | } | |
1015 | return 0; | |
1016 | } | |
1017 | ||
1018 | /* TODO: IPv6 */ | |
1019 | static bool check_guestfwd(Slirp *slirp, struct in_addr *guest_addr, | |
1020 | int guest_port) | |
1021 | { | |
1022 | struct gfwd_list *tmp_ptr; | |
1023 | ||
1024 | if (!guest_addr->s_addr) { | |
1025 | guest_addr->s_addr = slirp->vnetwork_addr.s_addr | | |
1026 | (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr); | |
1027 | } | |
1028 | if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) != | |
1029 | slirp->vnetwork_addr.s_addr || | |
1030 | guest_addr->s_addr == slirp->vhost_addr.s_addr || | |
1031 | guest_addr->s_addr == slirp->vnameserver_addr.s_addr) { | |
1032 | return false; | |
1033 | } | |
1034 | ||
1035 | /* check if the port is "bound" */ | |
1036 | for (tmp_ptr = slirp->guestfwd_list; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { | |
1037 | if (guest_port == tmp_ptr->ex_fport && | |
1038 | guest_addr->s_addr == tmp_ptr->ex_addr.s_addr) | |
1039 | return false; | |
1040 | } | |
1041 | ||
1042 | return true; | |
1043 | } | |
1044 | ||
1045 | int slirp_add_exec(Slirp *slirp, const char *cmdline, | |
1046 | struct in_addr *guest_addr, int guest_port) | |
1047 | { | |
1048 | if (!check_guestfwd(slirp, guest_addr, guest_port)) { | |
1049 | return -1; | |
1050 | } | |
1051 | ||
1052 | add_exec(&slirp->guestfwd_list, cmdline, *guest_addr, htons(guest_port)); | |
1053 | return 0; | |
1054 | } | |
1055 | ||
1056 | int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque, | |
1057 | struct in_addr *guest_addr, int guest_port) | |
1058 | { | |
1059 | if (!check_guestfwd(slirp, guest_addr, guest_port)) { | |
1060 | return -1; | |
1061 | } | |
1062 | ||
1063 | add_guestfwd(&slirp->guestfwd_list, write_cb, opaque, *guest_addr, | |
1064 | htons(guest_port)); | |
1065 | return 0; | |
1066 | } | |
1067 | ||
1068 | ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) | |
1069 | { | |
1070 | if (so->s == -1 && so->guestfwd) { | |
1071 | /* XXX this blocks entire thread. Rewrite to use | |
1072 | * qemu_chr_fe_write and background I/O callbacks */ | |
1073 | so->guestfwd->write_cb(buf, len, so->guestfwd->opaque); | |
1074 | return len; | |
1075 | } | |
1076 | ||
1077 | if (so->s == -1) { | |
1078 | /* | |
1079 | * This should in theory not happen but it is hard to be | |
1080 | * sure because some code paths will end up with so->s == -1 | |
1081 | * on a failure but don't dispose of the struct socket. | |
1082 | * Check specifically, so we don't pass -1 to send(). | |
1083 | */ | |
1084 | errno = EBADF; | |
1085 | return -1; | |
1086 | } | |
1087 | ||
1088 | return send(so->s, buf, len, flags); | |
1089 | } | |
1090 | ||
1091 | struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, | |
1092 | int guest_port) | |
1093 | { | |
1094 | struct socket *so; | |
1095 | ||
1096 | /* TODO: IPv6 */ | |
1097 | for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { | |
1098 | if (so->so_faddr.s_addr == guest_addr.s_addr && | |
1099 | htons(so->so_fport) == guest_port) { | |
1100 | return so; | |
1101 | } | |
1102 | } | |
1103 | return NULL; | |
1104 | } | |
1105 | ||
1106 | size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, | |
1107 | int guest_port) | |
1108 | { | |
1109 | struct iovec iov[2]; | |
1110 | struct socket *so; | |
1111 | ||
1112 | so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); | |
1113 | ||
1114 | if (!so || so->so_state & SS_NOFDREF) { | |
1115 | return 0; | |
1116 | } | |
1117 | ||
1118 | if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) { | |
1119 | return 0; | |
1120 | } | |
1121 | ||
1122 | return sopreprbuf(so, iov, NULL); | |
1123 | } | |
1124 | ||
1125 | void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, | |
1126 | const uint8_t *buf, int size) | |
1127 | { | |
1128 | int ret; | |
1129 | struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); | |
1130 | ||
1131 | if (!so) | |
1132 | return; | |
1133 | ||
1134 | ret = soreadbuf(so, (const char *)buf, size); | |
1135 | ||
1136 | if (ret > 0) | |
1137 | tcp_output(sototcpcb(so)); | |
1138 | } | |
1139 | ||
1140 | void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len) | |
1141 | { | |
1142 | ssize_t ret = slirp->cb->send_packet(buf, len, slirp->opaque); | |
1143 | ||
1144 | if (ret < 0) { | |
1145 | g_critical("Failed to send packet, ret: %ld", (long)ret); | |
1146 | } else if (ret < len) { | |
1147 | DEBUG_ERROR("send_packet() didn't send all data: %ld < %lu", (long)ret, | |
1148 | (unsigned long)len); | |
1149 | } | |
1150 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | #ifndef SLIRP_H | |
2 | #define SLIRP_H | |
3 | ||
4 | #ifdef _WIN32 | |
5 | ||
6 | /* as defined in sdkddkver.h */ | |
7 | #ifndef _WIN32_WINNT | |
8 | #define _WIN32_WINNT 0x0600 /* Vista */ | |
9 | #endif | |
10 | /* reduces the number of implicitly included headers */ | |
11 | #ifndef WIN32_LEAN_AND_MEAN | |
12 | #define WIN32_LEAN_AND_MEAN | |
13 | #endif | |
14 | ||
15 | #include <winsock2.h> | |
16 | #include <windows.h> | |
17 | #include <ws2tcpip.h> | |
18 | #include <sys/timeb.h> | |
19 | #include <iphlpapi.h> | |
20 | ||
21 | #else | |
22 | #if !defined(__HAIKU__) | |
23 | #define O_BINARY 0 | |
24 | #endif | |
25 | #endif | |
26 | ||
27 | #ifndef _WIN32 | |
28 | #include <sys/uio.h> | |
29 | #include <netinet/in.h> | |
30 | #include <arpa/inet.h> | |
31 | #include <sys/socket.h> | |
32 | #include <sys/ioctl.h> | |
33 | #endif | |
34 | ||
35 | #ifdef __APPLE__ | |
36 | #include <sys/filio.h> | |
37 | #endif | |
38 | ||
39 | /* Avoid conflicting with the libc insque() and remque(), which | |
40 | have different prototypes. */ | |
41 | #define insque slirp_insque | |
42 | #define remque slirp_remque | |
43 | #define quehead slirp_quehead | |
44 | ||
45 | #include "debug.h" | |
46 | #include "util.h" | |
47 | ||
48 | #include "libslirp.h" | |
49 | #include "ip.h" | |
50 | #include "ip6.h" | |
51 | #include "tcp.h" | |
52 | #include "tcp_timer.h" | |
53 | #include "tcp_var.h" | |
54 | #include "tcpip.h" | |
55 | #include "udp.h" | |
56 | #include "ip_icmp.h" | |
57 | #include "ip6_icmp.h" | |
58 | #include "mbuf.h" | |
59 | #include "sbuf.h" | |
60 | #include "socket.h" | |
61 | #include "if.h" | |
62 | #include "main.h" | |
63 | #include "misc.h" | |
64 | ||
65 | #include "bootp.h" | |
66 | #include "tftp.h" | |
67 | ||
68 | #define ARPOP_REQUEST 1 /* ARP request */ | |
69 | #define ARPOP_REPLY 2 /* ARP reply */ | |
70 | ||
71 | struct ethhdr { | |
72 | unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ | |
73 | unsigned char h_source[ETH_ALEN]; /* source ether addr */ | |
74 | unsigned short h_proto; /* packet type ID field */ | |
75 | }; | |
76 | ||
77 | struct slirp_arphdr { | |
78 | unsigned short ar_hrd; /* format of hardware address */ | |
79 | unsigned short ar_pro; /* format of protocol address */ | |
80 | unsigned char ar_hln; /* length of hardware address */ | |
81 | unsigned char ar_pln; /* length of protocol address */ | |
82 | unsigned short ar_op; /* ARP opcode (command) */ | |
83 | ||
84 | /* | |
85 | * Ethernet looks like this : This bit is variable sized however... | |
86 | */ | |
87 | unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ | |
88 | uint32_t ar_sip; /* sender IP address */ | |
89 | unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ | |
90 | uint32_t ar_tip; /* target IP address */ | |
91 | } SLIRP_PACKED; | |
92 | ||
93 | #define ARP_TABLE_SIZE 16 | |
94 | ||
95 | typedef struct ArpTable { | |
96 | struct slirp_arphdr table[ARP_TABLE_SIZE]; | |
97 | int next_victim; | |
98 | } ArpTable; | |
99 | ||
100 | void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]); | |
101 | ||
102 | bool arp_table_search(Slirp *slirp, uint32_t ip_addr, | |
103 | uint8_t out_ethaddr[ETH_ALEN]); | |
104 | ||
105 | struct ndpentry { | |
106 | unsigned char eth_addr[ETH_ALEN]; /* sender hardware address */ | |
107 | struct in6_addr ip_addr; /* sender IP address */ | |
108 | }; | |
109 | ||
110 | #define NDP_TABLE_SIZE 16 | |
111 | ||
112 | typedef struct NdpTable { | |
113 | struct ndpentry table[NDP_TABLE_SIZE]; | |
114 | int next_victim; | |
115 | } NdpTable; | |
116 | ||
117 | void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, | |
118 | uint8_t ethaddr[ETH_ALEN]); | |
119 | bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, | |
120 | uint8_t out_ethaddr[ETH_ALEN]); | |
121 | ||
122 | struct Slirp { | |
123 | unsigned time_fasttimo; | |
124 | unsigned last_slowtimo; | |
125 | bool do_slowtimo; | |
126 | ||
127 | bool in_enabled, in6_enabled; | |
128 | ||
129 | /* virtual network configuration */ | |
130 | struct in_addr vnetwork_addr; | |
131 | struct in_addr vnetwork_mask; | |
132 | struct in_addr vhost_addr; | |
133 | struct in6_addr vprefix_addr6; | |
134 | uint8_t vprefix_len; | |
135 | struct in6_addr vhost_addr6; | |
136 | struct in_addr vdhcp_startaddr; | |
137 | struct in_addr vnameserver_addr; | |
138 | struct in6_addr vnameserver_addr6; | |
139 | ||
140 | struct in_addr client_ipaddr; | |
141 | char client_hostname[33]; | |
142 | ||
143 | int restricted; | |
144 | struct gfwd_list *guestfwd_list; | |
145 | ||
146 | int if_mtu; | |
147 | int if_mru; | |
148 | ||
149 | bool disable_host_loopback; | |
150 | ||
151 | /* mbuf states */ | |
152 | struct quehead m_freelist; | |
153 | struct quehead m_usedlist; | |
154 | int mbuf_alloced; | |
155 | ||
156 | /* if states */ | |
157 | struct quehead if_fastq; /* fast queue (for interactive data) */ | |
158 | struct quehead if_batchq; /* queue for non-interactive data */ | |
159 | bool if_start_busy; /* avoid if_start recursion */ | |
160 | ||
161 | /* ip states */ | |
162 | struct ipq ipq; /* ip reass. queue */ | |
163 | uint16_t ip_id; /* ip packet ctr, for ids */ | |
164 | ||
165 | /* bootp/dhcp states */ | |
166 | BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; | |
167 | char *bootp_filename; | |
168 | size_t vdnssearch_len; | |
169 | uint8_t *vdnssearch; | |
170 | char *vdomainname; | |
171 | ||
172 | /* tcp states */ | |
173 | struct socket tcb; | |
174 | struct socket *tcp_last_so; | |
175 | tcp_seq tcp_iss; /* tcp initial send seq # */ | |
176 | uint32_t tcp_now; /* for RFC 1323 timestamps */ | |
177 | ||
178 | /* udp states */ | |
179 | struct socket udb; | |
180 | struct socket *udp_last_so; | |
181 | ||
182 | /* icmp states */ | |
183 | struct socket icmp; | |
184 | struct socket *icmp_last_so; | |
185 | ||
186 | /* tftp states */ | |
187 | char *tftp_prefix; | |
188 | struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; | |
189 | char *tftp_server_name; | |
190 | ||
191 | ArpTable arp_table; | |
192 | NdpTable ndp_table; | |
193 | ||
194 | GRand *grand; | |
195 | void *ra_timer; | |
196 | ||
197 | bool enable_emu; | |
198 | ||
199 | const SlirpCb *cb; | |
200 | void *opaque; | |
201 | }; | |
202 | ||
203 | void if_start(Slirp *); | |
204 | ||
205 | int get_dns_addr(struct in_addr *pdns_addr); | |
206 | int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id); | |
207 | ||
208 | /* ncsi.c */ | |
209 | void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); | |
210 | ||
211 | #ifndef _WIN32 | |
212 | #include <netdb.h> | |
213 | #endif | |
214 | ||
215 | ||
216 | extern bool slirp_do_keepalive; | |
217 | ||
218 | #define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL) | |
219 | ||
220 | /* dnssearch.c */ | |
221 | int translate_dnssearch(Slirp *s, const char **names); | |
222 | ||
223 | /* cksum.c */ | |
224 | int cksum(struct mbuf *m, int len); | |
225 | int ip6_cksum(struct mbuf *m); | |
226 | ||
227 | /* if.c */ | |
228 | void if_init(Slirp *); | |
229 | void if_output(struct socket *, struct mbuf *); | |
230 | ||
231 | /* ip_input.c */ | |
232 | void ip_init(Slirp *); | |
233 | void ip_cleanup(Slirp *); | |
234 | void ip_input(struct mbuf *); | |
235 | void ip_slowtimo(Slirp *); | |
236 | void ip_stripoptions(register struct mbuf *, struct mbuf *); | |
237 | ||
238 | /* ip_output.c */ | |
239 | int ip_output(struct socket *, struct mbuf *); | |
240 | ||
241 | /* ip6_input.c */ | |
242 | void ip6_init(Slirp *); | |
243 | void ip6_cleanup(Slirp *); | |
244 | void ip6_input(struct mbuf *); | |
245 | ||
246 | /* ip6_output */ | |
247 | int ip6_output(struct socket *, struct mbuf *, int fast); | |
248 | ||
249 | /* tcp_input.c */ | |
250 | void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af); | |
251 | int tcp_mss(register struct tcpcb *, unsigned); | |
252 | ||
253 | /* tcp_output.c */ | |
254 | int tcp_output(register struct tcpcb *); | |
255 | void tcp_setpersist(register struct tcpcb *); | |
256 | ||
257 | /* tcp_subr.c */ | |
258 | void tcp_init(Slirp *); | |
259 | void tcp_cleanup(Slirp *); | |
260 | void tcp_template(struct tcpcb *); | |
261 | void tcp_respond(struct tcpcb *, register struct tcpiphdr *, | |
262 | register struct mbuf *, tcp_seq, tcp_seq, int, unsigned short); | |
263 | struct tcpcb *tcp_newtcpcb(struct socket *); | |
264 | struct tcpcb *tcp_close(register struct tcpcb *); | |
265 | void tcp_sockclosed(struct tcpcb *); | |
266 | int tcp_fconnect(struct socket *, unsigned short af); | |
267 | void tcp_connect(struct socket *); | |
268 | int tcp_attach(struct socket *); | |
269 | uint8_t tcp_tos(struct socket *); | |
270 | int tcp_emu(struct socket *, struct mbuf *); | |
271 | int tcp_ctl(struct socket *); | |
272 | struct tcpcb *tcp_drop(struct tcpcb *tp, int err); | |
273 | ||
274 | struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, | |
275 | int guest_port); | |
276 | ||
277 | void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len); | |
278 | ||
279 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #include "slirp.h" | |
6 | #include "ip_icmp.h" | |
7 | #ifdef __sun__ | |
8 | #include <sys/filio.h> | |
9 | #endif | |
10 | ||
11 | static void sofcantrcvmore(struct socket *so); | |
12 | static void sofcantsendmore(struct socket *so); | |
13 | ||
14 | struct socket *solookup(struct socket **last, struct socket *head, | |
15 | struct sockaddr_storage *lhost, | |
16 | struct sockaddr_storage *fhost) | |
17 | { | |
18 | struct socket *so = *last; | |
19 | ||
20 | /* Optimisation */ | |
21 | if (so != head && sockaddr_equal(&(so->lhost.ss), lhost) && | |
22 | (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { | |
23 | return so; | |
24 | } | |
25 | ||
26 | for (so = head->so_next; so != head; so = so->so_next) { | |
27 | if (sockaddr_equal(&(so->lhost.ss), lhost) && | |
28 | (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) { | |
29 | *last = so; | |
30 | return so; | |
31 | } | |
32 | } | |
33 | ||
34 | return (struct socket *)NULL; | |
35 | } | |
36 | ||
37 | /* | |
38 | * Create a new socket, initialise the fields | |
39 | * It is the responsibility of the caller to | |
40 | * insque() it into the correct linked-list | |
41 | */ | |
42 | struct socket *socreate(Slirp *slirp) | |
43 | { | |
44 | struct socket *so = g_new(struct socket, 1); | |
45 | ||
46 | memset(so, 0, sizeof(struct socket)); | |
47 | so->so_state = SS_NOFDREF; | |
48 | so->s = -1; | |
49 | so->slirp = slirp; | |
50 | so->pollfds_idx = -1; | |
51 | ||
52 | return so; | |
53 | } | |
54 | ||
55 | /* | |
56 | * Remove references to so from the given message queue. | |
57 | */ | |
58 | static void soqfree(struct socket *so, struct quehead *qh) | |
59 | { | |
60 | struct mbuf *ifq; | |
61 | ||
62 | for (ifq = (struct mbuf *)qh->qh_link; (struct quehead *)ifq != qh; | |
63 | ifq = ifq->ifq_next) { | |
64 | if (ifq->ifq_so == so) { | |
65 | struct mbuf *ifm; | |
66 | ifq->ifq_so = NULL; | |
67 | for (ifm = ifq->ifs_next; ifm != ifq; ifm = ifm->ifs_next) { | |
68 | ifm->ifq_so = NULL; | |
69 | } | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
74 | /* | |
75 | * remque and free a socket, clobber cache | |
76 | */ | |
77 | void sofree(struct socket *so) | |
78 | { | |
79 | Slirp *slirp = so->slirp; | |
80 | ||
81 | soqfree(so, &slirp->if_fastq); | |
82 | soqfree(so, &slirp->if_batchq); | |
83 | ||
84 | if (so == slirp->tcp_last_so) { | |
85 | slirp->tcp_last_so = &slirp->tcb; | |
86 | } else if (so == slirp->udp_last_so) { | |
87 | slirp->udp_last_so = &slirp->udb; | |
88 | } else if (so == slirp->icmp_last_so) { | |
89 | slirp->icmp_last_so = &slirp->icmp; | |
90 | } | |
91 | m_free(so->so_m); | |
92 | ||
93 | if (so->so_next && so->so_prev) | |
94 | remque(so); /* crashes if so is not in a queue */ | |
95 | ||
96 | if (so->so_tcpcb) { | |
97 | free(so->so_tcpcb); | |
98 | } | |
99 | g_free(so); | |
100 | } | |
101 | ||
102 | size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np) | |
103 | { | |
104 | int n, lss, total; | |
105 | struct sbuf *sb = &so->so_snd; | |
106 | int len = sb->sb_datalen - sb->sb_cc; | |
107 | int mss = so->so_tcpcb->t_maxseg; | |
108 | ||
109 | DEBUG_CALL("sopreprbuf"); | |
110 | DEBUG_ARG("so = %p", so); | |
111 | ||
112 | if (len <= 0) | |
113 | return 0; | |
114 | ||
115 | iov[0].iov_base = sb->sb_wptr; | |
116 | iov[1].iov_base = NULL; | |
117 | iov[1].iov_len = 0; | |
118 | if (sb->sb_wptr < sb->sb_rptr) { | |
119 | iov[0].iov_len = sb->sb_rptr - sb->sb_wptr; | |
120 | /* Should never succeed, but... */ | |
121 | if (iov[0].iov_len > len) | |
122 | iov[0].iov_len = len; | |
123 | if (iov[0].iov_len > mss) | |
124 | iov[0].iov_len -= iov[0].iov_len % mss; | |
125 | n = 1; | |
126 | } else { | |
127 | iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr; | |
128 | /* Should never succeed, but... */ | |
129 | if (iov[0].iov_len > len) | |
130 | iov[0].iov_len = len; | |
131 | len -= iov[0].iov_len; | |
132 | if (len) { | |
133 | iov[1].iov_base = sb->sb_data; | |
134 | iov[1].iov_len = sb->sb_rptr - sb->sb_data; | |
135 | if (iov[1].iov_len > len) | |
136 | iov[1].iov_len = len; | |
137 | total = iov[0].iov_len + iov[1].iov_len; | |
138 | if (total > mss) { | |
139 | lss = total % mss; | |
140 | if (iov[1].iov_len > lss) { | |
141 | iov[1].iov_len -= lss; | |
142 | n = 2; | |
143 | } else { | |
144 | lss -= iov[1].iov_len; | |
145 | iov[0].iov_len -= lss; | |
146 | n = 1; | |
147 | } | |
148 | } else | |
149 | n = 2; | |
150 | } else { | |
151 | if (iov[0].iov_len > mss) | |
152 | iov[0].iov_len -= iov[0].iov_len % mss; | |
153 | n = 1; | |
154 | } | |
155 | } | |
156 | if (np) | |
157 | *np = n; | |
158 | ||
159 | return iov[0].iov_len + (n - 1) * iov[1].iov_len; | |
160 | } | |
161 | ||
162 | /* | |
163 | * Read from so's socket into sb_snd, updating all relevant sbuf fields | |
164 | * NOTE: This will only be called if it is select()ed for reading, so | |
165 | * a read() of 0 (or less) means it's disconnected | |
166 | */ | |
167 | int soread(struct socket *so) | |
168 | { | |
169 | int n, nn; | |
170 | size_t buf_len; | |
171 | struct sbuf *sb = &so->so_snd; | |
172 | struct iovec iov[2]; | |
173 | ||
174 | DEBUG_CALL("soread"); | |
175 | DEBUG_ARG("so = %p", so); | |
176 | ||
177 | /* | |
178 | * No need to check if there's enough room to read. | |
179 | * soread wouldn't have been called if there weren't | |
180 | */ | |
181 | buf_len = sopreprbuf(so, iov, &n); | |
182 | assert(buf_len != 0); | |
183 | ||
184 | nn = recv(so->s, iov[0].iov_base, iov[0].iov_len, 0); | |
185 | if (nn <= 0) { | |
186 | if (nn < 0 && (errno == EINTR || errno == EAGAIN)) | |
187 | return 0; | |
188 | else { | |
189 | int err; | |
190 | socklen_t elen = sizeof err; | |
191 | struct sockaddr_storage addr; | |
192 | struct sockaddr *paddr = (struct sockaddr *)&addr; | |
193 | socklen_t alen = sizeof addr; | |
194 | ||
195 | err = errno; | |
196 | if (nn == 0) { | |
197 | if (getpeername(so->s, paddr, &alen) < 0) { | |
198 | err = errno; | |
199 | } else { | |
200 | getsockopt(so->s, SOL_SOCKET, SO_ERROR, &err, &elen); | |
201 | } | |
202 | } | |
203 | ||
204 | DEBUG_MISC(" --- soread() disconnected, nn = %d, errno = %d-%s", nn, | |
205 | errno, strerror(errno)); | |
206 | sofcantrcvmore(so); | |
207 | ||
208 | if (err == ECONNRESET || err == ECONNREFUSED || err == ENOTCONN || | |
209 | err == EPIPE) { | |
210 | tcp_drop(sototcpcb(so), err); | |
211 | } else { | |
212 | tcp_sockclosed(sototcpcb(so)); | |
213 | } | |
214 | return -1; | |
215 | } | |
216 | } | |
217 | ||
218 | /* | |
219 | * If there was no error, try and read the second time round | |
220 | * We read again if n = 2 (ie, there's another part of the buffer) | |
221 | * and we read as much as we could in the first read | |
222 | * We don't test for <= 0 this time, because there legitimately | |
223 | * might not be any more data (since the socket is non-blocking), | |
224 | * a close will be detected on next iteration. | |
225 | * A return of -1 won't (shouldn't) happen, since it didn't happen above | |
226 | */ | |
227 | if (n == 2 && nn == iov[0].iov_len) { | |
228 | int ret; | |
229 | ret = recv(so->s, iov[1].iov_base, iov[1].iov_len, 0); | |
230 | if (ret > 0) | |
231 | nn += ret; | |
232 | } | |
233 | ||
234 | DEBUG_MISC(" ... read nn = %d bytes", nn); | |
235 | ||
236 | /* Update fields */ | |
237 | sb->sb_cc += nn; | |
238 | sb->sb_wptr += nn; | |
239 | if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) | |
240 | sb->sb_wptr -= sb->sb_datalen; | |
241 | return nn; | |
242 | } | |
243 | ||
244 | int soreadbuf(struct socket *so, const char *buf, int size) | |
245 | { | |
246 | int n, nn, copy = size; | |
247 | struct sbuf *sb = &so->so_snd; | |
248 | struct iovec iov[2]; | |
249 | ||
250 | DEBUG_CALL("soreadbuf"); | |
251 | DEBUG_ARG("so = %p", so); | |
252 | ||
253 | /* | |
254 | * No need to check if there's enough room to read. | |
255 | * soread wouldn't have been called if there weren't | |
256 | */ | |
257 | assert(size > 0); | |
258 | if (sopreprbuf(so, iov, &n) < size) | |
259 | goto err; | |
260 | ||
261 | nn = MIN(iov[0].iov_len, copy); | |
262 | memcpy(iov[0].iov_base, buf, nn); | |
263 | ||
264 | copy -= nn; | |
265 | buf += nn; | |
266 | ||
267 | if (copy == 0) | |
268 | goto done; | |
269 | ||
270 | memcpy(iov[1].iov_base, buf, copy); | |
271 | ||
272 | done: | |
273 | /* Update fields */ | |
274 | sb->sb_cc += size; | |
275 | sb->sb_wptr += size; | |
276 | if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen)) | |
277 | sb->sb_wptr -= sb->sb_datalen; | |
278 | return size; | |
279 | err: | |
280 | ||
281 | sofcantrcvmore(so); | |
282 | tcp_sockclosed(sototcpcb(so)); | |
283 | g_critical("soreadbuf buffer too small"); | |
284 | return -1; | |
285 | } | |
286 | ||
287 | /* | |
288 | * Get urgent data | |
289 | * | |
290 | * When the socket is created, we set it SO_OOBINLINE, | |
291 | * so when OOB data arrives, we soread() it and everything | |
292 | * in the send buffer is sent as urgent data | |
293 | */ | |
294 | int sorecvoob(struct socket *so) | |
295 | { | |
296 | struct tcpcb *tp = sototcpcb(so); | |
297 | int ret; | |
298 | ||
299 | DEBUG_CALL("sorecvoob"); | |
300 | DEBUG_ARG("so = %p", so); | |
301 | ||
302 | /* | |
303 | * We take a guess at how much urgent data has arrived. | |
304 | * In most situations, when urgent data arrives, the next | |
305 | * read() should get all the urgent data. This guess will | |
306 | * be wrong however if more data arrives just after the | |
307 | * urgent data, or the read() doesn't return all the | |
308 | * urgent data. | |
309 | */ | |
310 | ret = soread(so); | |
311 | if (ret > 0) { | |
312 | tp->snd_up = tp->snd_una + so->so_snd.sb_cc; | |
313 | tp->t_force = 1; | |
314 | tcp_output(tp); | |
315 | tp->t_force = 0; | |
316 | } | |
317 | ||
318 | return ret; | |
319 | } | |
320 | ||
321 | /* | |
322 | * Send urgent data | |
323 | * There's a lot duplicated code here, but... | |
324 | */ | |
325 | int sosendoob(struct socket *so) | |
326 | { | |
327 | struct sbuf *sb = &so->so_rcv; | |
328 | char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ | |
329 | ||
330 | int n; | |
331 | ||
332 | DEBUG_CALL("sosendoob"); | |
333 | DEBUG_ARG("so = %p", so); | |
334 | DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc); | |
335 | ||
336 | if (so->so_urgc > 2048) | |
337 | so->so_urgc = 2048; /* XXXX */ | |
338 | ||
339 | if (sb->sb_rptr < sb->sb_wptr) { | |
340 | /* We can send it directly */ | |
341 | n = slirp_send(so, sb->sb_rptr, so->so_urgc, | |
342 | (MSG_OOB)); /* |MSG_DONTWAIT)); */ | |
343 | } else { | |
344 | /* | |
345 | * Since there's no sendv or sendtov like writev, | |
346 | * we must copy all data to a linear buffer then | |
347 | * send it all | |
348 | */ | |
349 | uint32_t urgc = so->so_urgc; | |
350 | int len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; | |
351 | if (len > urgc) { | |
352 | len = urgc; | |
353 | } | |
354 | memcpy(buff, sb->sb_rptr, len); | |
355 | urgc -= len; | |
356 | if (urgc) { | |
357 | n = sb->sb_wptr - sb->sb_data; | |
358 | if (n > urgc) { | |
359 | n = urgc; | |
360 | } | |
361 | memcpy((buff + len), sb->sb_data, n); | |
362 | len += n; | |
363 | } | |
364 | n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ | |
365 | #ifdef DEBUG | |
366 | if (n != len) { | |
367 | DEBUG_ERROR("Didn't send all data urgently XXXXX"); | |
368 | } | |
369 | #endif | |
370 | } | |
371 | ||
372 | if (n < 0) { | |
373 | return n; | |
374 | } | |
375 | so->so_urgc -= n; | |
376 | DEBUG_MISC(" ---2 sent %d bytes urgent data, %d urgent bytes left", n, | |
377 | so->so_urgc); | |
378 | ||
379 | sb->sb_cc -= n; | |
380 | sb->sb_rptr += n; | |
381 | if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) | |
382 | sb->sb_rptr -= sb->sb_datalen; | |
383 | ||
384 | return n; | |
385 | } | |
386 | ||
387 | /* | |
388 | * Write data from so_rcv to so's socket, | |
389 | * updating all sbuf field as necessary | |
390 | */ | |
391 | int sowrite(struct socket *so) | |
392 | { | |
393 | int n, nn; | |
394 | struct sbuf *sb = &so->so_rcv; | |
395 | int len = sb->sb_cc; | |
396 | struct iovec iov[2]; | |
397 | ||
398 | DEBUG_CALL("sowrite"); | |
399 | DEBUG_ARG("so = %p", so); | |
400 | ||
401 | if (so->so_urgc) { | |
402 | uint32_t expected = so->so_urgc; | |
403 | if (sosendoob(so) < expected) { | |
404 | /* Treat a short write as a fatal error too, | |
405 | * rather than continuing on and sending the urgent | |
406 | * data as if it were non-urgent and leaving the | |
407 | * so_urgc count wrong. | |
408 | */ | |
409 | goto err_disconnected; | |
410 | } | |
411 | if (sb->sb_cc == 0) | |
412 | return 0; | |
413 | } | |
414 | ||
415 | /* | |
416 | * No need to check if there's something to write, | |
417 | * sowrite wouldn't have been called otherwise | |
418 | */ | |
419 | ||
420 | iov[0].iov_base = sb->sb_rptr; | |
421 | iov[1].iov_base = NULL; | |
422 | iov[1].iov_len = 0; | |
423 | if (sb->sb_rptr < sb->sb_wptr) { | |
424 | iov[0].iov_len = sb->sb_wptr - sb->sb_rptr; | |
425 | /* Should never succeed, but... */ | |
426 | if (iov[0].iov_len > len) | |
427 | iov[0].iov_len = len; | |
428 | n = 1; | |
429 | } else { | |
430 | iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; | |
431 | if (iov[0].iov_len > len) | |
432 | iov[0].iov_len = len; | |
433 | len -= iov[0].iov_len; | |
434 | if (len) { | |
435 | iov[1].iov_base = sb->sb_data; | |
436 | iov[1].iov_len = sb->sb_wptr - sb->sb_data; | |
437 | if (iov[1].iov_len > len) | |
438 | iov[1].iov_len = len; | |
439 | n = 2; | |
440 | } else | |
441 | n = 1; | |
442 | } | |
443 | /* Check if there's urgent data to send, and if so, send it */ | |
444 | ||
445 | nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len, 0); | |
446 | /* This should never happen, but people tell me it does *shrug* */ | |
447 | if (nn < 0 && (errno == EAGAIN || errno == EINTR)) | |
448 | return 0; | |
449 | ||
450 | if (nn <= 0) { | |
451 | goto err_disconnected; | |
452 | } | |
453 | ||
454 | if (n == 2 && nn == iov[0].iov_len) { | |
455 | int ret; | |
456 | ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len, 0); | |
457 | if (ret > 0) | |
458 | nn += ret; | |
459 | } | |
460 | DEBUG_MISC(" ... wrote nn = %d bytes", nn); | |
461 | ||
462 | /* Update sbuf */ | |
463 | sb->sb_cc -= nn; | |
464 | sb->sb_rptr += nn; | |
465 | if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen)) | |
466 | sb->sb_rptr -= sb->sb_datalen; | |
467 | ||
468 | /* | |
469 | * If in DRAIN mode, and there's no more data, set | |
470 | * it CANTSENDMORE | |
471 | */ | |
472 | if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0) | |
473 | sofcantsendmore(so); | |
474 | ||
475 | return nn; | |
476 | ||
477 | err_disconnected: | |
478 | DEBUG_MISC(" --- sowrite disconnected, so->so_state = %x, errno = %d", | |
479 | so->so_state, errno); | |
480 | sofcantsendmore(so); | |
481 | tcp_sockclosed(sototcpcb(so)); | |
482 | return -1; | |
483 | } | |
484 | ||
485 | /* | |
486 | * recvfrom() a UDP socket | |
487 | */ | |
488 | void sorecvfrom(struct socket *so) | |
489 | { | |
490 | struct sockaddr_storage addr; | |
491 | struct sockaddr_storage saddr, daddr; | |
492 | socklen_t addrlen = sizeof(struct sockaddr_storage); | |
493 | ||
494 | DEBUG_CALL("sorecvfrom"); | |
495 | DEBUG_ARG("so = %p", so); | |
496 | ||
497 | if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */ | |
498 | char buff[256]; | |
499 | int len; | |
500 | ||
501 | len = recvfrom(so->s, buff, 256, 0, (struct sockaddr *)&addr, &addrlen); | |
502 | /* XXX Check if reply is "correct"? */ | |
503 | ||
504 | if (len == -1 || len == 0) { | |
505 | uint8_t code = ICMP_UNREACH_PORT; | |
506 | ||
507 | if (errno == EHOSTUNREACH) | |
508 | code = ICMP_UNREACH_HOST; | |
509 | else if (errno == ENETUNREACH) | |
510 | code = ICMP_UNREACH_NET; | |
511 | ||
512 | DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, strerror(errno)); | |
513 | icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); | |
514 | } else { | |
515 | icmp_reflect(so->so_m); | |
516 | so->so_m = NULL; /* Don't m_free() it again! */ | |
517 | } | |
518 | /* No need for this socket anymore, udp_detach it */ | |
519 | udp_detach(so); | |
520 | } else { /* A "normal" UDP packet */ | |
521 | struct mbuf *m; | |
522 | int len; | |
523 | #ifdef _WIN32 | |
524 | unsigned long n; | |
525 | #else | |
526 | int n; | |
527 | #endif | |
528 | ||
529 | if (ioctlsocket(so->s, FIONREAD, &n) != 0) { | |
530 | DEBUG_MISC(" ioctlsocket errno = %d-%s\n", errno, strerror(errno)); | |
531 | return; | |
532 | } | |
533 | if (n == 0) { | |
534 | return; | |
535 | } | |
536 | ||
537 | m = m_get(so->slirp); | |
538 | if (!m) { | |
539 | return; | |
540 | } | |
541 | switch (so->so_ffamily) { | |
542 | case AF_INET: | |
543 | m->m_data += IF_MAXLINKHDR + sizeof(struct udpiphdr); | |
544 | break; | |
545 | case AF_INET6: | |
546 | m->m_data += | |
547 | IF_MAXLINKHDR + sizeof(struct ip6) + sizeof(struct udphdr); | |
548 | break; | |
549 | default: | |
550 | g_assert_not_reached(); | |
551 | break; | |
552 | } | |
553 | ||
554 | /* | |
555 | * XXX Shouldn't FIONREAD packets destined for port 53, | |
556 | * but I don't know the max packet size for DNS lookups | |
557 | */ | |
558 | len = M_FREEROOM(m); | |
559 | /* if (so->so_fport != htons(53)) { */ | |
560 | ||
561 | if (n > len) { | |
562 | n = (m->m_data - m->m_dat) + m->m_len + n + 1; | |
563 | m_inc(m, n); | |
564 | len = M_FREEROOM(m); | |
565 | } | |
566 | /* } */ | |
567 | ||
568 | m->m_len = recvfrom(so->s, m->m_data, len, 0, (struct sockaddr *)&addr, | |
569 | &addrlen); | |
570 | DEBUG_MISC(" did recvfrom %d, errno = %d-%s", m->m_len, errno, | |
571 | strerror(errno)); | |
572 | if (m->m_len < 0) { | |
573 | /* Report error as ICMP */ | |
574 | switch (so->so_lfamily) { | |
575 | uint8_t code; | |
576 | case AF_INET: | |
577 | code = ICMP_UNREACH_PORT; | |
578 | ||
579 | if (errno == EHOSTUNREACH) { | |
580 | code = ICMP_UNREACH_HOST; | |
581 | } else if (errno == ENETUNREACH) { | |
582 | code = ICMP_UNREACH_NET; | |
583 | } | |
584 | ||
585 | DEBUG_MISC(" rx error, tx icmp ICMP_UNREACH:%i", code); | |
586 | icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, | |
587 | strerror(errno)); | |
588 | break; | |
589 | case AF_INET6: | |
590 | code = ICMP6_UNREACH_PORT; | |
591 | ||
592 | if (errno == EHOSTUNREACH) { | |
593 | code = ICMP6_UNREACH_ADDRESS; | |
594 | } else if (errno == ENETUNREACH) { | |
595 | code = ICMP6_UNREACH_NO_ROUTE; | |
596 | } | |
597 | ||
598 | DEBUG_MISC(" rx error, tx icmp6 ICMP_UNREACH:%i", code); | |
599 | icmp6_send_error(so->so_m, ICMP6_UNREACH, code); | |
600 | break; | |
601 | default: | |
602 | g_assert_not_reached(); | |
603 | break; | |
604 | } | |
605 | m_free(m); | |
606 | } else { | |
607 | /* | |
608 | * Hack: domain name lookup will be used the most for UDP, | |
609 | * and since they'll only be used once there's no need | |
610 | * for the 4 minute (or whatever) timeout... So we time them | |
611 | * out much quicker (10 seconds for now...) | |
612 | */ | |
613 | if (so->so_expire) { | |
614 | if (so->so_fport == htons(53)) | |
615 | so->so_expire = curtime + SO_EXPIREFAST; | |
616 | else | |
617 | so->so_expire = curtime + SO_EXPIRE; | |
618 | } | |
619 | ||
620 | /* | |
621 | * If this packet was destined for CTL_ADDR, | |
622 | * make it look like that's where it came from | |
623 | */ | |
624 | saddr = addr; | |
625 | sotranslate_in(so, &saddr); | |
626 | daddr = so->lhost.ss; | |
627 | ||
628 | switch (so->so_ffamily) { | |
629 | case AF_INET: | |
630 | udp_output(so, m, (struct sockaddr_in *)&saddr, | |
631 | (struct sockaddr_in *)&daddr, so->so_iptos); | |
632 | break; | |
633 | case AF_INET6: | |
634 | udp6_output(so, m, (struct sockaddr_in6 *)&saddr, | |
635 | (struct sockaddr_in6 *)&daddr); | |
636 | break; | |
637 | default: | |
638 | g_assert_not_reached(); | |
639 | break; | |
640 | } | |
641 | } /* rx error */ | |
642 | } /* if ping packet */ | |
643 | } | |
644 | ||
645 | /* | |
646 | * sendto() a socket | |
647 | */ | |
648 | int sosendto(struct socket *so, struct mbuf *m) | |
649 | { | |
650 | int ret; | |
651 | struct sockaddr_storage addr; | |
652 | ||
653 | DEBUG_CALL("sosendto"); | |
654 | DEBUG_ARG("so = %p", so); | |
655 | DEBUG_ARG("m = %p", m); | |
656 | ||
657 | addr = so->fhost.ss; | |
658 | DEBUG_CALL(" sendto()ing)"); | |
659 | if (sotranslate_out(so, &addr) < 0) { | |
660 | return -1; | |
661 | } | |
662 | ||
663 | /* Don't care what port we get */ | |
664 | ret = sendto(so->s, m->m_data, m->m_len, 0, (struct sockaddr *)&addr, | |
665 | sockaddr_size(&addr)); | |
666 | if (ret < 0) | |
667 | return -1; | |
668 | ||
669 | /* | |
670 | * Kill the socket if there's no reply in 4 minutes, | |
671 | * but only if it's an expirable socket | |
672 | */ | |
673 | if (so->so_expire) | |
674 | so->so_expire = curtime + SO_EXPIRE; | |
675 | so->so_state &= SS_PERSISTENT_MASK; | |
676 | so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */ | |
677 | return 0; | |
678 | } | |
679 | ||
680 | /* | |
681 | * Listen for incoming TCP connections | |
682 | */ | |
683 | struct socket *tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, | |
684 | uint32_t laddr, unsigned lport, int flags) | |
685 | { | |
686 | /* TODO: IPv6 */ | |
687 | struct sockaddr_in addr; | |
688 | struct socket *so; | |
689 | int s, opt = 1; | |
690 | socklen_t addrlen = sizeof(addr); | |
691 | memset(&addr, 0, addrlen); | |
692 | ||
693 | DEBUG_CALL("tcp_listen"); | |
694 | DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){ .s_addr = haddr })); | |
695 | DEBUG_ARG("hport = %d", ntohs(hport)); | |
696 | DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){ .s_addr = laddr })); | |
697 | DEBUG_ARG("lport = %d", ntohs(lport)); | |
698 | DEBUG_ARG("flags = %x", flags); | |
699 | ||
700 | so = socreate(slirp); | |
701 | ||
702 | /* Don't tcp_attach... we don't need so_snd nor so_rcv */ | |
703 | if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) { | |
704 | g_free(so); | |
705 | return NULL; | |
706 | } | |
707 | insque(so, &slirp->tcb); | |
708 | ||
709 | /* | |
710 | * SS_FACCEPTONCE sockets must time out. | |
711 | */ | |
712 | if (flags & SS_FACCEPTONCE) | |
713 | so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT * 2; | |
714 | ||
715 | so->so_state &= SS_PERSISTENT_MASK; | |
716 | so->so_state |= (SS_FACCEPTCONN | flags); | |
717 | so->so_lfamily = AF_INET; | |
718 | so->so_lport = lport; /* Kept in network format */ | |
719 | so->so_laddr.s_addr = laddr; /* Ditto */ | |
720 | ||
721 | addr.sin_family = AF_INET; | |
722 | addr.sin_addr.s_addr = haddr; | |
723 | addr.sin_port = hport; | |
724 | ||
725 | if (((s = slirp_socket(AF_INET, SOCK_STREAM, 0)) < 0) || | |
726 | (slirp_socket_set_fast_reuse(s) < 0) || | |
727 | (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) || | |
728 | (listen(s, 1) < 0)) { | |
729 | int tmperrno = errno; /* Don't clobber the real reason we failed */ | |
730 | ||
731 | if (s >= 0) { | |
732 | closesocket(s); | |
733 | } | |
734 | sofree(so); | |
735 | /* Restore the real errno */ | |
736 | #ifdef _WIN32 | |
737 | WSASetLastError(tmperrno); | |
738 | #else | |
739 | errno = tmperrno; | |
740 | #endif | |
741 | return NULL; | |
742 | } | |
743 | setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); | |
744 | opt = 1; | |
745 | setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); | |
746 | ||
747 | getsockname(s, (struct sockaddr *)&addr, &addrlen); | |
748 | so->so_ffamily = AF_INET; | |
749 | so->so_fport = addr.sin_port; | |
750 | if (addr.sin_addr.s_addr == 0 || | |
751 | addr.sin_addr.s_addr == loopback_addr.s_addr) | |
752 | so->so_faddr = slirp->vhost_addr; | |
753 | else | |
754 | so->so_faddr = addr.sin_addr; | |
755 | ||
756 | so->s = s; | |
757 | return so; | |
758 | } | |
759 | ||
760 | /* | |
761 | * Various session state calls | |
762 | * XXX Should be #define's | |
763 | * The socket state stuff needs work, these often get call 2 or 3 | |
764 | * times each when only 1 was needed | |
765 | */ | |
766 | void soisfconnecting(struct socket *so) | |
767 | { | |
768 | so->so_state &= ~(SS_NOFDREF | SS_ISFCONNECTED | SS_FCANTRCVMORE | | |
769 | SS_FCANTSENDMORE | SS_FWDRAIN); | |
770 | so->so_state |= SS_ISFCONNECTING; /* Clobber other states */ | |
771 | } | |
772 | ||
773 | void soisfconnected(struct socket *so) | |
774 | { | |
775 | so->so_state &= ~(SS_ISFCONNECTING | SS_FWDRAIN | SS_NOFDREF); | |
776 | so->so_state |= SS_ISFCONNECTED; /* Clobber other states */ | |
777 | } | |
778 | ||
779 | static void sofcantrcvmore(struct socket *so) | |
780 | { | |
781 | if ((so->so_state & SS_NOFDREF) == 0) { | |
782 | shutdown(so->s, 0); | |
783 | } | |
784 | so->so_state &= ~(SS_ISFCONNECTING); | |
785 | if (so->so_state & SS_FCANTSENDMORE) { | |
786 | so->so_state &= SS_PERSISTENT_MASK; | |
787 | so->so_state |= SS_NOFDREF; /* Don't select it */ | |
788 | } else { | |
789 | so->so_state |= SS_FCANTRCVMORE; | |
790 | } | |
791 | } | |
792 | ||
793 | static void sofcantsendmore(struct socket *so) | |
794 | { | |
795 | if ((so->so_state & SS_NOFDREF) == 0) { | |
796 | shutdown(so->s, 1); /* send FIN to fhost */ | |
797 | } | |
798 | so->so_state &= ~(SS_ISFCONNECTING); | |
799 | if (so->so_state & SS_FCANTRCVMORE) { | |
800 | so->so_state &= SS_PERSISTENT_MASK; | |
801 | so->so_state |= SS_NOFDREF; /* as above */ | |
802 | } else { | |
803 | so->so_state |= SS_FCANTSENDMORE; | |
804 | } | |
805 | } | |
806 | ||
807 | /* | |
808 | * Set write drain mode | |
809 | * Set CANTSENDMORE once all data has been write()n | |
810 | */ | |
811 | void sofwdrain(struct socket *so) | |
812 | { | |
813 | if (so->so_rcv.sb_cc) | |
814 | so->so_state |= SS_FWDRAIN; | |
815 | else | |
816 | sofcantsendmore(so); | |
817 | } | |
818 | ||
819 | /* | |
820 | * Translate addr in host addr when it is a virtual address | |
821 | */ | |
822 | int sotranslate_out(struct socket *so, struct sockaddr_storage *addr) | |
823 | { | |
824 | int rc = 0; | |
825 | Slirp *slirp = so->slirp; | |
826 | struct sockaddr_in *sin = (struct sockaddr_in *)addr; | |
827 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; | |
828 | ||
829 | switch (addr->ss_family) { | |
830 | case AF_INET: | |
831 | if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == | |
832 | slirp->vnetwork_addr.s_addr) { | |
833 | /* It's an alias */ | |
834 | if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) { | |
835 | if (get_dns_addr(&sin->sin_addr) >= 0) { | |
836 | goto ret; | |
837 | } | |
838 | } | |
839 | if (slirp->disable_host_loopback) { | |
840 | rc = -1; | |
841 | errno = EPERM; | |
842 | goto ret; | |
843 | } else { | |
844 | sin->sin_addr = loopback_addr; | |
845 | } | |
846 | } | |
847 | break; | |
848 | case AF_INET6: | |
849 | if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6, | |
850 | slirp->vprefix_len)) { | |
851 | if (in6_equal(&so->so_faddr6, &slirp->vnameserver_addr6)) { | |
852 | uint32_t scope_id; | |
853 | if (get_dns6_addr(&sin6->sin6_addr, &scope_id) >= 0) { | |
854 | sin6->sin6_scope_id = scope_id; | |
855 | goto ret; | |
856 | } | |
857 | } | |
858 | if (slirp->disable_host_loopback) { | |
859 | rc = -1; | |
860 | errno = EPERM; | |
861 | goto ret; | |
862 | } else { | |
863 | sin6->sin6_addr = in6addr_loopback; | |
864 | } | |
865 | } | |
866 | break; | |
867 | ||
868 | default: | |
869 | break; | |
870 | } | |
871 | ret: | |
872 | return rc; | |
873 | } | |
874 | ||
875 | void sotranslate_in(struct socket *so, struct sockaddr_storage *addr) | |
876 | { | |
877 | Slirp *slirp = so->slirp; | |
878 | struct sockaddr_in *sin = (struct sockaddr_in *)addr; | |
879 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; | |
880 | ||
881 | switch (addr->ss_family) { | |
882 | case AF_INET: | |
883 | if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == | |
884 | slirp->vnetwork_addr.s_addr) { | |
885 | uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr; | |
886 | ||
887 | if ((so->so_faddr.s_addr & inv_mask) == inv_mask) { | |
888 | sin->sin_addr = slirp->vhost_addr; | |
889 | } else if (sin->sin_addr.s_addr == loopback_addr.s_addr || | |
890 | so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { | |
891 | sin->sin_addr = so->so_faddr; | |
892 | } | |
893 | } | |
894 | break; | |
895 | ||
896 | case AF_INET6: | |
897 | if (in6_equal_net(&so->so_faddr6, &slirp->vprefix_addr6, | |
898 | slirp->vprefix_len)) { | |
899 | if (in6_equal(&sin6->sin6_addr, &in6addr_loopback) || | |
900 | !in6_equal(&so->so_faddr6, &slirp->vhost_addr6)) { | |
901 | sin6->sin6_addr = so->so_faddr6; | |
902 | } | |
903 | } | |
904 | break; | |
905 | ||
906 | default: | |
907 | break; | |
908 | } | |
909 | } | |
910 | ||
911 | /* | |
912 | * Translate connections from localhost to the real hostname | |
913 | */ | |
914 | void sotranslate_accept(struct socket *so) | |
915 | { | |
916 | Slirp *slirp = so->slirp; | |
917 | ||
918 | switch (so->so_ffamily) { | |
919 | case AF_INET: | |
920 | if (so->so_faddr.s_addr == INADDR_ANY || | |
921 | (so->so_faddr.s_addr & loopback_mask) == | |
922 | (loopback_addr.s_addr & loopback_mask)) { | |
923 | so->so_faddr = slirp->vhost_addr; | |
924 | } | |
925 | break; | |
926 | ||
927 | case AF_INET6: | |
928 | if (in6_equal(&so->so_faddr6, &in6addr_any) || | |
929 | in6_equal(&so->so_faddr6, &in6addr_loopback)) { | |
930 | so->so_faddr6 = slirp->vhost_addr6; | |
931 | } | |
932 | break; | |
933 | ||
934 | default: | |
935 | break; | |
936 | } | |
937 | } | |
938 | ||
939 | void sodrop(struct socket *s, int num) | |
940 | { | |
941 | if (sbdrop(&s->so_snd, num)) { | |
942 | s->slirp->cb->notify(s->slirp->opaque); | |
943 | } | |
944 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1995 Danny Gasparovski. | |
3 | */ | |
4 | ||
5 | #ifndef SLIRP_SOCKET_H | |
6 | #define SLIRP_SOCKET_H | |
7 | ||
8 | #include "misc.h" | |
9 | ||
10 | #define SO_EXPIRE 240000 | |
11 | #define SO_EXPIREFAST 10000 | |
12 | ||
13 | /* | |
14 | * Our socket structure | |
15 | */ | |
16 | ||
17 | union slirp_sockaddr { | |
18 | struct sockaddr_storage ss; | |
19 | struct sockaddr_in sin; | |
20 | struct sockaddr_in6 sin6; | |
21 | }; | |
22 | ||
23 | struct socket { | |
24 | struct socket *so_next, *so_prev; /* For a linked list of sockets */ | |
25 | ||
26 | int s; /* The actual socket */ | |
27 | struct gfwd_list *guestfwd; | |
28 | ||
29 | int pollfds_idx; /* GPollFD GArray index */ | |
30 | ||
31 | Slirp *slirp; /* managing slirp instance */ | |
32 | ||
33 | /* XXX union these with not-yet-used sbuf params */ | |
34 | struct mbuf *so_m; /* Pointer to the original SYN packet, | |
35 | * for non-blocking connect()'s, and | |
36 | * PING reply's */ | |
37 | struct tcpiphdr *so_ti; /* Pointer to the original ti within | |
38 | * so_mconn, for non-blocking connections */ | |
39 | uint32_t so_urgc; | |
40 | union slirp_sockaddr fhost; /* Foreign host */ | |
41 | #define so_faddr fhost.sin.sin_addr | |
42 | #define so_fport fhost.sin.sin_port | |
43 | #define so_faddr6 fhost.sin6.sin6_addr | |
44 | #define so_fport6 fhost.sin6.sin6_port | |
45 | #define so_ffamily fhost.ss.ss_family | |
46 | ||
47 | union slirp_sockaddr lhost; /* Local host */ | |
48 | #define so_laddr lhost.sin.sin_addr | |
49 | #define so_lport lhost.sin.sin_port | |
50 | #define so_laddr6 lhost.sin6.sin6_addr | |
51 | #define so_lport6 lhost.sin6.sin6_port | |
52 | #define so_lfamily lhost.ss.ss_family | |
53 | ||
54 | uint8_t so_iptos; /* Type of service */ | |
55 | uint8_t so_emu; /* Is the socket emulated? */ | |
56 | ||
57 | uint8_t so_type; /* Type of socket, UDP or TCP */ | |
58 | int32_t so_state; /* internal state flags SS_*, below */ | |
59 | ||
60 | struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ | |
61 | unsigned so_expire; /* When the socket will expire */ | |
62 | ||
63 | int so_queued; /* Number of packets queued from this socket */ | |
64 | int so_nqueued; /* Number of packets queued in a row | |
65 | * Used to determine when to "downgrade" a session | |
66 | * from fastq to batchq */ | |
67 | ||
68 | struct sbuf so_rcv; /* Receive buffer */ | |
69 | struct sbuf so_snd; /* Send buffer */ | |
70 | }; | |
71 | ||
72 | ||
73 | /* | |
74 | * Socket state bits. (peer means the host on the Internet, | |
75 | * local host means the host on the other end of the modem) | |
76 | */ | |
77 | #define SS_NOFDREF 0x001 /* No fd reference */ | |
78 | ||
79 | #define SS_ISFCONNECTING \ | |
80 | 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */ | |
81 | #define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */ | |
82 | #define SS_FCANTRCVMORE \ | |
83 | 0x008 /* Socket can't receive more from peer (for half-closes) */ | |
84 | #define SS_FCANTSENDMORE \ | |
85 | 0x010 /* Socket can't send more to peer (for half-closes) */ | |
86 | #define SS_FWDRAIN \ | |
87 | 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */ | |
88 | ||
89 | #define SS_CTL 0x080 | |
90 | #define SS_FACCEPTCONN \ | |
91 | 0x100 /* Socket is accepting connections from a host on the internet */ | |
92 | #define SS_FACCEPTONCE \ | |
93 | 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */ | |
94 | ||
95 | #define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */ | |
96 | #define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ | |
97 | #define SS_INCOMING \ | |
98 | 0x2000 /* Connection was initiated by a host on the internet */ | |
99 | ||
100 | static inline int sockaddr_equal(struct sockaddr_storage *a, | |
101 | struct sockaddr_storage *b) | |
102 | { | |
103 | if (a->ss_family != b->ss_family) { | |
104 | return 0; | |
105 | } | |
106 | ||
107 | switch (a->ss_family) { | |
108 | case AF_INET: { | |
109 | struct sockaddr_in *a4 = (struct sockaddr_in *)a; | |
110 | struct sockaddr_in *b4 = (struct sockaddr_in *)b; | |
111 | return a4->sin_addr.s_addr == b4->sin_addr.s_addr && | |
112 | a4->sin_port == b4->sin_port; | |
113 | } | |
114 | case AF_INET6: { | |
115 | struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)a; | |
116 | struct sockaddr_in6 *b6 = (struct sockaddr_in6 *)b; | |
117 | return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) && | |
118 | a6->sin6_port == b6->sin6_port); | |
119 | } | |
120 | default: | |
121 | g_assert_not_reached(); | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static inline socklen_t sockaddr_size(struct sockaddr_storage *a) | |
128 | { | |
129 | switch (a->ss_family) { | |
130 | case AF_INET: | |
131 | return sizeof(struct sockaddr_in); | |
132 | case AF_INET6: | |
133 | return sizeof(struct sockaddr_in6); | |
134 | default: | |
135 | g_assert_not_reached(); | |
136 | } | |
137 | } | |
138 | ||
139 | struct socket *solookup(struct socket **, struct socket *, | |
140 | struct sockaddr_storage *, struct sockaddr_storage *); | |
141 | struct socket *socreate(Slirp *); | |
142 | void sofree(struct socket *); | |
143 | int soread(struct socket *); | |
144 | int sorecvoob(struct socket *); | |
145 | int sosendoob(struct socket *); | |
146 | int sowrite(struct socket *); | |
147 | void sorecvfrom(struct socket *); | |
148 | int sosendto(struct socket *, struct mbuf *); | |
149 | struct socket *tcp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int); | |
150 | void soisfconnecting(register struct socket *); | |
151 | void soisfconnected(register struct socket *); | |
152 | void sofwdrain(struct socket *); | |
153 | struct iovec; /* For win32 */ | |
154 | size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); | |
155 | int soreadbuf(struct socket *so, const char *buf, int size); | |
156 | ||
157 | int sotranslate_out(struct socket *, struct sockaddr_storage *); | |
158 | void sotranslate_in(struct socket *, struct sockaddr_storage *); | |
159 | void sotranslate_accept(struct socket *); | |
160 | void sodrop(struct socket *, int num); | |
161 | ||
162 | ||
163 | #endif /* SLIRP_SOCKET_H */ |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * libslirp | |
3 | * | |
4 | * Copyright (c) 2004-2008 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "slirp.h" | |
25 | #include "vmstate.h" | |
26 | #include "stream.h" | |
27 | ||
28 | static int slirp_tcp_post_load(void *opaque, int version) | |
29 | { | |
30 | tcp_template((struct tcpcb *)opaque); | |
31 | ||
32 | return 0; | |
33 | } | |
34 | ||
35 | static const VMStateDescription vmstate_slirp_tcp = { | |
36 | .name = "slirp-tcp", | |
37 | .version_id = 0, | |
38 | .post_load = slirp_tcp_post_load, | |
39 | .fields = (VMStateField[]){ VMSTATE_INT16(t_state, struct tcpcb), | |
40 | VMSTATE_INT16_ARRAY(t_timer, struct tcpcb, | |
41 | TCPT_NTIMERS), | |
42 | VMSTATE_INT16(t_rxtshift, struct tcpcb), | |
43 | VMSTATE_INT16(t_rxtcur, struct tcpcb), | |
44 | VMSTATE_INT16(t_dupacks, struct tcpcb), | |
45 | VMSTATE_UINT16(t_maxseg, struct tcpcb), | |
46 | VMSTATE_UINT8(t_force, struct tcpcb), | |
47 | VMSTATE_UINT16(t_flags, struct tcpcb), | |
48 | VMSTATE_UINT32(snd_una, struct tcpcb), | |
49 | VMSTATE_UINT32(snd_nxt, struct tcpcb), | |
50 | VMSTATE_UINT32(snd_up, struct tcpcb), | |
51 | VMSTATE_UINT32(snd_wl1, struct tcpcb), | |
52 | VMSTATE_UINT32(snd_wl2, struct tcpcb), | |
53 | VMSTATE_UINT32(iss, struct tcpcb), | |
54 | VMSTATE_UINT32(snd_wnd, struct tcpcb), | |
55 | VMSTATE_UINT32(rcv_wnd, struct tcpcb), | |
56 | VMSTATE_UINT32(rcv_nxt, struct tcpcb), | |
57 | VMSTATE_UINT32(rcv_up, struct tcpcb), | |
58 | VMSTATE_UINT32(irs, struct tcpcb), | |
59 | VMSTATE_UINT32(rcv_adv, struct tcpcb), | |
60 | VMSTATE_UINT32(snd_max, struct tcpcb), | |
61 | VMSTATE_UINT32(snd_cwnd, struct tcpcb), | |
62 | VMSTATE_UINT32(snd_ssthresh, struct tcpcb), | |
63 | VMSTATE_INT16(t_idle, struct tcpcb), | |
64 | VMSTATE_INT16(t_rtt, struct tcpcb), | |
65 | VMSTATE_UINT32(t_rtseq, struct tcpcb), | |
66 | VMSTATE_INT16(t_srtt, struct tcpcb), | |
67 | VMSTATE_INT16(t_rttvar, struct tcpcb), | |
68 | VMSTATE_UINT16(t_rttmin, struct tcpcb), | |
69 | VMSTATE_UINT32(max_sndwnd, struct tcpcb), | |
70 | VMSTATE_UINT8(t_oobflags, struct tcpcb), | |
71 | VMSTATE_UINT8(t_iobc, struct tcpcb), | |
72 | VMSTATE_INT16(t_softerror, struct tcpcb), | |
73 | VMSTATE_UINT8(snd_scale, struct tcpcb), | |
74 | VMSTATE_UINT8(rcv_scale, struct tcpcb), | |
75 | VMSTATE_UINT8(request_r_scale, struct tcpcb), | |
76 | VMSTATE_UINT8(requested_s_scale, struct tcpcb), | |
77 | VMSTATE_UINT32(ts_recent, struct tcpcb), | |
78 | VMSTATE_UINT32(ts_recent_age, struct tcpcb), | |
79 | VMSTATE_UINT32(last_ack_sent, struct tcpcb), | |
80 | VMSTATE_END_OF_LIST() } | |
81 | }; | |
82 | ||
83 | /* The sbuf has a pair of pointers that are migrated as offsets; | |
84 | * we calculate the offsets and restore the pointers using | |
85 | * pre_save/post_load on a tmp structure. | |
86 | */ | |
87 | struct sbuf_tmp { | |
88 | struct sbuf *parent; | |
89 | uint32_t roff, woff; | |
90 | }; | |
91 | ||
92 | static int sbuf_tmp_pre_save(void *opaque) | |
93 | { | |
94 | struct sbuf_tmp *tmp = opaque; | |
95 | tmp->woff = tmp->parent->sb_wptr - tmp->parent->sb_data; | |
96 | tmp->roff = tmp->parent->sb_rptr - tmp->parent->sb_data; | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | static int sbuf_tmp_post_load(void *opaque, int version) | |
102 | { | |
103 | struct sbuf_tmp *tmp = opaque; | |
104 | uint32_t requested_len = tmp->parent->sb_datalen; | |
105 | ||
106 | /* Allocate the buffer space used by the field after the tmp */ | |
107 | sbreserve(tmp->parent, tmp->parent->sb_datalen); | |
108 | ||
109 | if (tmp->parent->sb_datalen != requested_len) { | |
110 | return -ENOMEM; | |
111 | } | |
112 | if (tmp->woff >= requested_len || tmp->roff >= requested_len) { | |
113 | g_critical("invalid sbuf offsets r/w=%u/%u len=%u", tmp->roff, | |
114 | tmp->woff, requested_len); | |
115 | return -EINVAL; | |
116 | } | |
117 | ||
118 | tmp->parent->sb_wptr = tmp->parent->sb_data + tmp->woff; | |
119 | tmp->parent->sb_rptr = tmp->parent->sb_data + tmp->roff; | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
124 | ||
125 | static const VMStateDescription vmstate_slirp_sbuf_tmp = { | |
126 | .name = "slirp-sbuf-tmp", | |
127 | .post_load = sbuf_tmp_post_load, | |
128 | .pre_save = sbuf_tmp_pre_save, | |
129 | .version_id = 0, | |
130 | .fields = (VMStateField[]){ VMSTATE_UINT32(woff, struct sbuf_tmp), | |
131 | VMSTATE_UINT32(roff, struct sbuf_tmp), | |
132 | VMSTATE_END_OF_LIST() } | |
133 | }; | |
134 | ||
135 | static const VMStateDescription vmstate_slirp_sbuf = { | |
136 | .name = "slirp-sbuf", | |
137 | .version_id = 0, | |
138 | .fields = (VMStateField[]){ VMSTATE_UINT32(sb_cc, struct sbuf), | |
139 | VMSTATE_UINT32(sb_datalen, struct sbuf), | |
140 | VMSTATE_WITH_TMP(struct sbuf, struct sbuf_tmp, | |
141 | vmstate_slirp_sbuf_tmp), | |
142 | VMSTATE_VBUFFER_UINT32(sb_data, struct sbuf, 0, | |
143 | NULL, sb_datalen), | |
144 | VMSTATE_END_OF_LIST() } | |
145 | }; | |
146 | ||
147 | static bool slirp_older_than_v4(void *opaque, int version_id) | |
148 | { | |
149 | return version_id < 4; | |
150 | } | |
151 | ||
152 | static bool slirp_family_inet(void *opaque, int version_id) | |
153 | { | |
154 | union slirp_sockaddr *ssa = (union slirp_sockaddr *)opaque; | |
155 | return ssa->ss.ss_family == AF_INET; | |
156 | } | |
157 | ||
158 | static int slirp_socket_pre_load(void *opaque) | |
159 | { | |
160 | struct socket *so = opaque; | |
161 | if (tcp_attach(so) < 0) { | |
162 | return -ENOMEM; | |
163 | } | |
164 | /* Older versions don't load these fields */ | |
165 | so->so_ffamily = AF_INET; | |
166 | so->so_lfamily = AF_INET; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | #ifndef _WIN32 | |
171 | #define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_UINT32_TEST(f, s, t) | |
172 | #else | |
173 | /* Win uses u_long rather than uint32_t - but it's still 32bits long */ | |
174 | #define VMSTATE_SIN4_ADDR(f, s, t) \ | |
175 | VMSTATE_SINGLE_TEST(f, s, t, 0, slirp_vmstate_info_uint32, u_long) | |
176 | #endif | |
177 | ||
178 | /* The OS provided ss_family field isn't that portable; it's size | |
179 | * and type varies (16/8 bit, signed, unsigned) | |
180 | * and the values it contains aren't fully portable. | |
181 | */ | |
182 | typedef struct SS_FamilyTmpStruct { | |
183 | union slirp_sockaddr *parent; | |
184 | uint16_t portable_family; | |
185 | } SS_FamilyTmpStruct; | |
186 | ||
187 | #define SS_FAMILY_MIG_IPV4 2 /* Linux, BSD, Win... */ | |
188 | #define SS_FAMILY_MIG_IPV6 10 /* Linux */ | |
189 | #define SS_FAMILY_MIG_OTHER 0xffff | |
190 | ||
191 | static int ss_family_pre_save(void *opaque) | |
192 | { | |
193 | SS_FamilyTmpStruct *tss = opaque; | |
194 | ||
195 | tss->portable_family = SS_FAMILY_MIG_OTHER; | |
196 | ||
197 | if (tss->parent->ss.ss_family == AF_INET) { | |
198 | tss->portable_family = SS_FAMILY_MIG_IPV4; | |
199 | } else if (tss->parent->ss.ss_family == AF_INET6) { | |
200 | tss->portable_family = SS_FAMILY_MIG_IPV6; | |
201 | } | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static int ss_family_post_load(void *opaque, int version_id) | |
207 | { | |
208 | SS_FamilyTmpStruct *tss = opaque; | |
209 | ||
210 | switch (tss->portable_family) { | |
211 | case SS_FAMILY_MIG_IPV4: | |
212 | tss->parent->ss.ss_family = AF_INET; | |
213 | break; | |
214 | case SS_FAMILY_MIG_IPV6: | |
215 | case 23: /* compatibility: AF_INET6 from mingw */ | |
216 | case 28: /* compatibility: AF_INET6 from FreeBSD sys/socket.h */ | |
217 | tss->parent->ss.ss_family = AF_INET6; | |
218 | break; | |
219 | default: | |
220 | g_critical("invalid ss_family type %x", tss->portable_family); | |
221 | return -EINVAL; | |
222 | } | |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
227 | static const VMStateDescription vmstate_slirp_ss_family = { | |
228 | .name = "slirp-socket-addr/ss_family", | |
229 | .pre_save = ss_family_pre_save, | |
230 | .post_load = ss_family_post_load, | |
231 | .fields = | |
232 | (VMStateField[]){ VMSTATE_UINT16(portable_family, SS_FamilyTmpStruct), | |
233 | VMSTATE_END_OF_LIST() } | |
234 | }; | |
235 | ||
236 | static const VMStateDescription vmstate_slirp_socket_addr = { | |
237 | .name = "slirp-socket-addr", | |
238 | .version_id = 4, | |
239 | .fields = | |
240 | (VMStateField[]){ | |
241 | VMSTATE_WITH_TMP(union slirp_sockaddr, SS_FamilyTmpStruct, | |
242 | vmstate_slirp_ss_family), | |
243 | VMSTATE_SIN4_ADDR(sin.sin_addr.s_addr, union slirp_sockaddr, | |
244 | slirp_family_inet), | |
245 | VMSTATE_UINT16_TEST(sin.sin_port, union slirp_sockaddr, | |
246 | slirp_family_inet), | |
247 | ||
248 | #if 0 | |
249 | /* Untested: Needs checking by someone with IPv6 test */ | |
250 | VMSTATE_BUFFER_TEST(sin6.sin6_addr, union slirp_sockaddr, | |
251 | slirp_family_inet6), | |
252 | VMSTATE_UINT16_TEST(sin6.sin6_port, union slirp_sockaddr, | |
253 | slirp_family_inet6), | |
254 | VMSTATE_UINT32_TEST(sin6.sin6_flowinfo, union slirp_sockaddr, | |
255 | slirp_family_inet6), | |
256 | VMSTATE_UINT32_TEST(sin6.sin6_scope_id, union slirp_sockaddr, | |
257 | slirp_family_inet6), | |
258 | #endif | |
259 | ||
260 | VMSTATE_END_OF_LIST() } | |
261 | }; | |
262 | ||
263 | static const VMStateDescription vmstate_slirp_socket = { | |
264 | .name = "slirp-socket", | |
265 | .version_id = 4, | |
266 | .pre_load = slirp_socket_pre_load, | |
267 | .fields = | |
268 | (VMStateField[]){ | |
269 | VMSTATE_UINT32(so_urgc, struct socket), | |
270 | /* Pre-v4 versions */ | |
271 | VMSTATE_SIN4_ADDR(so_faddr.s_addr, struct socket, | |
272 | slirp_older_than_v4), | |
273 | VMSTATE_SIN4_ADDR(so_laddr.s_addr, struct socket, | |
274 | slirp_older_than_v4), | |
275 | VMSTATE_UINT16_TEST(so_fport, struct socket, slirp_older_than_v4), | |
276 | VMSTATE_UINT16_TEST(so_lport, struct socket, slirp_older_than_v4), | |
277 | /* v4 and newer */ | |
278 | VMSTATE_STRUCT(fhost, struct socket, 4, vmstate_slirp_socket_addr, | |
279 | union slirp_sockaddr), | |
280 | VMSTATE_STRUCT(lhost, struct socket, 4, vmstate_slirp_socket_addr, | |
281 | union slirp_sockaddr), | |
282 | ||
283 | VMSTATE_UINT8(so_iptos, struct socket), | |
284 | VMSTATE_UINT8(so_emu, struct socket), | |
285 | VMSTATE_UINT8(so_type, struct socket), | |
286 | VMSTATE_INT32(so_state, struct socket), | |
287 | VMSTATE_STRUCT(so_rcv, struct socket, 0, vmstate_slirp_sbuf, | |
288 | struct sbuf), | |
289 | VMSTATE_STRUCT(so_snd, struct socket, 0, vmstate_slirp_sbuf, | |
290 | struct sbuf), | |
291 | VMSTATE_STRUCT_POINTER(so_tcpcb, struct socket, vmstate_slirp_tcp, | |
292 | struct tcpcb), | |
293 | VMSTATE_END_OF_LIST() } | |
294 | }; | |
295 | ||
296 | static const VMStateDescription vmstate_slirp_bootp_client = { | |
297 | .name = "slirp_bootpclient", | |
298 | .fields = (VMStateField[]){ VMSTATE_UINT16(allocated, BOOTPClient), | |
299 | VMSTATE_BUFFER(macaddr, BOOTPClient), | |
300 | VMSTATE_END_OF_LIST() } | |
301 | }; | |
302 | ||
303 | static const VMStateDescription vmstate_slirp = { | |
304 | .name = "slirp", | |
305 | .version_id = 4, | |
306 | .fields = (VMStateField[]){ VMSTATE_UINT16_V(ip_id, Slirp, 2), | |
307 | VMSTATE_STRUCT_ARRAY( | |
308 | bootp_clients, Slirp, NB_BOOTP_CLIENTS, 3, | |
309 | vmstate_slirp_bootp_client, BOOTPClient), | |
310 | VMSTATE_END_OF_LIST() } | |
311 | }; | |
312 | ||
313 | void slirp_state_save(Slirp *slirp, SlirpWriteCb write_cb, void *opaque) | |
314 | { | |
315 | struct gfwd_list *ex_ptr; | |
316 | SlirpOStream f = { | |
317 | .write_cb = write_cb, | |
318 | .opaque = opaque, | |
319 | }; | |
320 | ||
321 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) | |
322 | if (ex_ptr->write_cb) { | |
323 | struct socket *so; | |
324 | so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, | |
325 | ntohs(ex_ptr->ex_fport)); | |
326 | if (!so) { | |
327 | continue; | |
328 | } | |
329 | ||
330 | slirp_ostream_write_u8(&f, 42); | |
331 | slirp_vmstate_save_state(&f, &vmstate_slirp_socket, so); | |
332 | } | |
333 | slirp_ostream_write_u8(&f, 0); | |
334 | ||
335 | slirp_vmstate_save_state(&f, &vmstate_slirp, slirp); | |
336 | } | |
337 | ||
338 | ||
339 | int slirp_state_load(Slirp *slirp, int version_id, SlirpReadCb read_cb, | |
340 | void *opaque) | |
341 | { | |
342 | struct gfwd_list *ex_ptr; | |
343 | SlirpIStream f = { | |
344 | .read_cb = read_cb, | |
345 | .opaque = opaque, | |
346 | }; | |
347 | ||
348 | while (slirp_istream_read_u8(&f)) { | |
349 | int ret; | |
350 | struct socket *so = socreate(slirp); | |
351 | ||
352 | ret = | |
353 | slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id); | |
354 | if (ret < 0) { | |
355 | return ret; | |
356 | } | |
357 | ||
358 | if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) != | |
359 | slirp->vnetwork_addr.s_addr) { | |
360 | return -EINVAL; | |
361 | } | |
362 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { | |
363 | if (ex_ptr->write_cb && | |
364 | so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr && | |
365 | so->so_fport == ex_ptr->ex_fport) { | |
366 | break; | |
367 | } | |
368 | } | |
369 | if (!ex_ptr) { | |
370 | return -EINVAL; | |
371 | } | |
372 | } | |
373 | ||
374 | return slirp_vmstate_load_state(&f, &vmstate_slirp, slirp, version_id); | |
375 | } | |
376 | ||
377 | int slirp_state_version(void) | |
378 | { | |
379 | return 4; | |
380 | } |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * libslirp io streams | |
3 | * | |
4 | * Copyright (c) 2018 Red Hat, Inc. | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "stream.h" | |
25 | #include <glib.h> | |
26 | ||
27 | bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size) | |
28 | { | |
29 | return f->read_cb(buf, size, f->opaque) == size; | |
30 | } | |
31 | ||
32 | bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size) | |
33 | { | |
34 | return f->write_cb(buf, size, f->opaque) == size; | |
35 | } | |
36 | ||
37 | uint8_t slirp_istream_read_u8(SlirpIStream *f) | |
38 | { | |
39 | uint8_t b; | |
40 | ||
41 | if (slirp_istream_read(f, &b, sizeof(b))) { | |
42 | return b; | |
43 | } | |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b) | |
49 | { | |
50 | return slirp_ostream_write(f, &b, sizeof(b)); | |
51 | } | |
52 | ||
53 | uint16_t slirp_istream_read_u16(SlirpIStream *f) | |
54 | { | |
55 | uint16_t b; | |
56 | ||
57 | if (slirp_istream_read(f, &b, sizeof(b))) { | |
58 | return GUINT16_FROM_BE(b); | |
59 | } | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b) | |
65 | { | |
66 | b = GUINT16_TO_BE(b); | |
67 | return slirp_ostream_write(f, &b, sizeof(b)); | |
68 | } | |
69 | ||
70 | uint32_t slirp_istream_read_u32(SlirpIStream *f) | |
71 | { | |
72 | uint32_t b; | |
73 | ||
74 | if (slirp_istream_read(f, &b, sizeof(b))) { | |
75 | return GUINT32_FROM_BE(b); | |
76 | } | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b) | |
82 | { | |
83 | b = GUINT32_TO_BE(b); | |
84 | return slirp_ostream_write(f, &b, sizeof(b)); | |
85 | } | |
86 | ||
87 | int16_t slirp_istream_read_i16(SlirpIStream *f) | |
88 | { | |
89 | int16_t b; | |
90 | ||
91 | if (slirp_istream_read(f, &b, sizeof(b))) { | |
92 | return GINT16_FROM_BE(b); | |
93 | } | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b) | |
99 | { | |
100 | b = GINT16_TO_BE(b); | |
101 | return slirp_ostream_write(f, &b, sizeof(b)); | |
102 | } | |
103 | ||
104 | int32_t slirp_istream_read_i32(SlirpIStream *f) | |
105 | { | |
106 | int32_t b; | |
107 | ||
108 | if (slirp_istream_read(f, &b, sizeof(b))) { | |
109 | return GINT32_FROM_BE(b); | |
110 | } | |
111 | ||
112 | return 0; | |
113 | } | |
114 | ||
115 | bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b) | |
116 | { | |
117 | b = GINT32_TO_BE(b); | |
118 | return slirp_ostream_write(f, &b, sizeof(b)); | |
119 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | #ifndef STREAM_H_ | |
2 | #define STREAM_H_ | |
3 | ||
4 | #include "libslirp.h" | |
5 | ||
6 | typedef struct SlirpIStream { | |
7 | SlirpReadCb read_cb; | |
8 | void *opaque; | |
9 | } SlirpIStream; | |
10 | ||
11 | typedef struct SlirpOStream { | |
12 | SlirpWriteCb write_cb; | |
13 | void *opaque; | |
14 | } SlirpOStream; | |
15 | ||
16 | bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size); | |
17 | bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size); | |
18 | ||
19 | uint8_t slirp_istream_read_u8(SlirpIStream *f); | |
20 | bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b); | |
21 | ||
22 | uint16_t slirp_istream_read_u16(SlirpIStream *f); | |
23 | bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b); | |
24 | ||
25 | uint32_t slirp_istream_read_u32(SlirpIStream *f); | |
26 | bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b); | |
27 | ||
28 | int16_t slirp_istream_read_i16(SlirpIStream *f); | |
29 | bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b); | |
30 | ||
31 | int32_t slirp_istream_read_i32(SlirpIStream *f); | |
32 | bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b); | |
33 | ||
34 | #endif /* STREAM_H_ */ |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp.h 8.1 (Berkeley) 6/10/93 | |
30 | * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef TCP_H | |
34 | #define TCP_H | |
35 | ||
36 | #include <glib.h> | |
37 | ||
38 | typedef uint32_t tcp_seq; | |
39 | ||
40 | #define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ | |
41 | #define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ | |
42 | ||
43 | #define TCP_SNDSPACE 1024 * 128 | |
44 | #define TCP_RCVSPACE 1024 * 128 | |
45 | #define TCP_MAXSEG_MAX 32768 | |
46 | ||
47 | /* | |
48 | * TCP header. | |
49 | * Per RFC 793, September, 1981. | |
50 | */ | |
51 | #define tcphdr slirp_tcphdr | |
52 | struct tcphdr { | |
53 | uint16_t th_sport; /* source port */ | |
54 | uint16_t th_dport; /* destination port */ | |
55 | tcp_seq th_seq; /* sequence number */ | |
56 | tcp_seq th_ack; /* acknowledgement number */ | |
57 | #if G_BYTE_ORDER == G_BIG_ENDIAN | |
58 | uint8_t th_off : 4, /* data offset */ | |
59 | th_x2 : 4; /* (unused) */ | |
60 | #else | |
61 | uint8_t th_x2 : 4, /* (unused) */ | |
62 | th_off : 4; /* data offset */ | |
63 | #endif | |
64 | uint8_t th_flags; | |
65 | uint16_t th_win; /* window */ | |
66 | uint16_t th_sum; /* checksum */ | |
67 | uint16_t th_urp; /* urgent pointer */ | |
68 | }; | |
69 | ||
70 | #include "tcp_var.h" | |
71 | ||
72 | #ifndef TH_FIN | |
73 | #define TH_FIN 0x01 | |
74 | #define TH_SYN 0x02 | |
75 | #define TH_RST 0x04 | |
76 | #define TH_PUSH 0x08 | |
77 | #define TH_ACK 0x10 | |
78 | #define TH_URG 0x20 | |
79 | #endif | |
80 | ||
81 | #ifndef TCPOPT_EOL | |
82 | #define TCPOPT_EOL 0 | |
83 | #define TCPOPT_NOP 1 | |
84 | #define TCPOPT_MAXSEG 2 | |
85 | #define TCPOPT_WINDOW 3 | |
86 | #define TCPOPT_SACK_PERMITTED 4 /* Experimental */ | |
87 | #define TCPOPT_SACK 5 /* Experimental */ | |
88 | #define TCPOPT_TIMESTAMP 8 | |
89 | ||
90 | #define TCPOPT_TSTAMP_HDR \ | |
91 | (TCPOPT_NOP << 24 | TCPOPT_NOP << 16 | TCPOPT_TIMESTAMP << 8 | \ | |
92 | TCPOLEN_TIMESTAMP) | |
93 | #endif | |
94 | ||
95 | #ifndef TCPOLEN_MAXSEG | |
96 | #define TCPOLEN_MAXSEG 4 | |
97 | #define TCPOLEN_WINDOW 3 | |
98 | #define TCPOLEN_SACK_PERMITTED 2 | |
99 | #define TCPOLEN_TIMESTAMP 10 | |
100 | #define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP + 2) /* appendix A */ | |
101 | #endif | |
102 | ||
103 | #undef TCP_MAXWIN | |
104 | #define TCP_MAXWIN 65535 /* largest value for (unscaled) window */ | |
105 | ||
106 | #undef TCP_MAX_WINSHIFT | |
107 | #define TCP_MAX_WINSHIFT 14 /* maximum window shift */ | |
108 | ||
109 | /* | |
110 | * User-settable options (used with setsockopt). | |
111 | * | |
112 | * We don't use the system headers on unix because we have conflicting | |
113 | * local structures. We can't avoid the system definitions on Windows, | |
114 | * so we undefine them. | |
115 | */ | |
116 | #undef TCP_NODELAY | |
117 | #define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ | |
118 | #undef TCP_MAXSEG | |
119 | ||
120 | /* | |
121 | * TCP FSM state definitions. | |
122 | * Per RFC793, September, 1981. | |
123 | */ | |
124 | ||
125 | #define TCP_NSTATES 11 | |
126 | ||
127 | #define TCPS_CLOSED 0 /* closed */ | |
128 | #define TCPS_LISTEN 1 /* listening for connection */ | |
129 | #define TCPS_SYN_SENT 2 /* active, have sent syn */ | |
130 | #define TCPS_SYN_RECEIVED 3 /* have send and received syn */ | |
131 | /* states < TCPS_ESTABLISHED are those where connections not established */ | |
132 | #define TCPS_ESTABLISHED 4 /* established */ | |
133 | #define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ | |
134 | /* states > TCPS_CLOSE_WAIT are those where user has closed */ | |
135 | #define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ | |
136 | #define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ | |
137 | #define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ | |
138 | /* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ | |
139 | #define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ | |
140 | #define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ | |
141 | ||
142 | #define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED) | |
143 | #define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED) | |
144 | #define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT) | |
145 | ||
146 | /* | |
147 | * TCP sequence numbers are 32 bit integers operated | |
148 | * on with modular arithmetic. These macros can be | |
149 | * used to compare such integers. | |
150 | */ | |
151 | #define SEQ_LT(a, b) ((int)((a) - (b)) < 0) | |
152 | #define SEQ_LEQ(a, b) ((int)((a) - (b)) <= 0) | |
153 | #define SEQ_GT(a, b) ((int)((a) - (b)) > 0) | |
154 | #define SEQ_GEQ(a, b) ((int)((a) - (b)) >= 0) | |
155 | ||
156 | /* | |
157 | * Macros to initialize tcp sequence numbers for | |
158 | * send and receive from initial send and receive | |
159 | * sequence numbers. | |
160 | */ | |
161 | #define tcp_rcvseqinit(tp) (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1 | |
162 | ||
163 | #define tcp_sendseqinit(tp) \ | |
164 | (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss | |
165 | ||
166 | #define TCP_ISSINCR (125 * 1024) /* increment for tcp_iss each second */ | |
167 | ||
168 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94 | |
30 | * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | */ | |
37 | ||
38 | #include "slirp.h" | |
39 | #include "ip_icmp.h" | |
40 | ||
41 | #define TCPREXMTTHRESH 3 | |
42 | ||
43 | #define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ) | |
44 | ||
45 | /* for modulo comparisons of timestamps */ | |
46 | #define TSTMP_LT(a, b) ((int)((a) - (b)) < 0) | |
47 | #define TSTMP_GEQ(a, b) ((int)((a) - (b)) >= 0) | |
48 | ||
49 | /* | |
50 | * Insert segment ti into reassembly queue of tcp with | |
51 | * control block tp. Return TH_FIN if reassembly now includes | |
52 | * a segment with FIN. The macro form does the common case inline | |
53 | * (segment is the next to be received on an established connection, | |
54 | * and the queue is empty), avoiding linkage into and removal | |
55 | * from the queue and repetition of various conversions. | |
56 | * Set DELACK for segments received in order, but ack immediately | |
57 | * when segments are out of order (so fast retransmit can work). | |
58 | */ | |
59 | #define TCP_REASS(tp, ti, m, so, flags) \ | |
60 | { \ | |
61 | if ((ti)->ti_seq == (tp)->rcv_nxt && tcpfrag_list_empty(tp) && \ | |
62 | (tp)->t_state == TCPS_ESTABLISHED) { \ | |
63 | tp->t_flags |= TF_DELACK; \ | |
64 | (tp)->rcv_nxt += (ti)->ti_len; \ | |
65 | flags = (ti)->ti_flags & TH_FIN; \ | |
66 | if (so->so_emu) { \ | |
67 | if (tcp_emu((so), (m))) \ | |
68 | sbappend(so, (m)); \ | |
69 | } else \ | |
70 | sbappend((so), (m)); \ | |
71 | } else { \ | |
72 | (flags) = tcp_reass((tp), (ti), (m)); \ | |
73 | tp->t_flags |= TF_ACKNOW; \ | |
74 | } \ | |
75 | } | |
76 | ||
77 | static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, | |
78 | struct tcpiphdr *ti); | |
79 | static void tcp_xmit_timer(register struct tcpcb *tp, int rtt); | |
80 | ||
81 | static int tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, | |
82 | struct mbuf *m) | |
83 | { | |
84 | register struct tcpiphdr *q; | |
85 | struct socket *so = tp->t_socket; | |
86 | int flags; | |
87 | ||
88 | /* | |
89 | * Call with ti==NULL after become established to | |
90 | * force pre-ESTABLISHED data up to user socket. | |
91 | */ | |
92 | if (ti == NULL) | |
93 | goto present; | |
94 | ||
95 | /* | |
96 | * Find a segment which begins after this one does. | |
97 | */ | |
98 | for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp); | |
99 | q = tcpiphdr_next(q)) | |
100 | if (SEQ_GT(q->ti_seq, ti->ti_seq)) | |
101 | break; | |
102 | ||
103 | /* | |
104 | * If there is a preceding segment, it may provide some of | |
105 | * our data already. If so, drop the data from the incoming | |
106 | * segment. If it provides all of our data, drop us. | |
107 | */ | |
108 | if (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) { | |
109 | register int i; | |
110 | q = tcpiphdr_prev(q); | |
111 | /* conversion to int (in i) handles seq wraparound */ | |
112 | i = q->ti_seq + q->ti_len - ti->ti_seq; | |
113 | if (i > 0) { | |
114 | if (i >= ti->ti_len) { | |
115 | m_free(m); | |
116 | /* | |
117 | * Try to present any queued data | |
118 | * at the left window edge to the user. | |
119 | * This is needed after the 3-WHS | |
120 | * completes. | |
121 | */ | |
122 | goto present; /* ??? */ | |
123 | } | |
124 | m_adj(m, i); | |
125 | ti->ti_len -= i; | |
126 | ti->ti_seq += i; | |
127 | } | |
128 | q = tcpiphdr_next(q); | |
129 | } | |
130 | ti->ti_mbuf = m; | |
131 | ||
132 | /* | |
133 | * While we overlap succeeding segments trim them or, | |
134 | * if they are completely covered, dequeue them. | |
135 | */ | |
136 | while (!tcpfrag_list_end(q, tp)) { | |
137 | register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq; | |
138 | if (i <= 0) | |
139 | break; | |
140 | if (i < q->ti_len) { | |
141 | q->ti_seq += i; | |
142 | q->ti_len -= i; | |
143 | m_adj(q->ti_mbuf, i); | |
144 | break; | |
145 | } | |
146 | q = tcpiphdr_next(q); | |
147 | m = tcpiphdr_prev(q)->ti_mbuf; | |
148 | remque(tcpiphdr2qlink(tcpiphdr_prev(q))); | |
149 | m_free(m); | |
150 | } | |
151 | ||
152 | /* | |
153 | * Stick new segment in its place. | |
154 | */ | |
155 | insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); | |
156 | ||
157 | present: | |
158 | /* | |
159 | * Present data to user, advancing rcv_nxt through | |
160 | * completed sequence space. | |
161 | */ | |
162 | if (!TCPS_HAVEESTABLISHED(tp->t_state)) | |
163 | return (0); | |
164 | ti = tcpfrag_list_first(tp); | |
165 | if (tcpfrag_list_end(ti, tp) || ti->ti_seq != tp->rcv_nxt) | |
166 | return (0); | |
167 | if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len) | |
168 | return (0); | |
169 | do { | |
170 | tp->rcv_nxt += ti->ti_len; | |
171 | flags = ti->ti_flags & TH_FIN; | |
172 | remque(tcpiphdr2qlink(ti)); | |
173 | m = ti->ti_mbuf; | |
174 | ti = tcpiphdr_next(ti); | |
175 | if (so->so_state & SS_FCANTSENDMORE) | |
176 | m_free(m); | |
177 | else { | |
178 | if (so->so_emu) { | |
179 | if (tcp_emu(so, m)) | |
180 | sbappend(so, m); | |
181 | } else | |
182 | sbappend(so, m); | |
183 | } | |
184 | } while (ti != (struct tcpiphdr *)tp && ti->ti_seq == tp->rcv_nxt); | |
185 | return (flags); | |
186 | } | |
187 | ||
188 | /* | |
189 | * TCP input routine, follows pages 65-76 of the | |
190 | * protocol specification dated September, 1981 very closely. | |
191 | */ | |
192 | void tcp_input(struct mbuf *m, int iphlen, struct socket *inso, | |
193 | unsigned short af) | |
194 | { | |
195 | struct ip save_ip, *ip; | |
196 | struct ip6 save_ip6, *ip6; | |
197 | register struct tcpiphdr *ti; | |
198 | char *optp = NULL; | |
199 | int optlen = 0; | |
200 | int len, tlen, off; | |
201 | register struct tcpcb *tp = NULL; | |
202 | register int tiflags; | |
203 | struct socket *so = NULL; | |
204 | int todrop, acked, ourfinisacked, needoutput = 0; | |
205 | int iss = 0; | |
206 | uint32_t tiwin; | |
207 | int ret; | |
208 | struct sockaddr_storage lhost, fhost; | |
209 | struct sockaddr_in *lhost4, *fhost4; | |
210 | struct sockaddr_in6 *lhost6, *fhost6; | |
211 | struct gfwd_list *ex_ptr; | |
212 | Slirp *slirp; | |
213 | ||
214 | DEBUG_CALL("tcp_input"); | |
215 | DEBUG_ARG("m = %p iphlen = %2d inso = %p", m, iphlen, inso); | |
216 | ||
217 | /* | |
218 | * If called with m == 0, then we're continuing the connect | |
219 | */ | |
220 | if (m == NULL) { | |
221 | so = inso; | |
222 | slirp = so->slirp; | |
223 | ||
224 | /* Re-set a few variables */ | |
225 | tp = sototcpcb(so); | |
226 | m = so->so_m; | |
227 | so->so_m = NULL; | |
228 | ti = so->so_ti; | |
229 | tiwin = ti->ti_win; | |
230 | tiflags = ti->ti_flags; | |
231 | ||
232 | goto cont_conn; | |
233 | } | |
234 | slirp = m->slirp; | |
235 | ||
236 | ip = mtod(m, struct ip *); | |
237 | ip6 = mtod(m, struct ip6 *); | |
238 | ||
239 | switch (af) { | |
240 | case AF_INET: | |
241 | if (iphlen > sizeof(struct ip)) { | |
242 | ip_stripoptions(m, (struct mbuf *)0); | |
243 | iphlen = sizeof(struct ip); | |
244 | } | |
245 | /* XXX Check if too short */ | |
246 | ||
247 | ||
248 | /* | |
249 | * Save a copy of the IP header in case we want restore it | |
250 | * for sending an ICMP error message in response. | |
251 | */ | |
252 | save_ip = *ip; | |
253 | save_ip.ip_len += iphlen; | |
254 | ||
255 | /* | |
256 | * Get IP and TCP header together in first mbuf. | |
257 | * Note: IP leaves IP header in first mbuf. | |
258 | */ | |
259 | m->m_data -= | |
260 | sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr); | |
261 | m->m_len += | |
262 | sizeof(struct tcpiphdr) - sizeof(struct ip) - sizeof(struct tcphdr); | |
263 | ti = mtod(m, struct tcpiphdr *); | |
264 | ||
265 | /* | |
266 | * Checksum extended TCP header and data. | |
267 | */ | |
268 | tlen = ip->ip_len; | |
269 | tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; | |
270 | memset(&ti->ih_mbuf, 0, sizeof(struct mbuf_ptr)); | |
271 | memset(&ti->ti, 0, sizeof(ti->ti)); | |
272 | ti->ti_x0 = 0; | |
273 | ti->ti_src = save_ip.ip_src; | |
274 | ti->ti_dst = save_ip.ip_dst; | |
275 | ti->ti_pr = save_ip.ip_p; | |
276 | ti->ti_len = htons((uint16_t)tlen); | |
277 | break; | |
278 | ||
279 | case AF_INET6: | |
280 | /* | |
281 | * Save a copy of the IP header in case we want restore it | |
282 | * for sending an ICMP error message in response. | |
283 | */ | |
284 | save_ip6 = *ip6; | |
285 | /* | |
286 | * Get IP and TCP header together in first mbuf. | |
287 | * Note: IP leaves IP header in first mbuf. | |
288 | */ | |
289 | m->m_data -= sizeof(struct tcpiphdr) - | |
290 | (sizeof(struct ip6) + sizeof(struct tcphdr)); | |
291 | m->m_len += sizeof(struct tcpiphdr) - | |
292 | (sizeof(struct ip6) + sizeof(struct tcphdr)); | |
293 | ti = mtod(m, struct tcpiphdr *); | |
294 | ||
295 | tlen = ip6->ip_pl; | |
296 | tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; | |
297 | memset(&ti->ih_mbuf, 0, sizeof(struct mbuf_ptr)); | |
298 | memset(&ti->ti, 0, sizeof(ti->ti)); | |
299 | ti->ti_x0 = 0; | |
300 | ti->ti_src6 = save_ip6.ip_src; | |
301 | ti->ti_dst6 = save_ip6.ip_dst; | |
302 | ti->ti_nh6 = save_ip6.ip_nh; | |
303 | ti->ti_len = htons((uint16_t)tlen); | |
304 | break; | |
305 | ||
306 | default: | |
307 | g_assert_not_reached(); | |
308 | } | |
309 | ||
310 | len = ((sizeof(struct tcpiphdr) - sizeof(struct tcphdr)) + tlen); | |
311 | if (cksum(m, len)) { | |
312 | goto drop; | |
313 | } | |
314 | ||
315 | /* | |
316 | * Check that TCP offset makes sense, | |
317 | * pull out TCP options and adjust length. XXX | |
318 | */ | |
319 | off = ti->ti_off << 2; | |
320 | if (off < sizeof(struct tcphdr) || off > tlen) { | |
321 | goto drop; | |
322 | } | |
323 | tlen -= off; | |
324 | ti->ti_len = tlen; | |
325 | if (off > sizeof(struct tcphdr)) { | |
326 | optlen = off - sizeof(struct tcphdr); | |
327 | optp = mtod(m, char *) + sizeof(struct tcpiphdr); | |
328 | } | |
329 | tiflags = ti->ti_flags; | |
330 | ||
331 | /* | |
332 | * Convert TCP protocol specific fields to host format. | |
333 | */ | |
334 | NTOHL(ti->ti_seq); | |
335 | NTOHL(ti->ti_ack); | |
336 | NTOHS(ti->ti_win); | |
337 | NTOHS(ti->ti_urp); | |
338 | ||
339 | /* | |
340 | * Drop TCP, IP headers and TCP options. | |
341 | */ | |
342 | m->m_data += sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr); | |
343 | m->m_len -= sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr); | |
344 | ||
345 | /* | |
346 | * Locate pcb for segment. | |
347 | */ | |
348 | findso: | |
349 | lhost.ss_family = af; | |
350 | fhost.ss_family = af; | |
351 | switch (af) { | |
352 | case AF_INET: | |
353 | lhost4 = (struct sockaddr_in *)&lhost; | |
354 | lhost4->sin_addr = ti->ti_src; | |
355 | lhost4->sin_port = ti->ti_sport; | |
356 | fhost4 = (struct sockaddr_in *)&fhost; | |
357 | fhost4->sin_addr = ti->ti_dst; | |
358 | fhost4->sin_port = ti->ti_dport; | |
359 | break; | |
360 | case AF_INET6: | |
361 | lhost6 = (struct sockaddr_in6 *)&lhost; | |
362 | lhost6->sin6_addr = ti->ti_src6; | |
363 | lhost6->sin6_port = ti->ti_sport; | |
364 | fhost6 = (struct sockaddr_in6 *)&fhost; | |
365 | fhost6->sin6_addr = ti->ti_dst6; | |
366 | fhost6->sin6_port = ti->ti_dport; | |
367 | break; | |
368 | default: | |
369 | g_assert_not_reached(); | |
370 | } | |
371 | ||
372 | so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost); | |
373 | ||
374 | /* | |
375 | * If the state is CLOSED (i.e., TCB does not exist) then | |
376 | * all data in the incoming segment is discarded. | |
377 | * If the TCB exists but is in CLOSED state, it is embryonic, | |
378 | * but should either do a listen or a connect soon. | |
379 | * | |
380 | * state == CLOSED means we've done socreate() but haven't | |
381 | * attached it to a protocol yet... | |
382 | * | |
383 | * XXX If a TCB does not exist, and the TH_SYN flag is | |
384 | * the only flag set, then create a session, mark it | |
385 | * as if it was LISTENING, and continue... | |
386 | */ | |
387 | if (so == NULL) { | |
388 | /* TODO: IPv6 */ | |
389 | if (slirp->restricted) { | |
390 | /* Any hostfwds will have an existing socket, so we only get here | |
391 | * for non-hostfwd connections. These should be dropped, unless it | |
392 | * happens to be a guestfwd. | |
393 | */ | |
394 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; | |
395 | ex_ptr = ex_ptr->ex_next) { | |
396 | if (ex_ptr->ex_fport == ti->ti_dport && | |
397 | ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) { | |
398 | break; | |
399 | } | |
400 | } | |
401 | if (!ex_ptr) { | |
402 | goto dropwithreset; | |
403 | } | |
404 | } | |
405 | ||
406 | if ((tiflags & (TH_SYN | TH_FIN | TH_RST | TH_URG | TH_ACK)) != TH_SYN) | |
407 | goto dropwithreset; | |
408 | ||
409 | so = socreate(slirp); | |
410 | if (tcp_attach(so) < 0) { | |
411 | g_free(so); /* Not sofree (if it failed, it's not insqued) */ | |
412 | goto dropwithreset; | |
413 | } | |
414 | ||
415 | sbreserve(&so->so_snd, TCP_SNDSPACE); | |
416 | sbreserve(&so->so_rcv, TCP_RCVSPACE); | |
417 | ||
418 | so->lhost.ss = lhost; | |
419 | so->fhost.ss = fhost; | |
420 | ||
421 | so->so_iptos = tcp_tos(so); | |
422 | if (so->so_iptos == 0) { | |
423 | switch (af) { | |
424 | case AF_INET: | |
425 | so->so_iptos = ((struct ip *)ti)->ip_tos; | |
426 | break; | |
427 | case AF_INET6: | |
428 | break; | |
429 | default: | |
430 | g_assert_not_reached(); | |
431 | } | |
432 | } | |
433 | ||
434 | tp = sototcpcb(so); | |
435 | tp->t_state = TCPS_LISTEN; | |
436 | } | |
437 | ||
438 | /* | |
439 | * If this is a still-connecting socket, this probably | |
440 | * a retransmit of the SYN. Whether it's a retransmit SYN | |
441 | * or something else, we nuke it. | |
442 | */ | |
443 | if (so->so_state & SS_ISFCONNECTING) | |
444 | goto drop; | |
445 | ||
446 | tp = sototcpcb(so); | |
447 | ||
448 | /* XXX Should never fail */ | |
449 | if (tp == NULL) | |
450 | goto dropwithreset; | |
451 | if (tp->t_state == TCPS_CLOSED) | |
452 | goto drop; | |
453 | ||
454 | tiwin = ti->ti_win; | |
455 | ||
456 | /* | |
457 | * Segment received on connection. | |
458 | * Reset idle time and keep-alive timer. | |
459 | */ | |
460 | tp->t_idle = 0; | |
461 | if (slirp_do_keepalive) | |
462 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; | |
463 | else | |
464 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; | |
465 | ||
466 | /* | |
467 | * Process options if not in LISTEN state, | |
468 | * else do it below (after getting remote address). | |
469 | */ | |
470 | if (optp && tp->t_state != TCPS_LISTEN) | |
471 | tcp_dooptions(tp, (uint8_t *)optp, optlen, ti); | |
472 | ||
473 | /* | |
474 | * Header prediction: check for the two common cases | |
475 | * of a uni-directional data xfer. If the packet has | |
476 | * no control flags, is in-sequence, the window didn't | |
477 | * change and we're not retransmitting, it's a | |
478 | * candidate. If the length is zero and the ack moved | |
479 | * forward, we're the sender side of the xfer. Just | |
480 | * free the data acked & wake any higher level process | |
481 | * that was blocked waiting for space. If the length | |
482 | * is non-zero and the ack didn't move, we're the | |
483 | * receiver side. If we're getting packets in-order | |
484 | * (the reassembly queue is empty), add the data to | |
485 | * the socket buffer and note that we need a delayed ack. | |
486 | * | |
487 | * XXX Some of these tests are not needed | |
488 | * eg: the tiwin == tp->snd_wnd prevents many more | |
489 | * predictions.. with no *real* advantage.. | |
490 | */ | |
491 | if (tp->t_state == TCPS_ESTABLISHED && | |
492 | (tiflags & (TH_SYN | TH_FIN | TH_RST | TH_URG | TH_ACK)) == TH_ACK && | |
493 | ti->ti_seq == tp->rcv_nxt && tiwin && tiwin == tp->snd_wnd && | |
494 | tp->snd_nxt == tp->snd_max) { | |
495 | if (ti->ti_len == 0) { | |
496 | if (SEQ_GT(ti->ti_ack, tp->snd_una) && | |
497 | SEQ_LEQ(ti->ti_ack, tp->snd_max) && | |
498 | tp->snd_cwnd >= tp->snd_wnd) { | |
499 | /* | |
500 | * this is a pure ack for outstanding data. | |
501 | */ | |
502 | if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) | |
503 | tcp_xmit_timer(tp, tp->t_rtt); | |
504 | acked = ti->ti_ack - tp->snd_una; | |
505 | sodrop(so, acked); | |
506 | tp->snd_una = ti->ti_ack; | |
507 | m_free(m); | |
508 | ||
509 | /* | |
510 | * If all outstanding data are acked, stop | |
511 | * retransmit timer, otherwise restart timer | |
512 | * using current (possibly backed-off) value. | |
513 | * If process is waiting for space, | |
514 | * wakeup/selwakeup/signal. If data | |
515 | * are ready to send, let tcp_output | |
516 | * decide between more output or persist. | |
517 | */ | |
518 | if (tp->snd_una == tp->snd_max) | |
519 | tp->t_timer[TCPT_REXMT] = 0; | |
520 | else if (tp->t_timer[TCPT_PERSIST] == 0) | |
521 | tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; | |
522 | ||
523 | /* | |
524 | * This is called because sowwakeup might have | |
525 | * put data into so_snd. Since we don't so sowwakeup, | |
526 | * we don't need this.. XXX??? | |
527 | */ | |
528 | if (so->so_snd.sb_cc) | |
529 | (void)tcp_output(tp); | |
530 | ||
531 | return; | |
532 | } | |
533 | } else if (ti->ti_ack == tp->snd_una && tcpfrag_list_empty(tp) && | |
534 | ti->ti_len <= sbspace(&so->so_rcv)) { | |
535 | /* | |
536 | * this is a pure, in-sequence data packet | |
537 | * with nothing on the reassembly queue and | |
538 | * we have enough buffer space to take it. | |
539 | */ | |
540 | tp->rcv_nxt += ti->ti_len; | |
541 | /* | |
542 | * Add data to socket buffer. | |
543 | */ | |
544 | if (so->so_emu) { | |
545 | if (tcp_emu(so, m)) | |
546 | sbappend(so, m); | |
547 | } else | |
548 | sbappend(so, m); | |
549 | ||
550 | /* | |
551 | * If this is a short packet, then ACK now - with Nagel | |
552 | * congestion avoidance sender won't send more until | |
553 | * he gets an ACK. | |
554 | * | |
555 | * It is better to not delay acks at all to maximize | |
556 | * TCP throughput. See RFC 2581. | |
557 | */ | |
558 | tp->t_flags |= TF_ACKNOW; | |
559 | tcp_output(tp); | |
560 | return; | |
561 | } | |
562 | } /* header prediction */ | |
563 | /* | |
564 | * Calculate amount of space in receive window, | |
565 | * and then do TCP input processing. | |
566 | * Receive window is amount of space in rcv queue, | |
567 | * but not less than advertised window. | |
568 | */ | |
569 | { | |
570 | int win; | |
571 | win = sbspace(&so->so_rcv); | |
572 | if (win < 0) | |
573 | win = 0; | |
574 | tp->rcv_wnd = MAX(win, (int)(tp->rcv_adv - tp->rcv_nxt)); | |
575 | } | |
576 | ||
577 | switch (tp->t_state) { | |
578 | /* | |
579 | * If the state is LISTEN then ignore segment if it contains an RST. | |
580 | * If the segment contains an ACK then it is bad and send a RST. | |
581 | * If it does not contain a SYN then it is not interesting; drop it. | |
582 | * Don't bother responding if the destination was a broadcast. | |
583 | * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial | |
584 | * tp->iss, and send a segment: | |
585 | * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK> | |
586 | * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss. | |
587 | * Fill in remote peer address fields if not previously specified. | |
588 | * Enter SYN_RECEIVED state, and process any other fields of this | |
589 | * segment in this state. | |
590 | */ | |
591 | case TCPS_LISTEN: { | |
592 | if (tiflags & TH_RST) | |
593 | goto drop; | |
594 | if (tiflags & TH_ACK) | |
595 | goto dropwithreset; | |
596 | if ((tiflags & TH_SYN) == 0) | |
597 | goto drop; | |
598 | ||
599 | /* | |
600 | * This has way too many gotos... | |
601 | * But a bit of spaghetti code never hurt anybody :) | |
602 | */ | |
603 | ||
604 | /* | |
605 | * If this is destined for the control address, then flag to | |
606 | * tcp_ctl once connected, otherwise connect | |
607 | */ | |
608 | /* TODO: IPv6 */ | |
609 | if (af == AF_INET && | |
610 | (so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == | |
611 | slirp->vnetwork_addr.s_addr) { | |
612 | if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr && | |
613 | so->so_faddr.s_addr != slirp->vnameserver_addr.s_addr) { | |
614 | /* May be an add exec */ | |
615 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; | |
616 | ex_ptr = ex_ptr->ex_next) { | |
617 | if (ex_ptr->ex_fport == so->so_fport && | |
618 | so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { | |
619 | so->so_state |= SS_CTL; | |
620 | break; | |
621 | } | |
622 | } | |
623 | if (so->so_state & SS_CTL) { | |
624 | goto cont_input; | |
625 | } | |
626 | } | |
627 | /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */ | |
628 | } | |
629 | ||
630 | if (so->so_emu & EMU_NOCONNECT) { | |
631 | so->so_emu &= ~EMU_NOCONNECT; | |
632 | goto cont_input; | |
633 | } | |
634 | ||
635 | if ((tcp_fconnect(so, so->so_ffamily) == -1) && (errno != EAGAIN) && | |
636 | (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) { | |
637 | uint8_t code; | |
638 | DEBUG_MISC(" tcp fconnect errno = %d-%s", errno, strerror(errno)); | |
639 | if (errno == ECONNREFUSED) { | |
640 | /* ACK the SYN, send RST to refuse the connection */ | |
641 | tcp_respond(tp, ti, m, ti->ti_seq + 1, (tcp_seq)0, | |
642 | TH_RST | TH_ACK, af); | |
643 | } else { | |
644 | switch (af) { | |
645 | case AF_INET: | |
646 | code = ICMP_UNREACH_NET; | |
647 | if (errno == EHOSTUNREACH) { | |
648 | code = ICMP_UNREACH_HOST; | |
649 | } | |
650 | break; | |
651 | case AF_INET6: | |
652 | code = ICMP6_UNREACH_NO_ROUTE; | |
653 | if (errno == EHOSTUNREACH) { | |
654 | code = ICMP6_UNREACH_ADDRESS; | |
655 | } | |
656 | break; | |
657 | default: | |
658 | g_assert_not_reached(); | |
659 | } | |
660 | HTONL(ti->ti_seq); /* restore tcp header */ | |
661 | HTONL(ti->ti_ack); | |
662 | HTONS(ti->ti_win); | |
663 | HTONS(ti->ti_urp); | |
664 | m->m_data -= | |
665 | sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr); | |
666 | m->m_len += | |
667 | sizeof(struct tcpiphdr) + off - sizeof(struct tcphdr); | |
668 | switch (af) { | |
669 | case AF_INET: | |
670 | m->m_data += sizeof(struct tcpiphdr) - sizeof(struct ip) - | |
671 | sizeof(struct tcphdr); | |
672 | m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct ip) - | |
673 | sizeof(struct tcphdr); | |
674 | *ip = save_ip; | |
675 | icmp_send_error(m, ICMP_UNREACH, code, 0, strerror(errno)); | |
676 | break; | |
677 | case AF_INET6: | |
678 | m->m_data += sizeof(struct tcpiphdr) - | |
679 | (sizeof(struct ip6) + sizeof(struct tcphdr)); | |
680 | m->m_len -= sizeof(struct tcpiphdr) - | |
681 | (sizeof(struct ip6) + sizeof(struct tcphdr)); | |
682 | *ip6 = save_ip6; | |
683 | icmp6_send_error(m, ICMP6_UNREACH, code); | |
684 | break; | |
685 | default: | |
686 | g_assert_not_reached(); | |
687 | } | |
688 | } | |
689 | tcp_close(tp); | |
690 | m_free(m); | |
691 | } else { | |
692 | /* | |
693 | * Haven't connected yet, save the current mbuf | |
694 | * and ti, and return | |
695 | * XXX Some OS's don't tell us whether the connect() | |
696 | * succeeded or not. So we must time it out. | |
697 | */ | |
698 | so->so_m = m; | |
699 | so->so_ti = ti; | |
700 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; | |
701 | tp->t_state = TCPS_SYN_RECEIVED; | |
702 | /* | |
703 | * Initialize receive sequence numbers now so that we can send a | |
704 | * valid RST if the remote end rejects our connection. | |
705 | */ | |
706 | tp->irs = ti->ti_seq; | |
707 | tcp_rcvseqinit(tp); | |
708 | tcp_template(tp); | |
709 | } | |
710 | return; | |
711 | ||
712 | cont_conn: | |
713 | /* m==NULL | |
714 | * Check if the connect succeeded | |
715 | */ | |
716 | if (so->so_state & SS_NOFDREF) { | |
717 | tp = tcp_close(tp); | |
718 | goto dropwithreset; | |
719 | } | |
720 | cont_input: | |
721 | tcp_template(tp); | |
722 | ||
723 | if (optp) | |
724 | tcp_dooptions(tp, (uint8_t *)optp, optlen, ti); | |
725 | ||
726 | if (iss) | |
727 | tp->iss = iss; | |
728 | else | |
729 | tp->iss = slirp->tcp_iss; | |
730 | slirp->tcp_iss += TCP_ISSINCR / 2; | |
731 | tp->irs = ti->ti_seq; | |
732 | tcp_sendseqinit(tp); | |
733 | tcp_rcvseqinit(tp); | |
734 | tp->t_flags |= TF_ACKNOW; | |
735 | tp->t_state = TCPS_SYN_RECEIVED; | |
736 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; | |
737 | goto trimthenstep6; | |
738 | } /* case TCPS_LISTEN */ | |
739 | ||
740 | /* | |
741 | * If the state is SYN_SENT: | |
742 | * if seg contains an ACK, but not for our SYN, drop the input. | |
743 | * if seg contains a RST, then drop the connection. | |
744 | * if seg does not contain SYN, then drop it. | |
745 | * Otherwise this is an acceptable SYN segment | |
746 | * initialize tp->rcv_nxt and tp->irs | |
747 | * if seg contains ack then advance tp->snd_una | |
748 | * if SYN has been acked change to ESTABLISHED else SYN_RCVD state | |
749 | * arrange for segment to be acked (eventually) | |
750 | * continue processing rest of data/controls, beginning with URG | |
751 | */ | |
752 | case TCPS_SYN_SENT: | |
753 | if ((tiflags & TH_ACK) && | |
754 | (SEQ_LEQ(ti->ti_ack, tp->iss) || SEQ_GT(ti->ti_ack, tp->snd_max))) | |
755 | goto dropwithreset; | |
756 | ||
757 | if (tiflags & TH_RST) { | |
758 | if (tiflags & TH_ACK) { | |
759 | tcp_drop(tp, 0); /* XXX Check t_softerror! */ | |
760 | } | |
761 | goto drop; | |
762 | } | |
763 | ||
764 | if ((tiflags & TH_SYN) == 0) | |
765 | goto drop; | |
766 | if (tiflags & TH_ACK) { | |
767 | tp->snd_una = ti->ti_ack; | |
768 | if (SEQ_LT(tp->snd_nxt, tp->snd_una)) | |
769 | tp->snd_nxt = tp->snd_una; | |
770 | } | |
771 | ||
772 | tp->t_timer[TCPT_REXMT] = 0; | |
773 | tp->irs = ti->ti_seq; | |
774 | tcp_rcvseqinit(tp); | |
775 | tp->t_flags |= TF_ACKNOW; | |
776 | if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) { | |
777 | soisfconnected(so); | |
778 | tp->t_state = TCPS_ESTABLISHED; | |
779 | ||
780 | (void)tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); | |
781 | /* | |
782 | * if we didn't have to retransmit the SYN, | |
783 | * use its rtt as our initial srtt & rtt var. | |
784 | */ | |
785 | if (tp->t_rtt) | |
786 | tcp_xmit_timer(tp, tp->t_rtt); | |
787 | } else | |
788 | tp->t_state = TCPS_SYN_RECEIVED; | |
789 | ||
790 | trimthenstep6: | |
791 | /* | |
792 | * Advance ti->ti_seq to correspond to first data byte. | |
793 | * If data, trim to stay within window, | |
794 | * dropping FIN if necessary. | |
795 | */ | |
796 | ti->ti_seq++; | |
797 | if (ti->ti_len > tp->rcv_wnd) { | |
798 | todrop = ti->ti_len - tp->rcv_wnd; | |
799 | m_adj(m, -todrop); | |
800 | ti->ti_len = tp->rcv_wnd; | |
801 | tiflags &= ~TH_FIN; | |
802 | } | |
803 | tp->snd_wl1 = ti->ti_seq - 1; | |
804 | tp->rcv_up = ti->ti_seq; | |
805 | goto step6; | |
806 | } /* switch tp->t_state */ | |
807 | /* | |
808 | * States other than LISTEN or SYN_SENT. | |
809 | * Check that at least some bytes of segment are within | |
810 | * receive window. If segment begins before rcv_nxt, | |
811 | * drop leading data (and SYN); if nothing left, just ack. | |
812 | */ | |
813 | todrop = tp->rcv_nxt - ti->ti_seq; | |
814 | if (todrop > 0) { | |
815 | if (tiflags & TH_SYN) { | |
816 | tiflags &= ~TH_SYN; | |
817 | ti->ti_seq++; | |
818 | if (ti->ti_urp > 1) | |
819 | ti->ti_urp--; | |
820 | else | |
821 | tiflags &= ~TH_URG; | |
822 | todrop--; | |
823 | } | |
824 | /* | |
825 | * Following if statement from Stevens, vol. 2, p. 960. | |
826 | */ | |
827 | if (todrop > ti->ti_len || | |
828 | (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) { | |
829 | /* | |
830 | * Any valid FIN must be to the left of the window. | |
831 | * At this point the FIN must be a duplicate or out | |
832 | * of sequence; drop it. | |
833 | */ | |
834 | tiflags &= ~TH_FIN; | |
835 | ||
836 | /* | |
837 | * Send an ACK to resynchronize and drop any data. | |
838 | * But keep on processing for RST or ACK. | |
839 | */ | |
840 | tp->t_flags |= TF_ACKNOW; | |
841 | todrop = ti->ti_len; | |
842 | } | |
843 | m_adj(m, todrop); | |
844 | ti->ti_seq += todrop; | |
845 | ti->ti_len -= todrop; | |
846 | if (ti->ti_urp > todrop) | |
847 | ti->ti_urp -= todrop; | |
848 | else { | |
849 | tiflags &= ~TH_URG; | |
850 | ti->ti_urp = 0; | |
851 | } | |
852 | } | |
853 | /* | |
854 | * If new data are received on a connection after the | |
855 | * user processes are gone, then RST the other end. | |
856 | */ | |
857 | if ((so->so_state & SS_NOFDREF) && tp->t_state > TCPS_CLOSE_WAIT && | |
858 | ti->ti_len) { | |
859 | tp = tcp_close(tp); | |
860 | goto dropwithreset; | |
861 | } | |
862 | ||
863 | /* | |
864 | * If segment ends after window, drop trailing data | |
865 | * (and PUSH and FIN); if nothing left, just ACK. | |
866 | */ | |
867 | todrop = (ti->ti_seq + ti->ti_len) - (tp->rcv_nxt + tp->rcv_wnd); | |
868 | if (todrop > 0) { | |
869 | if (todrop >= ti->ti_len) { | |
870 | /* | |
871 | * If a new connection request is received | |
872 | * while in TIME_WAIT, drop the old connection | |
873 | * and start over if the sequence numbers | |
874 | * are above the previous ones. | |
875 | */ | |
876 | if (tiflags & TH_SYN && tp->t_state == TCPS_TIME_WAIT && | |
877 | SEQ_GT(ti->ti_seq, tp->rcv_nxt)) { | |
878 | iss = tp->rcv_nxt + TCP_ISSINCR; | |
879 | tp = tcp_close(tp); | |
880 | goto findso; | |
881 | } | |
882 | /* | |
883 | * If window is closed can only take segments at | |
884 | * window edge, and have to drop data and PUSH from | |
885 | * incoming segments. Continue processing, but | |
886 | * remember to ack. Otherwise, drop segment | |
887 | * and ack. | |
888 | */ | |
889 | if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) { | |
890 | tp->t_flags |= TF_ACKNOW; | |
891 | } else { | |
892 | goto dropafterack; | |
893 | } | |
894 | } | |
895 | m_adj(m, -todrop); | |
896 | ti->ti_len -= todrop; | |
897 | tiflags &= ~(TH_PUSH | TH_FIN); | |
898 | } | |
899 | ||
900 | /* | |
901 | * If the RST bit is set examine the state: | |
902 | * SYN_RECEIVED STATE: | |
903 | * If passive open, return to LISTEN state. | |
904 | * If active open, inform user that connection was refused. | |
905 | * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES: | |
906 | * Inform user that connection was reset, and close tcb. | |
907 | * CLOSING, LAST_ACK, TIME_WAIT STATES | |
908 | * Close the tcb. | |
909 | */ | |
910 | if (tiflags & TH_RST) | |
911 | switch (tp->t_state) { | |
912 | case TCPS_SYN_RECEIVED: | |
913 | case TCPS_ESTABLISHED: | |
914 | case TCPS_FIN_WAIT_1: | |
915 | case TCPS_FIN_WAIT_2: | |
916 | case TCPS_CLOSE_WAIT: | |
917 | tp->t_state = TCPS_CLOSED; | |
918 | tcp_close(tp); | |
919 | goto drop; | |
920 | ||
921 | case TCPS_CLOSING: | |
922 | case TCPS_LAST_ACK: | |
923 | case TCPS_TIME_WAIT: | |
924 | tcp_close(tp); | |
925 | goto drop; | |
926 | } | |
927 | ||
928 | /* | |
929 | * If a SYN is in the window, then this is an | |
930 | * error and we send an RST and drop the connection. | |
931 | */ | |
932 | if (tiflags & TH_SYN) { | |
933 | tp = tcp_drop(tp, 0); | |
934 | goto dropwithreset; | |
935 | } | |
936 | ||
937 | /* | |
938 | * If the ACK bit is off we drop the segment and return. | |
939 | */ | |
940 | if ((tiflags & TH_ACK) == 0) | |
941 | goto drop; | |
942 | ||
943 | /* | |
944 | * Ack processing. | |
945 | */ | |
946 | switch (tp->t_state) { | |
947 | /* | |
948 | * In SYN_RECEIVED state if the ack ACKs our SYN then enter | |
949 | * ESTABLISHED state and continue processing, otherwise | |
950 | * send an RST. una<=ack<=max | |
951 | */ | |
952 | case TCPS_SYN_RECEIVED: | |
953 | ||
954 | if (SEQ_GT(tp->snd_una, ti->ti_ack) || SEQ_GT(ti->ti_ack, tp->snd_max)) | |
955 | goto dropwithreset; | |
956 | tp->t_state = TCPS_ESTABLISHED; | |
957 | /* | |
958 | * The sent SYN is ack'ed with our sequence number +1 | |
959 | * The first data byte already in the buffer will get | |
960 | * lost if no correction is made. This is only needed for | |
961 | * SS_CTL since the buffer is empty otherwise. | |
962 | * tp->snd_una++; or: | |
963 | */ | |
964 | tp->snd_una = ti->ti_ack; | |
965 | if (so->so_state & SS_CTL) { | |
966 | /* So tcp_ctl reports the right state */ | |
967 | ret = tcp_ctl(so); | |
968 | if (ret == 1) { | |
969 | soisfconnected(so); | |
970 | so->so_state &= ~SS_CTL; /* success XXX */ | |
971 | } else if (ret == 2) { | |
972 | so->so_state &= SS_PERSISTENT_MASK; | |
973 | so->so_state |= SS_NOFDREF; /* CTL_CMD */ | |
974 | } else { | |
975 | needoutput = 1; | |
976 | tp->t_state = TCPS_FIN_WAIT_1; | |
977 | } | |
978 | } else { | |
979 | soisfconnected(so); | |
980 | } | |
981 | ||
982 | (void)tcp_reass(tp, (struct tcpiphdr *)0, (struct mbuf *)0); | |
983 | tp->snd_wl1 = ti->ti_seq - 1; | |
984 | /* Avoid ack processing; snd_una==ti_ack => dup ack */ | |
985 | goto synrx_to_est; | |
986 | /* fall into ... */ | |
987 | ||
988 | /* | |
989 | * In ESTABLISHED state: drop duplicate ACKs; ACK out of range | |
990 | * ACKs. If the ack is in the range | |
991 | * tp->snd_una < ti->ti_ack <= tp->snd_max | |
992 | * then advance tp->snd_una to ti->ti_ack and drop | |
993 | * data from the retransmission queue. If this ACK reflects | |
994 | * more up to date window information we update our window information. | |
995 | */ | |
996 | case TCPS_ESTABLISHED: | |
997 | case TCPS_FIN_WAIT_1: | |
998 | case TCPS_FIN_WAIT_2: | |
999 | case TCPS_CLOSE_WAIT: | |
1000 | case TCPS_CLOSING: | |
1001 | case TCPS_LAST_ACK: | |
1002 | case TCPS_TIME_WAIT: | |
1003 | ||
1004 | if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) { | |
1005 | if (ti->ti_len == 0 && tiwin == tp->snd_wnd) { | |
1006 | DEBUG_MISC(" dup ack m = %p so = %p", m, so); | |
1007 | /* | |
1008 | * If we have outstanding data (other than | |
1009 | * a window probe), this is a completely | |
1010 | * duplicate ack (ie, window info didn't | |
1011 | * change), the ack is the biggest we've | |
1012 | * seen and we've seen exactly our rexmt | |
1013 | * threshold of them, assume a packet | |
1014 | * has been dropped and retransmit it. | |
1015 | * Kludge snd_nxt & the congestion | |
1016 | * window so we send only this one | |
1017 | * packet. | |
1018 | * | |
1019 | * We know we're losing at the current | |
1020 | * window size so do congestion avoidance | |
1021 | * (set ssthresh to half the current window | |
1022 | * and pull our congestion window back to | |
1023 | * the new ssthresh). | |
1024 | * | |
1025 | * Dup acks mean that packets have left the | |
1026 | * network (they're now cached at the receiver) | |
1027 | * so bump cwnd by the amount in the receiver | |
1028 | * to keep a constant cwnd packets in the | |
1029 | * network. | |
1030 | */ | |
1031 | if (tp->t_timer[TCPT_REXMT] == 0 || ti->ti_ack != tp->snd_una) | |
1032 | tp->t_dupacks = 0; | |
1033 | else if (++tp->t_dupacks == TCPREXMTTHRESH) { | |
1034 | tcp_seq onxt = tp->snd_nxt; | |
1035 | unsigned win = | |
1036 | MIN(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; | |
1037 | ||
1038 | if (win < 2) | |
1039 | win = 2; | |
1040 | tp->snd_ssthresh = win * tp->t_maxseg; | |
1041 | tp->t_timer[TCPT_REXMT] = 0; | |
1042 | tp->t_rtt = 0; | |
1043 | tp->snd_nxt = ti->ti_ack; | |
1044 | tp->snd_cwnd = tp->t_maxseg; | |
1045 | (void)tcp_output(tp); | |
1046 | tp->snd_cwnd = | |
1047 | tp->snd_ssthresh + tp->t_maxseg * tp->t_dupacks; | |
1048 | if (SEQ_GT(onxt, tp->snd_nxt)) | |
1049 | tp->snd_nxt = onxt; | |
1050 | goto drop; | |
1051 | } else if (tp->t_dupacks > TCPREXMTTHRESH) { | |
1052 | tp->snd_cwnd += tp->t_maxseg; | |
1053 | (void)tcp_output(tp); | |
1054 | goto drop; | |
1055 | } | |
1056 | } else | |
1057 | tp->t_dupacks = 0; | |
1058 | break; | |
1059 | } | |
1060 | synrx_to_est: | |
1061 | /* | |
1062 | * If the congestion window was inflated to account | |
1063 | * for the other side's cached packets, retract it. | |
1064 | */ | |
1065 | if (tp->t_dupacks > TCPREXMTTHRESH && tp->snd_cwnd > tp->snd_ssthresh) | |
1066 | tp->snd_cwnd = tp->snd_ssthresh; | |
1067 | tp->t_dupacks = 0; | |
1068 | if (SEQ_GT(ti->ti_ack, tp->snd_max)) { | |
1069 | goto dropafterack; | |
1070 | } | |
1071 | acked = ti->ti_ack - tp->snd_una; | |
1072 | ||
1073 | /* | |
1074 | * If transmit timer is running and timed sequence | |
1075 | * number was acked, update smoothed round trip time. | |
1076 | * Since we now have an rtt measurement, cancel the | |
1077 | * timer backoff (cf., Phil Karn's retransmit alg.). | |
1078 | * Recompute the initial retransmit timer. | |
1079 | */ | |
1080 | if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq)) | |
1081 | tcp_xmit_timer(tp, tp->t_rtt); | |
1082 | ||
1083 | /* | |
1084 | * If all outstanding data is acked, stop retransmit | |
1085 | * timer and remember to restart (more output or persist). | |
1086 | * If there is more data to be acked, restart retransmit | |
1087 | * timer, using current (possibly backed-off) value. | |
1088 | */ | |
1089 | if (ti->ti_ack == tp->snd_max) { | |
1090 | tp->t_timer[TCPT_REXMT] = 0; | |
1091 | needoutput = 1; | |
1092 | } else if (tp->t_timer[TCPT_PERSIST] == 0) | |
1093 | tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; | |
1094 | /* | |
1095 | * When new data is acked, open the congestion window. | |
1096 | * If the window gives us less than ssthresh packets | |
1097 | * in flight, open exponentially (maxseg per packet). | |
1098 | * Otherwise open linearly: maxseg per window | |
1099 | * (maxseg^2 / cwnd per packet). | |
1100 | */ | |
1101 | { | |
1102 | register unsigned cw = tp->snd_cwnd; | |
1103 | register unsigned incr = tp->t_maxseg; | |
1104 | ||
1105 | if (cw > tp->snd_ssthresh) | |
1106 | incr = incr * incr / cw; | |
1107 | tp->snd_cwnd = MIN(cw + incr, TCP_MAXWIN << tp->snd_scale); | |
1108 | } | |
1109 | if (acked > so->so_snd.sb_cc) { | |
1110 | tp->snd_wnd -= so->so_snd.sb_cc; | |
1111 | sodrop(so, (int)so->so_snd.sb_cc); | |
1112 | ourfinisacked = 1; | |
1113 | } else { | |
1114 | sodrop(so, acked); | |
1115 | tp->snd_wnd -= acked; | |
1116 | ourfinisacked = 0; | |
1117 | } | |
1118 | tp->snd_una = ti->ti_ack; | |
1119 | if (SEQ_LT(tp->snd_nxt, tp->snd_una)) | |
1120 | tp->snd_nxt = tp->snd_una; | |
1121 | ||
1122 | switch (tp->t_state) { | |
1123 | /* | |
1124 | * In FIN_WAIT_1 STATE in addition to the processing | |
1125 | * for the ESTABLISHED state if our FIN is now acknowledged | |
1126 | * then enter FIN_WAIT_2. | |
1127 | */ | |
1128 | case TCPS_FIN_WAIT_1: | |
1129 | if (ourfinisacked) { | |
1130 | /* | |
1131 | * If we can't receive any more | |
1132 | * data, then closing user can proceed. | |
1133 | * Starting the timer is contrary to the | |
1134 | * specification, but if we don't get a FIN | |
1135 | * we'll hang forever. | |
1136 | */ | |
1137 | if (so->so_state & SS_FCANTRCVMORE) { | |
1138 | tp->t_timer[TCPT_2MSL] = TCP_MAXIDLE; | |
1139 | } | |
1140 | tp->t_state = TCPS_FIN_WAIT_2; | |
1141 | } | |
1142 | break; | |
1143 | ||
1144 | /* | |
1145 | * In CLOSING STATE in addition to the processing for | |
1146 | * the ESTABLISHED state if the ACK acknowledges our FIN | |
1147 | * then enter the TIME-WAIT state, otherwise ignore | |
1148 | * the segment. | |
1149 | */ | |
1150 | case TCPS_CLOSING: | |
1151 | if (ourfinisacked) { | |
1152 | tp->t_state = TCPS_TIME_WAIT; | |
1153 | tcp_canceltimers(tp); | |
1154 | tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; | |
1155 | } | |
1156 | break; | |
1157 | ||
1158 | /* | |
1159 | * In LAST_ACK, we may still be waiting for data to drain | |
1160 | * and/or to be acked, as well as for the ack of our FIN. | |
1161 | * If our FIN is now acknowledged, delete the TCB, | |
1162 | * enter the closed state and return. | |
1163 | */ | |
1164 | case TCPS_LAST_ACK: | |
1165 | if (ourfinisacked) { | |
1166 | tcp_close(tp); | |
1167 | goto drop; | |
1168 | } | |
1169 | break; | |
1170 | ||
1171 | /* | |
1172 | * In TIME_WAIT state the only thing that should arrive | |
1173 | * is a retransmission of the remote FIN. Acknowledge | |
1174 | * it and restart the finack timer. | |
1175 | */ | |
1176 | case TCPS_TIME_WAIT: | |
1177 | tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; | |
1178 | goto dropafterack; | |
1179 | } | |
1180 | } /* switch(tp->t_state) */ | |
1181 | ||
1182 | step6: | |
1183 | /* | |
1184 | * Update window information. | |
1185 | * Don't look at window if no ACK: TAC's send garbage on first SYN. | |
1186 | */ | |
1187 | if ((tiflags & TH_ACK) && | |
1188 | (SEQ_LT(tp->snd_wl1, ti->ti_seq) || | |
1189 | (tp->snd_wl1 == ti->ti_seq && | |
1190 | (SEQ_LT(tp->snd_wl2, ti->ti_ack) || | |
1191 | (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) { | |
1192 | tp->snd_wnd = tiwin; | |
1193 | tp->snd_wl1 = ti->ti_seq; | |
1194 | tp->snd_wl2 = ti->ti_ack; | |
1195 | if (tp->snd_wnd > tp->max_sndwnd) | |
1196 | tp->max_sndwnd = tp->snd_wnd; | |
1197 | needoutput = 1; | |
1198 | } | |
1199 | ||
1200 | /* | |
1201 | * Process segments with URG. | |
1202 | */ | |
1203 | if ((tiflags & TH_URG) && ti->ti_urp && | |
1204 | TCPS_HAVERCVDFIN(tp->t_state) == 0) { | |
1205 | /* | |
1206 | * This is a kludge, but if we receive and accept | |
1207 | * random urgent pointers, we'll crash in | |
1208 | * soreceive. It's hard to imagine someone | |
1209 | * actually wanting to send this much urgent data. | |
1210 | */ | |
1211 | if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) { | |
1212 | ti->ti_urp = 0; | |
1213 | tiflags &= ~TH_URG; | |
1214 | goto dodata; | |
1215 | } | |
1216 | /* | |
1217 | * If this segment advances the known urgent pointer, | |
1218 | * then mark the data stream. This should not happen | |
1219 | * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since | |
1220 | * a FIN has been received from the remote side. | |
1221 | * In these states we ignore the URG. | |
1222 | * | |
1223 | * According to RFC961 (Assigned Protocols), | |
1224 | * the urgent pointer points to the last octet | |
1225 | * of urgent data. We continue, however, | |
1226 | * to consider it to indicate the first octet | |
1227 | * of data past the urgent section as the original | |
1228 | * spec states (in one of two places). | |
1229 | */ | |
1230 | if (SEQ_GT(ti->ti_seq + ti->ti_urp, tp->rcv_up)) { | |
1231 | tp->rcv_up = ti->ti_seq + ti->ti_urp; | |
1232 | so->so_urgc = | |
1233 | so->so_rcv.sb_cc + (tp->rcv_up - tp->rcv_nxt); /* -1; */ | |
1234 | tp->rcv_up = ti->ti_seq + ti->ti_urp; | |
1235 | } | |
1236 | } else | |
1237 | /* | |
1238 | * If no out of band data is expected, | |
1239 | * pull receive urgent pointer along | |
1240 | * with the receive window. | |
1241 | */ | |
1242 | if (SEQ_GT(tp->rcv_nxt, tp->rcv_up)) | |
1243 | tp->rcv_up = tp->rcv_nxt; | |
1244 | dodata: | |
1245 | ||
1246 | /* | |
1247 | * If this is a small packet, then ACK now - with Nagel | |
1248 | * congestion avoidance sender won't send more until | |
1249 | * he gets an ACK. | |
1250 | */ | |
1251 | if (ti->ti_len && (unsigned)ti->ti_len <= 5 && | |
1252 | ((struct tcpiphdr_2 *)ti)->first_char == (char)27) { | |
1253 | tp->t_flags |= TF_ACKNOW; | |
1254 | } | |
1255 | ||
1256 | /* | |
1257 | * Process the segment text, merging it into the TCP sequencing queue, | |
1258 | * and arranging for acknowledgment of receipt if necessary. | |
1259 | * This process logically involves adjusting tp->rcv_wnd as data | |
1260 | * is presented to the user (this happens in tcp_usrreq.c, | |
1261 | * case PRU_RCVD). If a FIN has already been received on this | |
1262 | * connection then we just ignore the text. | |
1263 | */ | |
1264 | if ((ti->ti_len || (tiflags & TH_FIN)) && | |
1265 | TCPS_HAVERCVDFIN(tp->t_state) == 0) { | |
1266 | TCP_REASS(tp, ti, m, so, tiflags); | |
1267 | } else { | |
1268 | m_free(m); | |
1269 | tiflags &= ~TH_FIN; | |
1270 | } | |
1271 | ||
1272 | /* | |
1273 | * If FIN is received ACK the FIN and let the user know | |
1274 | * that the connection is closing. | |
1275 | */ | |
1276 | if (tiflags & TH_FIN) { | |
1277 | if (TCPS_HAVERCVDFIN(tp->t_state) == 0) { | |
1278 | /* | |
1279 | * If we receive a FIN we can't send more data, | |
1280 | * set it SS_FDRAIN | |
1281 | * Shutdown the socket if there is no rx data in the | |
1282 | * buffer. | |
1283 | * soread() is called on completion of shutdown() and | |
1284 | * will got to TCPS_LAST_ACK, and use tcp_output() | |
1285 | * to send the FIN. | |
1286 | */ | |
1287 | sofwdrain(so); | |
1288 | ||
1289 | tp->t_flags |= TF_ACKNOW; | |
1290 | tp->rcv_nxt++; | |
1291 | } | |
1292 | switch (tp->t_state) { | |
1293 | /* | |
1294 | * In SYN_RECEIVED and ESTABLISHED STATES | |
1295 | * enter the CLOSE_WAIT state. | |
1296 | */ | |
1297 | case TCPS_SYN_RECEIVED: | |
1298 | case TCPS_ESTABLISHED: | |
1299 | if (so->so_emu == EMU_CTL) /* no shutdown on socket */ | |
1300 | tp->t_state = TCPS_LAST_ACK; | |
1301 | else | |
1302 | tp->t_state = TCPS_CLOSE_WAIT; | |
1303 | break; | |
1304 | ||
1305 | /* | |
1306 | * If still in FIN_WAIT_1 STATE FIN has not been acked so | |
1307 | * enter the CLOSING state. | |
1308 | */ | |
1309 | case TCPS_FIN_WAIT_1: | |
1310 | tp->t_state = TCPS_CLOSING; | |
1311 | break; | |
1312 | ||
1313 | /* | |
1314 | * In FIN_WAIT_2 state enter the TIME_WAIT state, | |
1315 | * starting the time-wait timer, turning off the other | |
1316 | * standard timers. | |
1317 | */ | |
1318 | case TCPS_FIN_WAIT_2: | |
1319 | tp->t_state = TCPS_TIME_WAIT; | |
1320 | tcp_canceltimers(tp); | |
1321 | tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; | |
1322 | break; | |
1323 | ||
1324 | /* | |
1325 | * In TIME_WAIT state restart the 2 MSL time_wait timer. | |
1326 | */ | |
1327 | case TCPS_TIME_WAIT: | |
1328 | tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL; | |
1329 | break; | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | /* | |
1334 | * Return any desired output. | |
1335 | */ | |
1336 | if (needoutput || (tp->t_flags & TF_ACKNOW)) { | |
1337 | (void)tcp_output(tp); | |
1338 | } | |
1339 | return; | |
1340 | ||
1341 | dropafterack: | |
1342 | /* | |
1343 | * Generate an ACK dropping incoming segment if it occupies | |
1344 | * sequence space, where the ACK reflects our state. | |
1345 | */ | |
1346 | if (tiflags & TH_RST) | |
1347 | goto drop; | |
1348 | m_free(m); | |
1349 | tp->t_flags |= TF_ACKNOW; | |
1350 | (void)tcp_output(tp); | |
1351 | return; | |
1352 | ||
1353 | dropwithreset: | |
1354 | /* reuses m if m!=NULL, m_free() unnecessary */ | |
1355 | if (tiflags & TH_ACK) | |
1356 | tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST, af); | |
1357 | else { | |
1358 | if (tiflags & TH_SYN) | |
1359 | ti->ti_len++; | |
1360 | tcp_respond(tp, ti, m, ti->ti_seq + ti->ti_len, (tcp_seq)0, | |
1361 | TH_RST | TH_ACK, af); | |
1362 | } | |
1363 | ||
1364 | return; | |
1365 | ||
1366 | drop: | |
1367 | /* | |
1368 | * Drop space held by incoming segment and return. | |
1369 | */ | |
1370 | m_free(m); | |
1371 | } | |
1372 | ||
1373 | static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, | |
1374 | struct tcpiphdr *ti) | |
1375 | { | |
1376 | uint16_t mss; | |
1377 | int opt, optlen; | |
1378 | ||
1379 | DEBUG_CALL("tcp_dooptions"); | |
1380 | DEBUG_ARG("tp = %p cnt=%i", tp, cnt); | |
1381 | ||
1382 | for (; cnt > 0; cnt -= optlen, cp += optlen) { | |
1383 | opt = cp[0]; | |
1384 | if (opt == TCPOPT_EOL) | |
1385 | break; | |
1386 | if (opt == TCPOPT_NOP) | |
1387 | optlen = 1; | |
1388 | else { | |
1389 | optlen = cp[1]; | |
1390 | if (optlen <= 0) | |
1391 | break; | |
1392 | } | |
1393 | switch (opt) { | |
1394 | default: | |
1395 | continue; | |
1396 | ||
1397 | case TCPOPT_MAXSEG: | |
1398 | if (optlen != TCPOLEN_MAXSEG) | |
1399 | continue; | |
1400 | if (!(ti->ti_flags & TH_SYN)) | |
1401 | continue; | |
1402 | memcpy((char *)&mss, (char *)cp + 2, sizeof(mss)); | |
1403 | NTOHS(mss); | |
1404 | (void)tcp_mss(tp, mss); /* sets t_maxseg */ | |
1405 | break; | |
1406 | } | |
1407 | } | |
1408 | } | |
1409 | ||
1410 | /* | |
1411 | * Collect new round-trip time estimate | |
1412 | * and update averages and current timeout. | |
1413 | */ | |
1414 | ||
1415 | static void tcp_xmit_timer(register struct tcpcb *tp, int rtt) | |
1416 | { | |
1417 | register short delta; | |
1418 | ||
1419 | DEBUG_CALL("tcp_xmit_timer"); | |
1420 | DEBUG_ARG("tp = %p", tp); | |
1421 | DEBUG_ARG("rtt = %d", rtt); | |
1422 | ||
1423 | if (tp->t_srtt != 0) { | |
1424 | /* | |
1425 | * srtt is stored as fixed point with 3 bits after the | |
1426 | * binary point (i.e., scaled by 8). The following magic | |
1427 | * is equivalent to the smoothing algorithm in rfc793 with | |
1428 | * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed | |
1429 | * point). Adjust rtt to origin 0. | |
1430 | */ | |
1431 | delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT); | |
1432 | if ((tp->t_srtt += delta) <= 0) | |
1433 | tp->t_srtt = 1; | |
1434 | /* | |
1435 | * We accumulate a smoothed rtt variance (actually, a | |
1436 | * smoothed mean difference), then set the retransmit | |
1437 | * timer to smoothed rtt + 4 times the smoothed variance. | |
1438 | * rttvar is stored as fixed point with 2 bits after the | |
1439 | * binary point (scaled by 4). The following is | |
1440 | * equivalent to rfc793 smoothing with an alpha of .75 | |
1441 | * (rttvar = rttvar*3/4 + |delta| / 4). This replaces | |
1442 | * rfc793's wired-in beta. | |
1443 | */ | |
1444 | if (delta < 0) | |
1445 | delta = -delta; | |
1446 | delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT); | |
1447 | if ((tp->t_rttvar += delta) <= 0) | |
1448 | tp->t_rttvar = 1; | |
1449 | } else { | |
1450 | /* | |
1451 | * No rtt measurement yet - use the unsmoothed rtt. | |
1452 | * Set the variance to half the rtt (so our first | |
1453 | * retransmit happens at 3*rtt). | |
1454 | */ | |
1455 | tp->t_srtt = rtt << TCP_RTT_SHIFT; | |
1456 | tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1); | |
1457 | } | |
1458 | tp->t_rtt = 0; | |
1459 | tp->t_rxtshift = 0; | |
1460 | ||
1461 | /* | |
1462 | * the retransmit should happen at rtt + 4 * rttvar. | |
1463 | * Because of the way we do the smoothing, srtt and rttvar | |
1464 | * will each average +1/2 tick of bias. When we compute | |
1465 | * the retransmit timer, we want 1/2 tick of rounding and | |
1466 | * 1 extra tick because of +-1/2 tick uncertainty in the | |
1467 | * firing of the timer. The bias will give us exactly the | |
1468 | * 1.5 tick we need. But, because the bias is | |
1469 | * statistical, we have to test that we don't drop below | |
1470 | * the minimum feasible timer (which is 2 ticks). | |
1471 | */ | |
1472 | TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp), (short)tp->t_rttmin, | |
1473 | TCPTV_REXMTMAX); /* XXX */ | |
1474 | ||
1475 | /* | |
1476 | * We received an ack for a packet that wasn't retransmitted; | |
1477 | * it is probably safe to discard any error indications we've | |
1478 | * received recently. This isn't quite right, but close enough | |
1479 | * for now (a route might have failed after we sent a segment, | |
1480 | * and the return path might not be symmetrical). | |
1481 | */ | |
1482 | tp->t_softerror = 0; | |
1483 | } | |
1484 | ||
1485 | /* | |
1486 | * Determine a reasonable value for maxseg size. | |
1487 | * If the route is known, check route for mtu. | |
1488 | * If none, use an mss that can be handled on the outgoing | |
1489 | * interface without forcing IP to fragment; if bigger than | |
1490 | * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES | |
1491 | * to utilize large mbufs. If no route is found, route has no mtu, | |
1492 | * or the destination isn't local, use a default, hopefully conservative | |
1493 | * size (usually 512 or the default IP max size, but no more than the mtu | |
1494 | * of the interface), as we can't discover anything about intervening | |
1495 | * gateways or networks. We also initialize the congestion/slow start | |
1496 | * window to be a single segment if the destination isn't local. | |
1497 | * While looking at the routing entry, we also initialize other path-dependent | |
1498 | * parameters from pre-set or cached values in the routing entry. | |
1499 | */ | |
1500 | ||
1501 | int tcp_mss(struct tcpcb *tp, unsigned offer) | |
1502 | { | |
1503 | struct socket *so = tp->t_socket; | |
1504 | int mss; | |
1505 | ||
1506 | DEBUG_CALL("tcp_mss"); | |
1507 | DEBUG_ARG("tp = %p", tp); | |
1508 | DEBUG_ARG("offer = %d", offer); | |
1509 | ||
1510 | switch (so->so_ffamily) { | |
1511 | case AF_INET: | |
1512 | mss = MIN(so->slirp->if_mtu, so->slirp->if_mru) - | |
1513 | sizeof(struct tcphdr) - sizeof(struct ip); | |
1514 | break; | |
1515 | case AF_INET6: | |
1516 | mss = MIN(so->slirp->if_mtu, so->slirp->if_mru) - | |
1517 | sizeof(struct tcphdr) - sizeof(struct ip6); | |
1518 | break; | |
1519 | default: | |
1520 | g_assert_not_reached(); | |
1521 | } | |
1522 | ||
1523 | if (offer) | |
1524 | mss = MIN(mss, offer); | |
1525 | mss = MAX(mss, 32); | |
1526 | if (mss < tp->t_maxseg || offer != 0) | |
1527 | tp->t_maxseg = MIN(mss, TCP_MAXSEG_MAX); | |
1528 | ||
1529 | tp->snd_cwnd = mss; | |
1530 | ||
1531 | sbreserve(&so->so_snd, | |
1532 | TCP_SNDSPACE + | |
1533 | ((TCP_SNDSPACE % mss) ? (mss - (TCP_SNDSPACE % mss)) : 0)); | |
1534 | sbreserve(&so->so_rcv, | |
1535 | TCP_RCVSPACE + | |
1536 | ((TCP_RCVSPACE % mss) ? (mss - (TCP_RCVSPACE % mss)) : 0)); | |
1537 | ||
1538 | DEBUG_MISC(" returning mss = %d", mss); | |
1539 | ||
1540 | return mss; | |
1541 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93 | |
30 | * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | */ | |
37 | ||
38 | #include "slirp.h" | |
39 | ||
40 | static const uint8_t tcp_outflags[TCP_NSTATES] = { | |
41 | TH_RST | TH_ACK, 0, TH_SYN, TH_SYN | TH_ACK, | |
42 | TH_ACK, TH_ACK, TH_FIN | TH_ACK, TH_FIN | TH_ACK, | |
43 | TH_FIN | TH_ACK, TH_ACK, TH_ACK, | |
44 | }; | |
45 | ||
46 | ||
47 | #undef MAX_TCPOPTLEN | |
48 | #define MAX_TCPOPTLEN 32 /* max # bytes that go in options */ | |
49 | ||
50 | /* | |
51 | * Tcp output routine: figure out what should be sent and send it. | |
52 | */ | |
53 | int tcp_output(struct tcpcb *tp) | |
54 | { | |
55 | register struct socket *so = tp->t_socket; | |
56 | register long len, win; | |
57 | int off, flags, error; | |
58 | register struct mbuf *m; | |
59 | register struct tcpiphdr *ti, tcpiph_save; | |
60 | struct ip *ip; | |
61 | struct ip6 *ip6; | |
62 | uint8_t opt[MAX_TCPOPTLEN]; | |
63 | unsigned optlen, hdrlen; | |
64 | int idle, sendalot; | |
65 | ||
66 | DEBUG_CALL("tcp_output"); | |
67 | DEBUG_ARG("tp = %p", tp); | |
68 | ||
69 | /* | |
70 | * Determine length of data that should be transmitted, | |
71 | * and flags that will be used. | |
72 | * If there is some data or critical controls (SYN, RST) | |
73 | * to send, then transmit; otherwise, investigate further. | |
74 | */ | |
75 | idle = (tp->snd_max == tp->snd_una); | |
76 | if (idle && tp->t_idle >= tp->t_rxtcur) | |
77 | /* | |
78 | * We have been idle for "a while" and no acks are | |
79 | * expected to clock out any data we send -- | |
80 | * slow start to get ack "clock" running again. | |
81 | */ | |
82 | tp->snd_cwnd = tp->t_maxseg; | |
83 | again: | |
84 | sendalot = 0; | |
85 | off = tp->snd_nxt - tp->snd_una; | |
86 | win = MIN(tp->snd_wnd, tp->snd_cwnd); | |
87 | ||
88 | flags = tcp_outflags[tp->t_state]; | |
89 | ||
90 | DEBUG_MISC(" --- tcp_output flags = 0x%x", flags); | |
91 | ||
92 | /* | |
93 | * If in persist timeout with window of 0, send 1 byte. | |
94 | * Otherwise, if window is small but nonzero | |
95 | * and timer expired, we will send what we can | |
96 | * and go to transmit state. | |
97 | */ | |
98 | if (tp->t_force) { | |
99 | if (win == 0) { | |
100 | /* | |
101 | * If we still have some data to send, then | |
102 | * clear the FIN bit. Usually this would | |
103 | * happen below when it realizes that we | |
104 | * aren't sending all the data. However, | |
105 | * if we have exactly 1 byte of unset data, | |
106 | * then it won't clear the FIN bit below, | |
107 | * and if we are in persist state, we wind | |
108 | * up sending the packet without recording | |
109 | * that we sent the FIN bit. | |
110 | * | |
111 | * We can't just blindly clear the FIN bit, | |
112 | * because if we don't have any more data | |
113 | * to send then the probe will be the FIN | |
114 | * itself. | |
115 | */ | |
116 | if (off < so->so_snd.sb_cc) | |
117 | flags &= ~TH_FIN; | |
118 | win = 1; | |
119 | } else { | |
120 | tp->t_timer[TCPT_PERSIST] = 0; | |
121 | tp->t_rxtshift = 0; | |
122 | } | |
123 | } | |
124 | ||
125 | len = MIN(so->so_snd.sb_cc, win) - off; | |
126 | ||
127 | if (len < 0) { | |
128 | /* | |
129 | * If FIN has been sent but not acked, | |
130 | * but we haven't been called to retransmit, | |
131 | * len will be -1. Otherwise, window shrank | |
132 | * after we sent into it. If window shrank to 0, | |
133 | * cancel pending retransmit and pull snd_nxt | |
134 | * back to (closed) window. We will enter persist | |
135 | * state below. If the window didn't close completely, | |
136 | * just wait for an ACK. | |
137 | */ | |
138 | len = 0; | |
139 | if (win == 0) { | |
140 | tp->t_timer[TCPT_REXMT] = 0; | |
141 | tp->snd_nxt = tp->snd_una; | |
142 | } | |
143 | } | |
144 | ||
145 | if (len > tp->t_maxseg) { | |
146 | len = tp->t_maxseg; | |
147 | sendalot = 1; | |
148 | } | |
149 | if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc)) | |
150 | flags &= ~TH_FIN; | |
151 | ||
152 | win = sbspace(&so->so_rcv); | |
153 | ||
154 | /* | |
155 | * Sender silly window avoidance. If connection is idle | |
156 | * and can send all data, a maximum segment, | |
157 | * at least a maximum default-size segment do it, | |
158 | * or are forced, do it; otherwise don't bother. | |
159 | * If peer's buffer is tiny, then send | |
160 | * when window is at least half open. | |
161 | * If retransmitting (possibly after persist timer forced us | |
162 | * to send into a small window), then must resend. | |
163 | */ | |
164 | if (len) { | |
165 | if (len == tp->t_maxseg) | |
166 | goto send; | |
167 | if ((1 || idle || tp->t_flags & TF_NODELAY) && | |
168 | len + off >= so->so_snd.sb_cc) | |
169 | goto send; | |
170 | if (tp->t_force) | |
171 | goto send; | |
172 | if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0) | |
173 | goto send; | |
174 | if (SEQ_LT(tp->snd_nxt, tp->snd_max)) | |
175 | goto send; | |
176 | } | |
177 | ||
178 | /* | |
179 | * Compare available window to amount of window | |
180 | * known to peer (as advertised window less | |
181 | * next expected input). If the difference is at least two | |
182 | * max size segments, or at least 50% of the maximum possible | |
183 | * window, then want to send a window update to peer. | |
184 | */ | |
185 | if (win > 0) { | |
186 | /* | |
187 | * "adv" is the amount we can increase the window, | |
188 | * taking into account that we are limited by | |
189 | * TCP_MAXWIN << tp->rcv_scale. | |
190 | */ | |
191 | long adv = MIN(win, (long)TCP_MAXWIN << tp->rcv_scale) - | |
192 | (tp->rcv_adv - tp->rcv_nxt); | |
193 | ||
194 | if (adv >= (long)(2 * tp->t_maxseg)) | |
195 | goto send; | |
196 | if (2 * adv >= (long)so->so_rcv.sb_datalen) | |
197 | goto send; | |
198 | } | |
199 | ||
200 | /* | |
201 | * Send if we owe peer an ACK. | |
202 | */ | |
203 | if (tp->t_flags & TF_ACKNOW) | |
204 | goto send; | |
205 | if (flags & (TH_SYN | TH_RST)) | |
206 | goto send; | |
207 | if (SEQ_GT(tp->snd_up, tp->snd_una)) | |
208 | goto send; | |
209 | /* | |
210 | * If our state indicates that FIN should be sent | |
211 | * and we have not yet done so, or we're retransmitting the FIN, | |
212 | * then we need to send. | |
213 | */ | |
214 | if (flags & TH_FIN && | |
215 | ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una)) | |
216 | goto send; | |
217 | ||
218 | /* | |
219 | * TCP window updates are not reliable, rather a polling protocol | |
220 | * using ``persist'' packets is used to insure receipt of window | |
221 | * updates. The three ``states'' for the output side are: | |
222 | * idle not doing retransmits or persists | |
223 | * persisting to move a small or zero window | |
224 | * (re)transmitting and thereby not persisting | |
225 | * | |
226 | * tp->t_timer[TCPT_PERSIST] | |
227 | * is set when we are in persist state. | |
228 | * tp->t_force | |
229 | * is set when we are called to send a persist packet. | |
230 | * tp->t_timer[TCPT_REXMT] | |
231 | * is set when we are retransmitting | |
232 | * The output side is idle when both timers are zero. | |
233 | * | |
234 | * If send window is too small, there is data to transmit, and no | |
235 | * retransmit or persist is pending, then go to persist state. | |
236 | * If nothing happens soon, send when timer expires: | |
237 | * if window is nonzero, transmit what we can, | |
238 | * otherwise force out a byte. | |
239 | */ | |
240 | if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 && | |
241 | tp->t_timer[TCPT_PERSIST] == 0) { | |
242 | tp->t_rxtshift = 0; | |
243 | tcp_setpersist(tp); | |
244 | } | |
245 | ||
246 | /* | |
247 | * No reason to send a segment, just return. | |
248 | */ | |
249 | return (0); | |
250 | ||
251 | send: | |
252 | /* | |
253 | * Before ESTABLISHED, force sending of initial options | |
254 | * unless TCP set not to do any options. | |
255 | * NOTE: we assume that the IP/TCP header plus TCP options | |
256 | * always fit in a single mbuf, leaving room for a maximum | |
257 | * link header, i.e. | |
258 | * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN | |
259 | */ | |
260 | optlen = 0; | |
261 | hdrlen = sizeof(struct tcpiphdr); | |
262 | if (flags & TH_SYN) { | |
263 | tp->snd_nxt = tp->iss; | |
264 | if ((tp->t_flags & TF_NOOPT) == 0) { | |
265 | uint16_t mss; | |
266 | ||
267 | opt[0] = TCPOPT_MAXSEG; | |
268 | opt[1] = 4; | |
269 | mss = htons((uint16_t)tcp_mss(tp, 0)); | |
270 | memcpy((char *)(opt + 2), (char *)&mss, sizeof(mss)); | |
271 | optlen = 4; | |
272 | } | |
273 | } | |
274 | ||
275 | hdrlen += optlen; | |
276 | ||
277 | /* | |
278 | * Adjust data length if insertion of options will | |
279 | * bump the packet length beyond the t_maxseg length. | |
280 | */ | |
281 | if (len > tp->t_maxseg - optlen) { | |
282 | len = tp->t_maxseg - optlen; | |
283 | sendalot = 1; | |
284 | } | |
285 | ||
286 | /* | |
287 | * Grab a header mbuf, attaching a copy of data to | |
288 | * be transmitted, and initialize the header from | |
289 | * the template for sends on this connection. | |
290 | */ | |
291 | if (len) { | |
292 | m = m_get(so->slirp); | |
293 | if (m == NULL) { | |
294 | error = 1; | |
295 | goto out; | |
296 | } | |
297 | m->m_data += IF_MAXLINKHDR; | |
298 | m->m_len = hdrlen; | |
299 | ||
300 | sbcopy(&so->so_snd, off, (int)len, mtod(m, char *) + hdrlen); | |
301 | m->m_len += len; | |
302 | ||
303 | /* | |
304 | * If we're sending everything we've got, set PUSH. | |
305 | * (This will keep happy those implementations which only | |
306 | * give data to the user when a buffer fills or | |
307 | * a PUSH comes in.) | |
308 | */ | |
309 | if (off + len == so->so_snd.sb_cc) | |
310 | flags |= TH_PUSH; | |
311 | } else { | |
312 | m = m_get(so->slirp); | |
313 | if (m == NULL) { | |
314 | error = 1; | |
315 | goto out; | |
316 | } | |
317 | m->m_data += IF_MAXLINKHDR; | |
318 | m->m_len = hdrlen; | |
319 | } | |
320 | ||
321 | ti = mtod(m, struct tcpiphdr *); | |
322 | ||
323 | memcpy((char *)ti, &tp->t_template, sizeof(struct tcpiphdr)); | |
324 | ||
325 | /* | |
326 | * Fill in fields, remembering maximum advertised | |
327 | * window for use in delaying messages about window sizes. | |
328 | * If resending a FIN, be sure not to use a new sequence number. | |
329 | */ | |
330 | if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && | |
331 | tp->snd_nxt == tp->snd_max) | |
332 | tp->snd_nxt--; | |
333 | /* | |
334 | * If we are doing retransmissions, then snd_nxt will | |
335 | * not reflect the first unsent octet. For ACK only | |
336 | * packets, we do not want the sequence number of the | |
337 | * retransmitted packet, we want the sequence number | |
338 | * of the next unsent octet. So, if there is no data | |
339 | * (and no SYN or FIN), use snd_max instead of snd_nxt | |
340 | * when filling in ti_seq. But if we are in persist | |
341 | * state, snd_max might reflect one byte beyond the | |
342 | * right edge of the window, so use snd_nxt in that | |
343 | * case, since we know we aren't doing a retransmission. | |
344 | * (retransmit and persist are mutually exclusive...) | |
345 | */ | |
346 | if (len || (flags & (TH_SYN | TH_FIN)) || tp->t_timer[TCPT_PERSIST]) | |
347 | ti->ti_seq = htonl(tp->snd_nxt); | |
348 | else | |
349 | ti->ti_seq = htonl(tp->snd_max); | |
350 | ti->ti_ack = htonl(tp->rcv_nxt); | |
351 | if (optlen) { | |
352 | memcpy((char *)(ti + 1), (char *)opt, optlen); | |
353 | ti->ti_off = (sizeof(struct tcphdr) + optlen) >> 2; | |
354 | } | |
355 | ti->ti_flags = flags; | |
356 | /* | |
357 | * Calculate receive window. Don't shrink window, | |
358 | * but avoid silly window syndrome. | |
359 | */ | |
360 | if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg) | |
361 | win = 0; | |
362 | if (win > (long)TCP_MAXWIN << tp->rcv_scale) | |
363 | win = (long)TCP_MAXWIN << tp->rcv_scale; | |
364 | if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) | |
365 | win = (long)(tp->rcv_adv - tp->rcv_nxt); | |
366 | ti->ti_win = htons((uint16_t)(win >> tp->rcv_scale)); | |
367 | ||
368 | if (SEQ_GT(tp->snd_up, tp->snd_una)) { | |
369 | ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); | |
370 | ti->ti_flags |= TH_URG; | |
371 | } else | |
372 | /* | |
373 | * If no urgent pointer to send, then we pull | |
374 | * the urgent pointer to the left edge of the send window | |
375 | * so that it doesn't drift into the send window on sequence | |
376 | * number wraparound. | |
377 | */ | |
378 | tp->snd_up = tp->snd_una; /* drag it along */ | |
379 | ||
380 | /* | |
381 | * Put TCP length in extended header, and then | |
382 | * checksum extended header and data. | |
383 | */ | |
384 | if (len + optlen) | |
385 | ti->ti_len = htons((uint16_t)(sizeof(struct tcphdr) + optlen + len)); | |
386 | ti->ti_sum = cksum(m, (int)(hdrlen + len)); | |
387 | ||
388 | /* | |
389 | * In transmit state, time the transmission and arrange for | |
390 | * the retransmit. In persist state, just set snd_max. | |
391 | */ | |
392 | if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) { | |
393 | tcp_seq startseq = tp->snd_nxt; | |
394 | ||
395 | /* | |
396 | * Advance snd_nxt over sequence space of this segment. | |
397 | */ | |
398 | if (flags & (TH_SYN | TH_FIN)) { | |
399 | if (flags & TH_SYN) | |
400 | tp->snd_nxt++; | |
401 | if (flags & TH_FIN) { | |
402 | tp->snd_nxt++; | |
403 | tp->t_flags |= TF_SENTFIN; | |
404 | } | |
405 | } | |
406 | tp->snd_nxt += len; | |
407 | if (SEQ_GT(tp->snd_nxt, tp->snd_max)) { | |
408 | tp->snd_max = tp->snd_nxt; | |
409 | /* | |
410 | * Time this transmission if not a retransmission and | |
411 | * not currently timing anything. | |
412 | */ | |
413 | if (tp->t_rtt == 0) { | |
414 | tp->t_rtt = 1; | |
415 | tp->t_rtseq = startseq; | |
416 | } | |
417 | } | |
418 | ||
419 | /* | |
420 | * Set retransmit timer if not currently set, | |
421 | * and not doing an ack or a keep-alive probe. | |
422 | * Initial value for retransmit timer is smoothed | |
423 | * round-trip time + 2 * round-trip time variance. | |
424 | * Initialize shift counter which is used for backoff | |
425 | * of retransmit time. | |
426 | */ | |
427 | if (tp->t_timer[TCPT_REXMT] == 0 && tp->snd_nxt != tp->snd_una) { | |
428 | tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; | |
429 | if (tp->t_timer[TCPT_PERSIST]) { | |
430 | tp->t_timer[TCPT_PERSIST] = 0; | |
431 | tp->t_rxtshift = 0; | |
432 | } | |
433 | } | |
434 | } else if (SEQ_GT(tp->snd_nxt + len, tp->snd_max)) | |
435 | tp->snd_max = tp->snd_nxt + len; | |
436 | ||
437 | /* | |
438 | * Fill in IP length and desired time to live and | |
439 | * send to IP level. There should be a better way | |
440 | * to handle ttl and tos; we could keep them in | |
441 | * the template, but need a way to checksum without them. | |
442 | */ | |
443 | m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */ | |
444 | tcpiph_save = *mtod(m, struct tcpiphdr *); | |
445 | ||
446 | switch (so->so_ffamily) { | |
447 | case AF_INET: | |
448 | m->m_data += | |
449 | sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); | |
450 | m->m_len -= | |
451 | sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); | |
452 | ip = mtod(m, struct ip *); | |
453 | ||
454 | ip->ip_len = m->m_len; | |
455 | ip->ip_dst = tcpiph_save.ti_dst; | |
456 | ip->ip_src = tcpiph_save.ti_src; | |
457 | ip->ip_p = tcpiph_save.ti_pr; | |
458 | ||
459 | ip->ip_ttl = IPDEFTTL; | |
460 | ip->ip_tos = so->so_iptos; | |
461 | error = ip_output(so, m); | |
462 | break; | |
463 | ||
464 | case AF_INET6: | |
465 | m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - | |
466 | sizeof(struct ip6); | |
467 | m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - | |
468 | sizeof(struct ip6); | |
469 | ip6 = mtod(m, struct ip6 *); | |
470 | ||
471 | ip6->ip_pl = tcpiph_save.ti_len; | |
472 | ip6->ip_dst = tcpiph_save.ti_dst6; | |
473 | ip6->ip_src = tcpiph_save.ti_src6; | |
474 | ip6->ip_nh = tcpiph_save.ti_nh6; | |
475 | ||
476 | error = ip6_output(so, m, 0); | |
477 | break; | |
478 | ||
479 | default: | |
480 | g_assert_not_reached(); | |
481 | } | |
482 | ||
483 | if (error) { | |
484 | out: | |
485 | return (error); | |
486 | } | |
487 | ||
488 | /* | |
489 | * Data sent (as far as we can tell). | |
490 | * If this advertises a larger window than any other segment, | |
491 | * then remember the size of the advertised window. | |
492 | * Any pending ACK has now been sent. | |
493 | */ | |
494 | if (win > 0 && SEQ_GT(tp->rcv_nxt + win, tp->rcv_adv)) | |
495 | tp->rcv_adv = tp->rcv_nxt + win; | |
496 | tp->last_ack_sent = tp->rcv_nxt; | |
497 | tp->t_flags &= ~(TF_ACKNOW | TF_DELACK); | |
498 | if (sendalot) | |
499 | goto again; | |
500 | ||
501 | return (0); | |
502 | } | |
503 | ||
504 | void tcp_setpersist(struct tcpcb *tp) | |
505 | { | |
506 | int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1; | |
507 | ||
508 | /* | |
509 | * Start/restart persistence timer. | |
510 | */ | |
511 | TCPT_RANGESET(tp->t_timer[TCPT_PERSIST], t * tcp_backoff[tp->t_rxtshift], | |
512 | TCPTV_PERSMIN, TCPTV_PERSMAX); | |
513 | if (tp->t_rxtshift < TCP_MAXRXTSHIFT) | |
514 | tp->t_rxtshift++; | |
515 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93 | |
30 | * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | */ | |
37 | ||
38 | #include "slirp.h" | |
39 | ||
40 | /* patchable/settable parameters for tcp */ | |
41 | /* Don't do rfc1323 performance enhancements */ | |
42 | #define TCP_DO_RFC1323 0 | |
43 | ||
44 | /* | |
45 | * Tcp initialization | |
46 | */ | |
47 | void tcp_init(Slirp *slirp) | |
48 | { | |
49 | slirp->tcp_iss = 1; /* wrong */ | |
50 | slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb; | |
51 | slirp->tcp_last_so = &slirp->tcb; | |
52 | } | |
53 | ||
54 | void tcp_cleanup(Slirp *slirp) | |
55 | { | |
56 | while (slirp->tcb.so_next != &slirp->tcb) { | |
57 | tcp_close(sototcpcb(slirp->tcb.so_next)); | |
58 | } | |
59 | } | |
60 | ||
61 | /* | |
62 | * Create template to be used to send tcp packets on a connection. | |
63 | * Call after host entry created, fills | |
64 | * in a skeletal tcp/ip header, minimizing the amount of work | |
65 | * necessary when the connection is used. | |
66 | */ | |
67 | void tcp_template(struct tcpcb *tp) | |
68 | { | |
69 | struct socket *so = tp->t_socket; | |
70 | register struct tcpiphdr *n = &tp->t_template; | |
71 | ||
72 | n->ti_mbuf = NULL; | |
73 | memset(&n->ti, 0, sizeof(n->ti)); | |
74 | n->ti_x0 = 0; | |
75 | switch (so->so_ffamily) { | |
76 | case AF_INET: | |
77 | n->ti_pr = IPPROTO_TCP; | |
78 | n->ti_len = htons(sizeof(struct tcphdr)); | |
79 | n->ti_src = so->so_faddr; | |
80 | n->ti_dst = so->so_laddr; | |
81 | n->ti_sport = so->so_fport; | |
82 | n->ti_dport = so->so_lport; | |
83 | break; | |
84 | ||
85 | case AF_INET6: | |
86 | n->ti_nh6 = IPPROTO_TCP; | |
87 | n->ti_len = htons(sizeof(struct tcphdr)); | |
88 | n->ti_src6 = so->so_faddr6; | |
89 | n->ti_dst6 = so->so_laddr6; | |
90 | n->ti_sport = so->so_fport6; | |
91 | n->ti_dport = so->so_lport6; | |
92 | break; | |
93 | ||
94 | default: | |
95 | g_assert_not_reached(); | |
96 | } | |
97 | ||
98 | n->ti_seq = 0; | |
99 | n->ti_ack = 0; | |
100 | n->ti_x2 = 0; | |
101 | n->ti_off = 5; | |
102 | n->ti_flags = 0; | |
103 | n->ti_win = 0; | |
104 | n->ti_sum = 0; | |
105 | n->ti_urp = 0; | |
106 | } | |
107 | ||
108 | /* | |
109 | * Send a single message to the TCP at address specified by | |
110 | * the given TCP/IP header. If m == 0, then we make a copy | |
111 | * of the tcpiphdr at ti and send directly to the addressed host. | |
112 | * This is used to force keep alive messages out using the TCP | |
113 | * template for a connection tp->t_template. If flags are given | |
114 | * then we send a message back to the TCP which originated the | |
115 | * segment ti, and discard the mbuf containing it and any other | |
116 | * attached mbufs. | |
117 | * | |
118 | * In any case the ack and sequence number of the transmitted | |
119 | * segment are as specified by the parameters. | |
120 | */ | |
121 | void tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, | |
122 | tcp_seq ack, tcp_seq seq, int flags, unsigned short af) | |
123 | { | |
124 | register int tlen; | |
125 | int win = 0; | |
126 | ||
127 | DEBUG_CALL("tcp_respond"); | |
128 | DEBUG_ARG("tp = %p", tp); | |
129 | DEBUG_ARG("ti = %p", ti); | |
130 | DEBUG_ARG("m = %p", m); | |
131 | DEBUG_ARG("ack = %u", ack); | |
132 | DEBUG_ARG("seq = %u", seq); | |
133 | DEBUG_ARG("flags = %x", flags); | |
134 | ||
135 | if (tp) | |
136 | win = sbspace(&tp->t_socket->so_rcv); | |
137 | if (m == NULL) { | |
138 | if (!tp || (m = m_get(tp->t_socket->slirp)) == NULL) | |
139 | return; | |
140 | tlen = 0; | |
141 | m->m_data += IF_MAXLINKHDR; | |
142 | *mtod(m, struct tcpiphdr *) = *ti; | |
143 | ti = mtod(m, struct tcpiphdr *); | |
144 | switch (af) { | |
145 | case AF_INET: | |
146 | ti->ti.ti_i4.ih_x1 = 0; | |
147 | break; | |
148 | case AF_INET6: | |
149 | ti->ti.ti_i6.ih_x1 = 0; | |
150 | break; | |
151 | default: | |
152 | g_assert_not_reached(); | |
153 | } | |
154 | flags = TH_ACK; | |
155 | } else { | |
156 | /* | |
157 | * ti points into m so the next line is just making | |
158 | * the mbuf point to ti | |
159 | */ | |
160 | m->m_data = (char *)ti; | |
161 | ||
162 | m->m_len = sizeof(struct tcpiphdr); | |
163 | tlen = 0; | |
164 | #define xchg(a, b, type) \ | |
165 | { \ | |
166 | type t; \ | |
167 | t = a; \ | |
168 | a = b; \ | |
169 | b = t; \ | |
170 | } | |
171 | switch (af) { | |
172 | case AF_INET: | |
173 | xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); | |
174 | xchg(ti->ti_dport, ti->ti_sport, uint16_t); | |
175 | break; | |
176 | case AF_INET6: | |
177 | xchg(ti->ti_dst6, ti->ti_src6, struct in6_addr); | |
178 | xchg(ti->ti_dport, ti->ti_sport, uint16_t); | |
179 | break; | |
180 | default: | |
181 | g_assert_not_reached(); | |
182 | } | |
183 | #undef xchg | |
184 | } | |
185 | ti->ti_len = htons((uint16_t)(sizeof(struct tcphdr) + tlen)); | |
186 | tlen += sizeof(struct tcpiphdr); | |
187 | m->m_len = tlen; | |
188 | ||
189 | ti->ti_mbuf = NULL; | |
190 | ti->ti_x0 = 0; | |
191 | ti->ti_seq = htonl(seq); | |
192 | ti->ti_ack = htonl(ack); | |
193 | ti->ti_x2 = 0; | |
194 | ti->ti_off = sizeof(struct tcphdr) >> 2; | |
195 | ti->ti_flags = flags; | |
196 | if (tp) | |
197 | ti->ti_win = htons((uint16_t)(win >> tp->rcv_scale)); | |
198 | else | |
199 | ti->ti_win = htons((uint16_t)win); | |
200 | ti->ti_urp = 0; | |
201 | ti->ti_sum = 0; | |
202 | ti->ti_sum = cksum(m, tlen); | |
203 | ||
204 | struct tcpiphdr tcpiph_save = *(mtod(m, struct tcpiphdr *)); | |
205 | struct ip *ip; | |
206 | struct ip6 *ip6; | |
207 | ||
208 | switch (af) { | |
209 | case AF_INET: | |
210 | m->m_data += | |
211 | sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); | |
212 | m->m_len -= | |
213 | sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - sizeof(struct ip); | |
214 | ip = mtod(m, struct ip *); | |
215 | ip->ip_len = m->m_len; | |
216 | ip->ip_dst = tcpiph_save.ti_dst; | |
217 | ip->ip_src = tcpiph_save.ti_src; | |
218 | ip->ip_p = tcpiph_save.ti_pr; | |
219 | ||
220 | if (flags & TH_RST) { | |
221 | ip->ip_ttl = MAXTTL; | |
222 | } else { | |
223 | ip->ip_ttl = IPDEFTTL; | |
224 | } | |
225 | ||
226 | ip_output(NULL, m); | |
227 | break; | |
228 | ||
229 | case AF_INET6: | |
230 | m->m_data += sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - | |
231 | sizeof(struct ip6); | |
232 | m->m_len -= sizeof(struct tcpiphdr) - sizeof(struct tcphdr) - | |
233 | sizeof(struct ip6); | |
234 | ip6 = mtod(m, struct ip6 *); | |
235 | ip6->ip_pl = tcpiph_save.ti_len; | |
236 | ip6->ip_dst = tcpiph_save.ti_dst6; | |
237 | ip6->ip_src = tcpiph_save.ti_src6; | |
238 | ip6->ip_nh = tcpiph_save.ti_nh6; | |
239 | ||
240 | ip6_output(NULL, m, 0); | |
241 | break; | |
242 | ||
243 | default: | |
244 | g_assert_not_reached(); | |
245 | } | |
246 | } | |
247 | ||
248 | /* | |
249 | * Create a new TCP control block, making an | |
250 | * empty reassembly queue and hooking it to the argument | |
251 | * protocol control block. | |
252 | */ | |
253 | struct tcpcb *tcp_newtcpcb(struct socket *so) | |
254 | { | |
255 | register struct tcpcb *tp; | |
256 | ||
257 | tp = (struct tcpcb *)malloc(sizeof(*tp)); | |
258 | if (tp == NULL) | |
259 | return ((struct tcpcb *)0); | |
260 | ||
261 | memset((char *)tp, 0, sizeof(struct tcpcb)); | |
262 | tp->seg_next = tp->seg_prev = (struct tcpiphdr *)tp; | |
263 | /* | |
264 | * 40: length of IPv4 header (20) + TCP header (20) | |
265 | * 60: length of IPv6 header (40) + TCP header (20) | |
266 | */ | |
267 | tp->t_maxseg = | |
268 | MIN(so->slirp->if_mtu - ((so->so_ffamily == AF_INET) ? 40 : 60), | |
269 | TCP_MAXSEG_MAX); | |
270 | ||
271 | tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE | TF_REQ_TSTMP) : 0; | |
272 | tp->t_socket = so; | |
273 | ||
274 | /* | |
275 | * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no | |
276 | * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives | |
277 | * reasonable initial retransmit time. | |
278 | */ | |
279 | tp->t_srtt = TCPTV_SRTTBASE; | |
280 | tp->t_rttvar = TCPTV_SRTTDFLT << 2; | |
281 | tp->t_rttmin = TCPTV_MIN; | |
282 | ||
283 | TCPT_RANGESET(tp->t_rxtcur, | |
284 | ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1, | |
285 | TCPTV_MIN, TCPTV_REXMTMAX); | |
286 | ||
287 | tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT; | |
288 | tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT; | |
289 | tp->t_state = TCPS_CLOSED; | |
290 | ||
291 | so->so_tcpcb = tp; | |
292 | ||
293 | return (tp); | |
294 | } | |
295 | ||
296 | /* | |
297 | * Drop a TCP connection, reporting | |
298 | * the specified error. If connection is synchronized, | |
299 | * then send a RST to peer. | |
300 | */ | |
301 | struct tcpcb *tcp_drop(struct tcpcb *tp, int err) | |
302 | { | |
303 | DEBUG_CALL("tcp_drop"); | |
304 | DEBUG_ARG("tp = %p", tp); | |
305 | DEBUG_ARG("errno = %d", errno); | |
306 | ||
307 | if (TCPS_HAVERCVDSYN(tp->t_state)) { | |
308 | tp->t_state = TCPS_CLOSED; | |
309 | (void)tcp_output(tp); | |
310 | } | |
311 | return (tcp_close(tp)); | |
312 | } | |
313 | ||
314 | /* | |
315 | * Close a TCP control block: | |
316 | * discard all space held by the tcp | |
317 | * discard internet protocol block | |
318 | * wake up any sleepers | |
319 | */ | |
320 | struct tcpcb *tcp_close(struct tcpcb *tp) | |
321 | { | |
322 | register struct tcpiphdr *t; | |
323 | struct socket *so = tp->t_socket; | |
324 | Slirp *slirp = so->slirp; | |
325 | register struct mbuf *m; | |
326 | ||
327 | DEBUG_CALL("tcp_close"); | |
328 | DEBUG_ARG("tp = %p", tp); | |
329 | ||
330 | /* free the reassembly queue, if any */ | |
331 | t = tcpfrag_list_first(tp); | |
332 | while (!tcpfrag_list_end(t, tp)) { | |
333 | t = tcpiphdr_next(t); | |
334 | m = tcpiphdr_prev(t)->ti_mbuf; | |
335 | remque(tcpiphdr2qlink(tcpiphdr_prev(t))); | |
336 | m_free(m); | |
337 | } | |
338 | free(tp); | |
339 | so->so_tcpcb = NULL; | |
340 | /* clobber input socket cache if we're closing the cached connection */ | |
341 | if (so == slirp->tcp_last_so) | |
342 | slirp->tcp_last_so = &slirp->tcb; | |
343 | so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); | |
344 | closesocket(so->s); | |
345 | sbfree(&so->so_rcv); | |
346 | sbfree(&so->so_snd); | |
347 | sofree(so); | |
348 | return ((struct tcpcb *)0); | |
349 | } | |
350 | ||
351 | /* | |
352 | * TCP protocol interface to socket abstraction. | |
353 | */ | |
354 | ||
355 | /* | |
356 | * User issued close, and wish to trail through shutdown states: | |
357 | * if never received SYN, just forget it. If got a SYN from peer, | |
358 | * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN. | |
359 | * If already got a FIN from peer, then almost done; go to LAST_ACK | |
360 | * state. In all other cases, have already sent FIN to peer (e.g. | |
361 | * after PRU_SHUTDOWN), and just have to play tedious game waiting | |
362 | * for peer to send FIN or not respond to keep-alives, etc. | |
363 | * We can let the user exit from the close as soon as the FIN is acked. | |
364 | */ | |
365 | void tcp_sockclosed(struct tcpcb *tp) | |
366 | { | |
367 | DEBUG_CALL("tcp_sockclosed"); | |
368 | DEBUG_ARG("tp = %p", tp); | |
369 | ||
370 | if (!tp) { | |
371 | return; | |
372 | } | |
373 | ||
374 | switch (tp->t_state) { | |
375 | case TCPS_CLOSED: | |
376 | case TCPS_LISTEN: | |
377 | case TCPS_SYN_SENT: | |
378 | tp->t_state = TCPS_CLOSED; | |
379 | tp = tcp_close(tp); | |
380 | break; | |
381 | ||
382 | case TCPS_SYN_RECEIVED: | |
383 | case TCPS_ESTABLISHED: | |
384 | tp->t_state = TCPS_FIN_WAIT_1; | |
385 | break; | |
386 | ||
387 | case TCPS_CLOSE_WAIT: | |
388 | tp->t_state = TCPS_LAST_ACK; | |
389 | break; | |
390 | } | |
391 | tcp_output(tp); | |
392 | } | |
393 | ||
394 | /* | |
395 | * Connect to a host on the Internet | |
396 | * Called by tcp_input | |
397 | * Only do a connect, the tcp fields will be set in tcp_input | |
398 | * return 0 if there's a result of the connect, | |
399 | * else return -1 means we're still connecting | |
400 | * The return value is almost always -1 since the socket is | |
401 | * nonblocking. Connect returns after the SYN is sent, and does | |
402 | * not wait for ACK+SYN. | |
403 | */ | |
404 | int tcp_fconnect(struct socket *so, unsigned short af) | |
405 | { | |
406 | int ret = 0; | |
407 | ||
408 | DEBUG_CALL("tcp_fconnect"); | |
409 | DEBUG_ARG("so = %p", so); | |
410 | ||
411 | ret = so->s = slirp_socket(af, SOCK_STREAM, 0); | |
412 | if (ret >= 0) { | |
413 | int opt, s = so->s; | |
414 | struct sockaddr_storage addr; | |
415 | ||
416 | slirp_set_nonblock(s); | |
417 | so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); | |
418 | slirp_socket_set_fast_reuse(s); | |
419 | opt = 1; | |
420 | setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); | |
421 | opt = 1; | |
422 | setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); | |
423 | ||
424 | addr = so->fhost.ss; | |
425 | DEBUG_CALL(" connect()ing"); | |
426 | if (sotranslate_out(so, &addr) < 0) { | |
427 | return -1; | |
428 | } | |
429 | ||
430 | /* We don't care what port we get */ | |
431 | ret = connect(s, (struct sockaddr *)&addr, sockaddr_size(&addr)); | |
432 | ||
433 | /* | |
434 | * If it's not in progress, it failed, so we just return 0, | |
435 | * without clearing SS_NOFDREF | |
436 | */ | |
437 | soisfconnecting(so); | |
438 | } | |
439 | ||
440 | return (ret); | |
441 | } | |
442 | ||
443 | /* | |
444 | * Accept the socket and connect to the local-host | |
445 | * | |
446 | * We have a problem. The correct thing to do would be | |
447 | * to first connect to the local-host, and only if the | |
448 | * connection is accepted, then do an accept() here. | |
449 | * But, a) we need to know who's trying to connect | |
450 | * to the socket to be able to SYN the local-host, and | |
451 | * b) we are already connected to the foreign host by | |
452 | * the time it gets to accept(), so... We simply accept | |
453 | * here and SYN the local-host. | |
454 | */ | |
455 | void tcp_connect(struct socket *inso) | |
456 | { | |
457 | Slirp *slirp = inso->slirp; | |
458 | struct socket *so; | |
459 | struct sockaddr_storage addr; | |
460 | socklen_t addrlen = sizeof(struct sockaddr_storage); | |
461 | struct tcpcb *tp; | |
462 | int s, opt; | |
463 | ||
464 | DEBUG_CALL("tcp_connect"); | |
465 | DEBUG_ARG("inso = %p", inso); | |
466 | ||
467 | /* | |
468 | * If it's an SS_ACCEPTONCE socket, no need to socreate() | |
469 | * another socket, just use the accept() socket. | |
470 | */ | |
471 | if (inso->so_state & SS_FACCEPTONCE) { | |
472 | /* FACCEPTONCE already have a tcpcb */ | |
473 | so = inso; | |
474 | } else { | |
475 | so = socreate(slirp); | |
476 | if (tcp_attach(so) < 0) { | |
477 | g_free(so); /* NOT sofree */ | |
478 | return; | |
479 | } | |
480 | so->lhost = inso->lhost; | |
481 | so->so_ffamily = inso->so_ffamily; | |
482 | } | |
483 | ||
484 | tcp_mss(sototcpcb(so), 0); | |
485 | ||
486 | s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); | |
487 | if (s < 0) { | |
488 | tcp_close(sototcpcb(so)); /* This will sofree() as well */ | |
489 | return; | |
490 | } | |
491 | slirp_set_nonblock(s); | |
492 | so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); | |
493 | slirp_socket_set_fast_reuse(s); | |
494 | opt = 1; | |
495 | setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); | |
496 | slirp_socket_set_nodelay(s); | |
497 | ||
498 | so->fhost.ss = addr; | |
499 | sotranslate_accept(so); | |
500 | ||
501 | /* Close the accept() socket, set right state */ | |
502 | if (inso->so_state & SS_FACCEPTONCE) { | |
503 | /* If we only accept once, close the accept() socket */ | |
504 | so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); | |
505 | closesocket(so->s); | |
506 | ||
507 | /* Don't select it yet, even though we have an FD */ | |
508 | /* if it's not FACCEPTONCE, it's already NOFDREF */ | |
509 | so->so_state = SS_NOFDREF; | |
510 | } | |
511 | so->s = s; | |
512 | so->so_state |= SS_INCOMING; | |
513 | ||
514 | so->so_iptos = tcp_tos(so); | |
515 | tp = sototcpcb(so); | |
516 | ||
517 | tcp_template(tp); | |
518 | ||
519 | tp->t_state = TCPS_SYN_SENT; | |
520 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT; | |
521 | tp->iss = slirp->tcp_iss; | |
522 | slirp->tcp_iss += TCP_ISSINCR / 2; | |
523 | tcp_sendseqinit(tp); | |
524 | tcp_output(tp); | |
525 | } | |
526 | ||
527 | /* | |
528 | * Attach a TCPCB to a socket. | |
529 | */ | |
530 | int tcp_attach(struct socket *so) | |
531 | { | |
532 | if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) | |
533 | return -1; | |
534 | ||
535 | insque(so, &so->slirp->tcb); | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | /* | |
541 | * Set the socket's type of service field | |
542 | */ | |
543 | static const struct tos_t tcptos[] = { | |
544 | { 0, 20, IPTOS_THROUGHPUT, 0 }, /* ftp data */ | |
545 | { 21, 21, IPTOS_LOWDELAY, EMU_FTP }, /* ftp control */ | |
546 | { 0, 23, IPTOS_LOWDELAY, 0 }, /* telnet */ | |
547 | { 0, 80, IPTOS_THROUGHPUT, 0 }, /* WWW */ | |
548 | { 0, 513, IPTOS_LOWDELAY, EMU_RLOGIN | EMU_NOCONNECT }, /* rlogin */ | |
549 | { 0, 544, IPTOS_LOWDELAY, EMU_KSH }, /* kshell */ | |
550 | { 0, 543, IPTOS_LOWDELAY, 0 }, /* klogin */ | |
551 | { 0, 6667, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC */ | |
552 | { 0, 6668, IPTOS_THROUGHPUT, EMU_IRC }, /* IRC undernet */ | |
553 | { 0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */ | |
554 | { 0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */ | |
555 | { 0, 0, 0, 0 } | |
556 | }; | |
557 | ||
558 | /* | |
559 | * Return TOS according to the above table | |
560 | */ | |
561 | uint8_t tcp_tos(struct socket *so) | |
562 | { | |
563 | int i = 0; | |
564 | ||
565 | while (tcptos[i].tos) { | |
566 | if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) || | |
567 | (tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) { | |
568 | if (so->slirp->enable_emu) | |
569 | so->so_emu = tcptos[i].emu; | |
570 | return tcptos[i].tos; | |
571 | } | |
572 | i++; | |
573 | } | |
574 | return 0; | |
575 | } | |
576 | ||
577 | /* | |
578 | * Emulate programs that try and connect to us | |
579 | * This includes ftp (the data connection is | |
580 | * initiated by the server) and IRC (DCC CHAT and | |
581 | * DCC SEND) for now | |
582 | * | |
583 | * NOTE: It's possible to crash SLiRP by sending it | |
584 | * unstandard strings to emulate... if this is a problem, | |
585 | * more checks are needed here | |
586 | * | |
587 | * XXX Assumes the whole command came in one packet | |
588 | * | |
589 | * XXX Some ftp clients will have their TOS set to | |
590 | * LOWDELAY and so Nagel will kick in. Because of this, | |
591 | * we'll get the first letter, followed by the rest, so | |
592 | * we simply scan for ORT instead of PORT... | |
593 | * DCC doesn't have this problem because there's other stuff | |
594 | * in the packet before the DCC command. | |
595 | * | |
596 | * Return 1 if the mbuf m is still valid and should be | |
597 | * sbappend()ed | |
598 | * | |
599 | * NOTE: if you return 0 you MUST m_free() the mbuf! | |
600 | */ | |
601 | int tcp_emu(struct socket *so, struct mbuf *m) | |
602 | { | |
603 | Slirp *slirp = so->slirp; | |
604 | unsigned n1, n2, n3, n4, n5, n6; | |
605 | char buff[257]; | |
606 | uint32_t laddr; | |
607 | unsigned lport; | |
608 | char *bptr; | |
609 | ||
610 | DEBUG_CALL("tcp_emu"); | |
611 | DEBUG_ARG("so = %p", so); | |
612 | DEBUG_ARG("m = %p", m); | |
613 | ||
614 | switch (so->so_emu) { | |
615 | int x, i; | |
616 | ||
617 | /* TODO: IPv6 */ | |
618 | case EMU_IDENT: | |
619 | /* | |
620 | * Identification protocol as per rfc-1413 | |
621 | */ | |
622 | ||
623 | { | |
624 | struct socket *tmpso; | |
625 | struct sockaddr_in addr; | |
626 | socklen_t addrlen = sizeof(struct sockaddr_in); | |
627 | char *eol = g_strstr_len(m->m_data, m->m_len, "\r\n"); | |
628 | ||
629 | if (!eol) { | |
630 | return 1; | |
631 | } | |
632 | ||
633 | *eol = '\0'; | |
634 | if (sscanf(m->m_data, "%u%*[ ,]%u", &n1, &n2) == 2) { | |
635 | HTONS(n1); | |
636 | HTONS(n2); | |
637 | /* n2 is the one on our host */ | |
638 | for (tmpso = slirp->tcb.so_next; tmpso != &slirp->tcb; | |
639 | tmpso = tmpso->so_next) { | |
640 | if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr && | |
641 | tmpso->so_lport == n2 && | |
642 | tmpso->so_faddr.s_addr == so->so_faddr.s_addr && | |
643 | tmpso->so_fport == n1) { | |
644 | if (getsockname(tmpso->s, (struct sockaddr *)&addr, | |
645 | &addrlen) == 0) | |
646 | n2 = addr.sin_port; | |
647 | break; | |
648 | } | |
649 | } | |
650 | NTOHS(n1); | |
651 | NTOHS(n2); | |
652 | m_inc(m, snprintf(NULL, 0, "%d,%d\r\n", n1, n2) + 1); | |
653 | m->m_len = snprintf(m->m_data, M_ROOM(m), "%d,%d\r\n", n1, n2); | |
654 | assert(m->m_len < M_ROOM(m)); | |
655 | } else { | |
656 | *eol = '\r'; | |
657 | } | |
658 | ||
659 | return 1; | |
660 | } | |
661 | ||
662 | case EMU_FTP: /* ftp */ | |
663 | m_inc(m, m->m_len + 1); | |
664 | *(m->m_data + m->m_len) = 0; /* NUL terminate for strstr */ | |
665 | if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) { | |
666 | /* | |
667 | * Need to emulate the PORT command | |
668 | */ | |
669 | x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]", &n1, &n2, | |
670 | &n3, &n4, &n5, &n6, buff); | |
671 | if (x < 6) | |
672 | return 1; | |
673 | ||
674 | laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); | |
675 | lport = htons((n5 << 8) | (n6)); | |
676 | ||
677 | if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr, lport, | |
678 | SS_FACCEPTONCE)) == NULL) { | |
679 | return 1; | |
680 | } | |
681 | n6 = ntohs(so->so_fport); | |
682 | ||
683 | n5 = (n6 >> 8) & 0xff; | |
684 | n6 &= 0xff; | |
685 | ||
686 | laddr = ntohl(so->so_faddr.s_addr); | |
687 | ||
688 | n1 = ((laddr >> 24) & 0xff); | |
689 | n2 = ((laddr >> 16) & 0xff); | |
690 | n3 = ((laddr >> 8) & 0xff); | |
691 | n4 = (laddr & 0xff); | |
692 | ||
693 | m->m_len = bptr - m->m_data; /* Adjust length */ | |
694 | m->m_len += snprintf(bptr, m->m_size - m->m_len, | |
695 | "ORT %d,%d,%d,%d,%d,%d\r\n%s", n1, n2, n3, n4, | |
696 | n5, n6, x == 7 ? buff : ""); | |
697 | return 1; | |
698 | } else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) { | |
699 | /* | |
700 | * Need to emulate the PASV response | |
701 | */ | |
702 | x = sscanf( | |
703 | bptr, | |
704 | "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]", | |
705 | &n1, &n2, &n3, &n4, &n5, &n6, buff); | |
706 | if (x < 6) | |
707 | return 1; | |
708 | ||
709 | laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4)); | |
710 | lport = htons((n5 << 8) | (n6)); | |
711 | ||
712 | if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr, lport, | |
713 | SS_FACCEPTONCE)) == NULL) { | |
714 | return 1; | |
715 | } | |
716 | n6 = ntohs(so->so_fport); | |
717 | ||
718 | n5 = (n6 >> 8) & 0xff; | |
719 | n6 &= 0xff; | |
720 | ||
721 | laddr = ntohl(so->so_faddr.s_addr); | |
722 | ||
723 | n1 = ((laddr >> 24) & 0xff); | |
724 | n2 = ((laddr >> 16) & 0xff); | |
725 | n3 = ((laddr >> 8) & 0xff); | |
726 | n4 = (laddr & 0xff); | |
727 | ||
728 | m->m_len = bptr - m->m_data; /* Adjust length */ | |
729 | m->m_len += | |
730 | snprintf(bptr, m->m_size - m->m_len, | |
731 | "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s", | |
732 | n1, n2, n3, n4, n5, n6, x == 7 ? buff : ""); | |
733 | ||
734 | return 1; | |
735 | } | |
736 | ||
737 | return 1; | |
738 | ||
739 | case EMU_KSH: | |
740 | /* | |
741 | * The kshell (Kerberos rsh) and shell services both pass | |
742 | * a local port port number to carry signals to the server | |
743 | * and stderr to the client. It is passed at the beginning | |
744 | * of the connection as a NUL-terminated decimal ASCII string. | |
745 | */ | |
746 | so->so_emu = 0; | |
747 | for (lport = 0, i = 0; i < m->m_len - 1; ++i) { | |
748 | if (m->m_data[i] < '0' || m->m_data[i] > '9') | |
749 | return 1; /* invalid number */ | |
750 | lport *= 10; | |
751 | lport += m->m_data[i] - '0'; | |
752 | } | |
753 | if (m->m_data[m->m_len - 1] == '\0' && lport != 0 && | |
754 | (so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr, | |
755 | htons(lport), SS_FACCEPTONCE)) != NULL) | |
756 | m->m_len = | |
757 | snprintf(m->m_data, m->m_size, "%d", ntohs(so->so_fport)) + 1; | |
758 | return 1; | |
759 | ||
760 | case EMU_IRC: | |
761 | /* | |
762 | * Need to emulate DCC CHAT, DCC SEND and DCC MOVE | |
763 | */ | |
764 | m_inc(m, m->m_len + 1); | |
765 | *(m->m_data + m->m_len) = 0; /* NULL terminate the string for strstr */ | |
766 | if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL) | |
767 | return 1; | |
768 | ||
769 | /* The %256s is for the broken mIRC */ | |
770 | if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) { | |
771 | if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), | |
772 | htons(lport), SS_FACCEPTONCE)) == NULL) { | |
773 | return 1; | |
774 | } | |
775 | m->m_len = bptr - m->m_data; /* Adjust length */ | |
776 | m->m_len += snprintf(bptr, m->m_size, "DCC CHAT chat %lu %u%c\n", | |
777 | (unsigned long)ntohl(so->so_faddr.s_addr), | |
778 | ntohs(so->so_fport), 1); | |
779 | } else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, | |
780 | &n1) == 4) { | |
781 | if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), | |
782 | htons(lport), SS_FACCEPTONCE)) == NULL) { | |
783 | return 1; | |
784 | } | |
785 | m->m_len = bptr - m->m_data; /* Adjust length */ | |
786 | m->m_len += | |
787 | snprintf(bptr, m->m_size, "DCC SEND %s %lu %u %u%c\n", buff, | |
788 | (unsigned long)ntohl(so->so_faddr.s_addr), | |
789 | ntohs(so->so_fport), n1, 1); | |
790 | } else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, | |
791 | &n1) == 4) { | |
792 | if ((so = tcp_listen(slirp, INADDR_ANY, 0, htonl(laddr), | |
793 | htons(lport), SS_FACCEPTONCE)) == NULL) { | |
794 | return 1; | |
795 | } | |
796 | m->m_len = bptr - m->m_data; /* Adjust length */ | |
797 | m->m_len += | |
798 | snprintf(bptr, m->m_size, "DCC MOVE %s %lu %u %u%c\n", buff, | |
799 | (unsigned long)ntohl(so->so_faddr.s_addr), | |
800 | ntohs(so->so_fport), n1, 1); | |
801 | } | |
802 | return 1; | |
803 | ||
804 | case EMU_REALAUDIO: | |
805 | /* | |
806 | * RealAudio emulation - JP. We must try to parse the incoming | |
807 | * data and try to find the two characters that contain the | |
808 | * port number. Then we redirect an udp port and replace the | |
809 | * number with the real port we got. | |
810 | * | |
811 | * The 1.0 beta versions of the player are not supported | |
812 | * any more. | |
813 | * | |
814 | * A typical packet for player version 1.0 (release version): | |
815 | * | |
816 | * 0000:50 4E 41 00 05 | |
817 | * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 ........g.l.c..P | |
818 | * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH | |
819 | * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v | |
820 | * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB | |
821 | * | |
822 | * Now the port number 0x1BD7 is found at offset 0x04 of the | |
823 | * Now the port number 0x1BD7 is found at offset 0x04 of the | |
824 | * second packet. This time we received five bytes first and | |
825 | * then the rest. You never know how many bytes you get. | |
826 | * | |
827 | * A typical packet for player version 2.0 (beta): | |
828 | * | |
829 | * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA............. | |
830 | * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux.c..Win2.0.0 | |
831 | * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/ | |
832 | * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas | |
833 | * 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B | |
834 | * | |
835 | * Port number 0x1BC1 is found at offset 0x0d. | |
836 | * | |
837 | * This is just a horrible switch statement. Variable ra tells | |
838 | * us where we're going. | |
839 | */ | |
840 | ||
841 | bptr = m->m_data; | |
842 | while (bptr < m->m_data + m->m_len) { | |
843 | uint16_t p; | |
844 | static int ra = 0; | |
845 | char ra_tbl[4]; | |
846 | ||
847 | ra_tbl[0] = 0x50; | |
848 | ra_tbl[1] = 0x4e; | |
849 | ra_tbl[2] = 0x41; | |
850 | ra_tbl[3] = 0; | |
851 | ||
852 | switch (ra) { | |
853 | case 0: | |
854 | case 2: | |
855 | case 3: | |
856 | if (*bptr++ != ra_tbl[ra]) { | |
857 | ra = 0; | |
858 | continue; | |
859 | } | |
860 | break; | |
861 | ||
862 | case 1: | |
863 | /* | |
864 | * We may get 0x50 several times, ignore them | |
865 | */ | |
866 | if (*bptr == 0x50) { | |
867 | ra = 1; | |
868 | bptr++; | |
869 | continue; | |
870 | } else if (*bptr++ != ra_tbl[ra]) { | |
871 | ra = 0; | |
872 | continue; | |
873 | } | |
874 | break; | |
875 | ||
876 | case 4: | |
877 | /* | |
878 | * skip version number | |
879 | */ | |
880 | bptr++; | |
881 | break; | |
882 | ||
883 | case 5: | |
884 | /* | |
885 | * The difference between versions 1.0 and | |
886 | * 2.0 is here. For future versions of | |
887 | * the player this may need to be modified. | |
888 | */ | |
889 | if (*(bptr + 1) == 0x02) | |
890 | bptr += 8; | |
891 | else | |
892 | bptr += 4; | |
893 | break; | |
894 | ||
895 | case 6: | |
896 | /* This is the field containing the port | |
897 | * number that RA-player is listening to. | |
898 | */ | |
899 | lport = (((uint8_t *)bptr)[0] << 8) + ((uint8_t *)bptr)[1]; | |
900 | if (lport < 6970) | |
901 | lport += 256; /* don't know why */ | |
902 | if (lport < 6970 || lport > 7170) | |
903 | return 1; /* failed */ | |
904 | ||
905 | /* try to get udp port between 6970 - 7170 */ | |
906 | for (p = 6970; p < 7071; p++) { | |
907 | if (udp_listen(slirp, INADDR_ANY, htons(p), | |
908 | so->so_laddr.s_addr, htons(lport), | |
909 | SS_FACCEPTONCE)) { | |
910 | break; | |
911 | } | |
912 | } | |
913 | if (p == 7071) | |
914 | p = 0; | |
915 | *(uint8_t *)bptr++ = (p >> 8) & 0xff; | |
916 | *(uint8_t *)bptr = p & 0xff; | |
917 | ra = 0; | |
918 | return 1; /* port redirected, we're done */ | |
919 | break; | |
920 | ||
921 | default: | |
922 | ra = 0; | |
923 | } | |
924 | ra++; | |
925 | } | |
926 | return 1; | |
927 | ||
928 | default: | |
929 | /* Ooops, not emulated, won't call tcp_emu again */ | |
930 | so->so_emu = 0; | |
931 | return 1; | |
932 | } | |
933 | } | |
934 | ||
935 | /* | |
936 | * Do misc. config of SLiRP while its running. | |
937 | * Return 0 if this connections is to be closed, 1 otherwise, | |
938 | * return 2 if this is a command-line connection | |
939 | */ | |
940 | int tcp_ctl(struct socket *so) | |
941 | { | |
942 | Slirp *slirp = so->slirp; | |
943 | struct sbuf *sb = &so->so_snd; | |
944 | struct gfwd_list *ex_ptr; | |
945 | ||
946 | DEBUG_CALL("tcp_ctl"); | |
947 | DEBUG_ARG("so = %p", so); | |
948 | ||
949 | /* TODO: IPv6 */ | |
950 | if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { | |
951 | /* Check if it's pty_exec */ | |
952 | for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { | |
953 | if (ex_ptr->ex_fport == so->so_fport && | |
954 | so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { | |
955 | if (ex_ptr->write_cb) { | |
956 | so->s = -1; | |
957 | so->guestfwd = ex_ptr; | |
958 | return 1; | |
959 | } | |
960 | DEBUG_MISC(" executing %s", ex_ptr->ex_exec); | |
961 | return fork_exec(so, ex_ptr->ex_exec); | |
962 | } | |
963 | } | |
964 | } | |
965 | sb->sb_cc = | |
966 | snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data), | |
967 | "Error: No application configured.\r\n"); | |
968 | sb->sb_wptr += sb->sb_cc; | |
969 | return 0; | |
970 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93 | |
30 | * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp | |
31 | */ | |
32 | ||
33 | #include "slirp.h" | |
34 | ||
35 | static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer); | |
36 | ||
37 | /* | |
38 | * Fast timeout routine for processing delayed acks | |
39 | */ | |
40 | void tcp_fasttimo(Slirp *slirp) | |
41 | { | |
42 | register struct socket *so; | |
43 | register struct tcpcb *tp; | |
44 | ||
45 | DEBUG_CALL("tcp_fasttimo"); | |
46 | ||
47 | so = slirp->tcb.so_next; | |
48 | if (so) | |
49 | for (; so != &slirp->tcb; so = so->so_next) | |
50 | if ((tp = (struct tcpcb *)so->so_tcpcb) && | |
51 | (tp->t_flags & TF_DELACK)) { | |
52 | tp->t_flags &= ~TF_DELACK; | |
53 | tp->t_flags |= TF_ACKNOW; | |
54 | (void)tcp_output(tp); | |
55 | } | |
56 | } | |
57 | ||
58 | /* | |
59 | * Tcp protocol timeout routine called every 500 ms. | |
60 | * Updates the timers in all active tcb's and | |
61 | * causes finite state machine actions if timers expire. | |
62 | */ | |
63 | void tcp_slowtimo(Slirp *slirp) | |
64 | { | |
65 | register struct socket *ip, *ipnxt; | |
66 | register struct tcpcb *tp; | |
67 | register int i; | |
68 | ||
69 | DEBUG_CALL("tcp_slowtimo"); | |
70 | ||
71 | /* | |
72 | * Search through tcb's and update active timers. | |
73 | */ | |
74 | ip = slirp->tcb.so_next; | |
75 | if (ip == NULL) { | |
76 | return; | |
77 | } | |
78 | for (; ip != &slirp->tcb; ip = ipnxt) { | |
79 | ipnxt = ip->so_next; | |
80 | tp = sototcpcb(ip); | |
81 | if (tp == NULL) { | |
82 | continue; | |
83 | } | |
84 | for (i = 0; i < TCPT_NTIMERS; i++) { | |
85 | if (tp->t_timer[i] && --tp->t_timer[i] == 0) { | |
86 | tcp_timers(tp, i); | |
87 | if (ipnxt->so_prev != ip) | |
88 | goto tpgone; | |
89 | } | |
90 | } | |
91 | tp->t_idle++; | |
92 | if (tp->t_rtt) | |
93 | tp->t_rtt++; | |
94 | tpgone:; | |
95 | } | |
96 | slirp->tcp_iss += TCP_ISSINCR / PR_SLOWHZ; /* increment iss */ | |
97 | slirp->tcp_now++; /* for timestamps */ | |
98 | } | |
99 | ||
100 | /* | |
101 | * Cancel all timers for TCP tp. | |
102 | */ | |
103 | void tcp_canceltimers(struct tcpcb *tp) | |
104 | { | |
105 | register int i; | |
106 | ||
107 | for (i = 0; i < TCPT_NTIMERS; i++) | |
108 | tp->t_timer[i] = 0; | |
109 | } | |
110 | ||
111 | const int tcp_backoff[TCP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, | |
112 | 64, 64, 64, 64, 64, 64 }; | |
113 | ||
114 | /* | |
115 | * TCP timer processing. | |
116 | */ | |
117 | static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer) | |
118 | { | |
119 | register int rexmt; | |
120 | ||
121 | DEBUG_CALL("tcp_timers"); | |
122 | ||
123 | switch (timer) { | |
124 | /* | |
125 | * 2 MSL timeout in shutdown went off. If we're closed but | |
126 | * still waiting for peer to close and connection has been idle | |
127 | * too long, or if 2MSL time is up from TIME_WAIT, delete connection | |
128 | * control block. Otherwise, check again in a bit. | |
129 | */ | |
130 | case TCPT_2MSL: | |
131 | if (tp->t_state != TCPS_TIME_WAIT && tp->t_idle <= TCP_MAXIDLE) | |
132 | tp->t_timer[TCPT_2MSL] = TCPTV_KEEPINTVL; | |
133 | else | |
134 | tp = tcp_close(tp); | |
135 | break; | |
136 | ||
137 | /* | |
138 | * Retransmission timer went off. Message has not | |
139 | * been acked within retransmit interval. Back off | |
140 | * to a longer retransmit interval and retransmit one segment. | |
141 | */ | |
142 | case TCPT_REXMT: | |
143 | ||
144 | /* | |
145 | * XXXXX If a packet has timed out, then remove all the queued | |
146 | * packets for that session. | |
147 | */ | |
148 | ||
149 | if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) { | |
150 | /* | |
151 | * This is a hack to suit our terminal server here at the uni of | |
152 | * canberra since they have trouble with zeroes... It usually lets | |
153 | * them through unharmed, but under some conditions, it'll eat the | |
154 | * zeros. If we keep retransmitting it, it'll keep eating the | |
155 | * zeroes, so we keep retransmitting, and eventually the connection | |
156 | * dies... (this only happens on incoming data) | |
157 | * | |
158 | * So, if we were gonna drop the connection from too many | |
159 | * retransmits, don't... instead halve the t_maxseg, which might | |
160 | * break up the NULLs and let them through | |
161 | * | |
162 | * *sigh* | |
163 | */ | |
164 | ||
165 | tp->t_maxseg >>= 1; | |
166 | if (tp->t_maxseg < 32) { | |
167 | /* | |
168 | * We tried our best, now the connection must die! | |
169 | */ | |
170 | tp->t_rxtshift = TCP_MAXRXTSHIFT; | |
171 | tp = tcp_drop(tp, tp->t_softerror); | |
172 | /* tp->t_softerror : ETIMEDOUT); */ /* XXX */ | |
173 | return (tp); /* XXX */ | |
174 | } | |
175 | ||
176 | /* | |
177 | * Set rxtshift to 6, which is still at the maximum | |
178 | * backoff time | |
179 | */ | |
180 | tp->t_rxtshift = 6; | |
181 | } | |
182 | rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift]; | |
183 | TCPT_RANGESET(tp->t_rxtcur, rexmt, (short)tp->t_rttmin, | |
184 | TCPTV_REXMTMAX); /* XXX */ | |
185 | tp->t_timer[TCPT_REXMT] = tp->t_rxtcur; | |
186 | /* | |
187 | * If losing, let the lower level know and try for | |
188 | * a better route. Also, if we backed off this far, | |
189 | * our srtt estimate is probably bogus. Clobber it | |
190 | * so we'll take the next rtt measurement as our srtt; | |
191 | * move the current srtt into rttvar to keep the current | |
192 | * retransmit times until then. | |
193 | */ | |
194 | if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) { | |
195 | tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT); | |
196 | tp->t_srtt = 0; | |
197 | } | |
198 | tp->snd_nxt = tp->snd_una; | |
199 | /* | |
200 | * If timing a segment in this window, stop the timer. | |
201 | */ | |
202 | tp->t_rtt = 0; | |
203 | /* | |
204 | * Close the congestion window down to one segment | |
205 | * (we'll open it by one segment for each ack we get). | |
206 | * Since we probably have a window's worth of unacked | |
207 | * data accumulated, this "slow start" keeps us from | |
208 | * dumping all that data as back-to-back packets (which | |
209 | * might overwhelm an intermediate gateway). | |
210 | * | |
211 | * There are two phases to the opening: Initially we | |
212 | * open by one mss on each ack. This makes the window | |
213 | * size increase exponentially with time. If the | |
214 | * window is larger than the path can handle, this | |
215 | * exponential growth results in dropped packet(s) | |
216 | * almost immediately. To get more time between | |
217 | * drops but still "push" the network to take advantage | |
218 | * of improving conditions, we switch from exponential | |
219 | * to linear window opening at some threshold size. | |
220 | * For a threshold, we use half the current window | |
221 | * size, truncated to a multiple of the mss. | |
222 | * | |
223 | * (the minimum cwnd that will give us exponential | |
224 | * growth is 2 mss. We don't allow the threshold | |
225 | * to go below this.) | |
226 | */ | |
227 | { | |
228 | unsigned win = MIN(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; | |
229 | if (win < 2) | |
230 | win = 2; | |
231 | tp->snd_cwnd = tp->t_maxseg; | |
232 | tp->snd_ssthresh = win * tp->t_maxseg; | |
233 | tp->t_dupacks = 0; | |
234 | } | |
235 | (void)tcp_output(tp); | |
236 | break; | |
237 | ||
238 | /* | |
239 | * Persistence timer into zero window. | |
240 | * Force a byte to be output, if possible. | |
241 | */ | |
242 | case TCPT_PERSIST: | |
243 | tcp_setpersist(tp); | |
244 | tp->t_force = 1; | |
245 | (void)tcp_output(tp); | |
246 | tp->t_force = 0; | |
247 | break; | |
248 | ||
249 | /* | |
250 | * Keep-alive timer went off; send something | |
251 | * or drop connection if idle for too long. | |
252 | */ | |
253 | case TCPT_KEEP: | |
254 | if (tp->t_state < TCPS_ESTABLISHED) | |
255 | goto dropit; | |
256 | ||
257 | if (slirp_do_keepalive && tp->t_state <= TCPS_CLOSE_WAIT) { | |
258 | if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE) | |
259 | goto dropit; | |
260 | /* | |
261 | * Send a packet designed to force a response | |
262 | * if the peer is up and reachable: | |
263 | * either an ACK if the connection is still alive, | |
264 | * or an RST if the peer has closed the connection | |
265 | * due to timeout or reboot. | |
266 | * Using sequence number tp->snd_una-1 | |
267 | * causes the transmitted zero-length segment | |
268 | * to lie outside the receive window; | |
269 | * by the protocol spec, this requires the | |
270 | * correspondent TCP to respond. | |
271 | */ | |
272 | tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL, tp->rcv_nxt, | |
273 | tp->snd_una - 1, 0, tp->t_socket->so_ffamily); | |
274 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; | |
275 | } else | |
276 | tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; | |
277 | break; | |
278 | ||
279 | dropit: | |
280 | tp = tcp_drop(tp, 0); | |
281 | break; | |
282 | } | |
283 | ||
284 | return (tp); | |
285 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93 | |
30 | * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef TCP_TIMER_H | |
34 | #define TCP_TIMER_H | |
35 | ||
36 | /* | |
37 | * Definitions of the TCP timers. These timers are counted | |
38 | * down PR_SLOWHZ times a second. | |
39 | */ | |
40 | #define TCPT_NTIMERS 4 | |
41 | ||
42 | #define TCPT_REXMT 0 /* retransmit */ | |
43 | #define TCPT_PERSIST 1 /* retransmit persistence */ | |
44 | #define TCPT_KEEP 2 /* keep alive */ | |
45 | #define TCPT_2MSL 3 /* 2*msl quiet time timer */ | |
46 | ||
47 | /* | |
48 | * The TCPT_REXMT timer is used to force retransmissions. | |
49 | * The TCP has the TCPT_REXMT timer set whenever segments | |
50 | * have been sent for which ACKs are expected but not yet | |
51 | * received. If an ACK is received which advances tp->snd_una, | |
52 | * then the retransmit timer is cleared (if there are no more | |
53 | * outstanding segments) or reset to the base value (if there | |
54 | * are more ACKs expected). Whenever the retransmit timer goes off, | |
55 | * we retransmit one unacknowledged segment, and do a backoff | |
56 | * on the retransmit timer. | |
57 | * | |
58 | * The TCPT_PERSIST timer is used to keep window size information | |
59 | * flowing even if the window goes shut. If all previous transmissions | |
60 | * have been acknowledged (so that there are no retransmissions in progress), | |
61 | * and the window is too small to bother sending anything, then we start | |
62 | * the TCPT_PERSIST timer. When it expires, if the window is nonzero, | |
63 | * we go to transmit state. Otherwise, at intervals send a single byte | |
64 | * into the peer's window to force him to update our window information. | |
65 | * We do this at most as often as TCPT_PERSMIN time intervals, | |
66 | * but no more frequently than the current estimate of round-trip | |
67 | * packet time. The TCPT_PERSIST timer is cleared whenever we receive | |
68 | * a window update from the peer. | |
69 | * | |
70 | * The TCPT_KEEP timer is used to keep connections alive. If an | |
71 | * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time, | |
72 | * but not yet established, then we drop the connection. Once the connection | |
73 | * is established, if the connection is idle for TCPTV_KEEP_IDLE time | |
74 | * (and keepalives have been enabled on the socket), we begin to probe | |
75 | * the connection. We force the peer to send us a segment by sending: | |
76 | * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK> | |
77 | * This segment is (deliberately) outside the window, and should elicit | |
78 | * an ack segment in response from the peer. If, despite the TCPT_KEEP | |
79 | * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE | |
80 | * amount of time probing, then we drop the connection. | |
81 | */ | |
82 | ||
83 | /* | |
84 | * Time constants. | |
85 | */ | |
86 | #define TCPTV_MSL (5 * PR_SLOWHZ) /* max seg lifetime (hah!) */ | |
87 | ||
88 | #define TCPTV_SRTTBASE \ | |
89 | 0 /* base roundtrip time; \ | |
90 | if 0, no idea yet */ | |
91 | #define TCPTV_SRTTDFLT (3 * PR_SLOWHZ) /* assumed RTT if no info */ | |
92 | ||
93 | #define TCPTV_PERSMIN (5 * PR_SLOWHZ) /* retransmit persistence */ | |
94 | #define TCPTV_PERSMAX (60 * PR_SLOWHZ) /* maximum persist interval */ | |
95 | ||
96 | #define TCPTV_KEEP_INIT (75 * PR_SLOWHZ) /* initial connect keep alive */ | |
97 | #define TCPTV_KEEP_IDLE (120 * 60 * PR_SLOWHZ) /* dflt time before probing */ | |
98 | #define TCPTV_KEEPINTVL (75 * PR_SLOWHZ) /* default probe interval */ | |
99 | #define TCPTV_KEEPCNT 8 /* max probes before drop */ | |
100 | ||
101 | #define TCPTV_MIN (1 * PR_SLOWHZ) /* minimum allowable value */ | |
102 | #define TCPTV_REXMTMAX (12 * PR_SLOWHZ) /* max allowable REXMT value */ | |
103 | ||
104 | #define TCP_LINGERTIME 120 /* linger at most 2 minutes */ | |
105 | ||
106 | #define TCP_MAXRXTSHIFT 12 /* maximum retransmits */ | |
107 | ||
108 | ||
109 | /* | |
110 | * Force a time value to be in a certain range. | |
111 | */ | |
112 | #define TCPT_RANGESET(tv, value, tvmin, tvmax) \ | |
113 | { \ | |
114 | (tv) = (value); \ | |
115 | if ((tv) < (tvmin)) \ | |
116 | (tv) = (tvmin); \ | |
117 | else if ((tv) > (tvmax)) \ | |
118 | (tv) = (tvmax); \ | |
119 | } | |
120 | ||
121 | extern const int tcp_backoff[]; | |
122 | ||
123 | struct tcpcb; | |
124 | ||
125 | void tcp_fasttimo(Slirp *); | |
126 | void tcp_slowtimo(Slirp *); | |
127 | void tcp_canceltimers(struct tcpcb *); | |
128 | ||
129 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94 | |
30 | * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef TCP_VAR_H | |
34 | #define TCP_VAR_H | |
35 | ||
36 | #include "tcpip.h" | |
37 | #include "tcp_timer.h" | |
38 | ||
39 | /* | |
40 | * Tcp control block, one per tcp; fields: | |
41 | */ | |
42 | struct tcpcb { | |
43 | struct tcpiphdr *seg_next; /* sequencing queue */ | |
44 | struct tcpiphdr *seg_prev; | |
45 | short t_state; /* state of this connection */ | |
46 | short t_timer[TCPT_NTIMERS]; /* tcp timers */ | |
47 | short t_rxtshift; /* log(2) of rexmt exp. backoff */ | |
48 | short t_rxtcur; /* current retransmit value */ | |
49 | short t_dupacks; /* consecutive dup acks recd */ | |
50 | uint16_t t_maxseg; /* maximum segment size */ | |
51 | uint8_t t_force; /* 1 if forcing out a byte */ | |
52 | uint16_t t_flags; | |
53 | #define TF_ACKNOW 0x0001 /* ack peer immediately */ | |
54 | #define TF_DELACK 0x0002 /* ack, but try to delay it */ | |
55 | #define TF_NODELAY 0x0004 /* don't delay packets to coalesce */ | |
56 | #define TF_NOOPT 0x0008 /* don't use tcp options */ | |
57 | #define TF_SENTFIN 0x0010 /* have sent FIN */ | |
58 | #define TF_REQ_SCALE 0x0020 /* have/will request window scaling */ | |
59 | #define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */ | |
60 | #define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */ | |
61 | #define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */ | |
62 | #define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */ | |
63 | ||
64 | struct tcpiphdr t_template; /* static skeletal packet for xmit */ | |
65 | ||
66 | struct socket *t_socket; /* back pointer to socket */ | |
67 | /* | |
68 | * The following fields are used as in the protocol specification. | |
69 | * See RFC783, Dec. 1981, page 21. | |
70 | */ | |
71 | /* send sequence variables */ | |
72 | tcp_seq snd_una; /* send unacknowledged */ | |
73 | tcp_seq snd_nxt; /* send next */ | |
74 | tcp_seq snd_up; /* send urgent pointer */ | |
75 | tcp_seq snd_wl1; /* window update seg seq number */ | |
76 | tcp_seq snd_wl2; /* window update seg ack number */ | |
77 | tcp_seq iss; /* initial send sequence number */ | |
78 | uint32_t snd_wnd; /* send window */ | |
79 | /* receive sequence variables */ | |
80 | uint32_t rcv_wnd; /* receive window */ | |
81 | tcp_seq rcv_nxt; /* receive next */ | |
82 | tcp_seq rcv_up; /* receive urgent pointer */ | |
83 | tcp_seq irs; /* initial receive sequence number */ | |
84 | /* | |
85 | * Additional variables for this implementation. | |
86 | */ | |
87 | /* receive variables */ | |
88 | tcp_seq rcv_adv; /* advertised window */ | |
89 | /* retransmit variables */ | |
90 | tcp_seq snd_max; /* highest sequence number sent; | |
91 | * used to recognize retransmits | |
92 | */ | |
93 | /* congestion control (for slow start, source quench, retransmit after loss) | |
94 | */ | |
95 | uint32_t snd_cwnd; /* congestion-controlled window */ | |
96 | uint32_t snd_ssthresh; /* snd_cwnd size threshold for | |
97 | * for slow start exponential to | |
98 | * linear switch | |
99 | */ | |
100 | /* | |
101 | * transmit timing stuff. See below for scale of srtt and rttvar. | |
102 | * "Variance" is actually smoothed difference. | |
103 | */ | |
104 | short t_idle; /* inactivity time */ | |
105 | short t_rtt; /* round trip time */ | |
106 | tcp_seq t_rtseq; /* sequence number being timed */ | |
107 | short t_srtt; /* smoothed round-trip time */ | |
108 | short t_rttvar; /* variance in round-trip time */ | |
109 | uint16_t t_rttmin; /* minimum rtt allowed */ | |
110 | uint32_t max_sndwnd; /* largest window peer has offered */ | |
111 | ||
112 | /* out-of-band data */ | |
113 | uint8_t t_oobflags; /* have some */ | |
114 | uint8_t t_iobc; /* input character */ | |
115 | #define TCPOOB_HAVEDATA 0x01 | |
116 | #define TCPOOB_HADDATA 0x02 | |
117 | short t_softerror; /* possible error not yet reported */ | |
118 | ||
119 | /* RFC 1323 variables */ | |
120 | uint8_t snd_scale; /* window scaling for send window */ | |
121 | uint8_t rcv_scale; /* window scaling for recv window */ | |
122 | uint8_t request_r_scale; /* pending window scaling */ | |
123 | uint8_t requested_s_scale; | |
124 | uint32_t ts_recent; /* timestamp echo data */ | |
125 | uint32_t ts_recent_age; /* when last updated */ | |
126 | tcp_seq last_ack_sent; | |
127 | }; | |
128 | ||
129 | #define sototcpcb(so) ((so)->so_tcpcb) | |
130 | ||
131 | /* | |
132 | * The smoothed round-trip time and estimated variance | |
133 | * are stored as fixed point numbers scaled by the values below. | |
134 | * For convenience, these scales are also used in smoothing the average | |
135 | * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed). | |
136 | * With these scales, srtt has 3 bits to the right of the binary point, | |
137 | * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the | |
138 | * binary point, and is smoothed with an ALPHA of 0.75. | |
139 | */ | |
140 | #define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */ | |
141 | #define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */ | |
142 | #define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */ | |
143 | #define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */ | |
144 | ||
145 | /* | |
146 | * The initial retransmission should happen at rtt + 4 * rttvar. | |
147 | * Because of the way we do the smoothing, srtt and rttvar | |
148 | * will each average +1/2 tick of bias. When we compute | |
149 | * the retransmit timer, we want 1/2 tick of rounding and | |
150 | * 1 extra tick because of +-1/2 tick uncertainty in the | |
151 | * firing of the timer. The bias will give us exactly the | |
152 | * 1.5 tick we need. But, because the bias is | |
153 | * statistical, we have to test that we don't drop below | |
154 | * the minimum feasible timer (which is 2 ticks). | |
155 | * This macro assumes that the value of TCP_RTTVAR_SCALE | |
156 | * is the same as the multiplier for rttvar. | |
157 | */ | |
158 | #define TCP_REXMTVAL(tp) (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar) | |
159 | ||
160 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)tcpip.h 8.1 (Berkeley) 6/10/93 | |
30 | * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef TCPIP_H | |
34 | #define TCPIP_H | |
35 | ||
36 | /* | |
37 | * Tcp+ip header, after ip options removed. | |
38 | */ | |
39 | struct tcpiphdr { | |
40 | struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ | |
41 | union { | |
42 | struct { | |
43 | struct in_addr ih_src; /* source internet address */ | |
44 | struct in_addr ih_dst; /* destination internet address */ | |
45 | uint8_t ih_x1; /* (unused) */ | |
46 | uint8_t ih_pr; /* protocol */ | |
47 | } ti_i4; | |
48 | struct { | |
49 | struct in6_addr ih_src; | |
50 | struct in6_addr ih_dst; | |
51 | uint8_t ih_x1; | |
52 | uint8_t ih_nh; | |
53 | } ti_i6; | |
54 | } ti; | |
55 | uint16_t ti_x0; | |
56 | uint16_t ti_len; /* protocol length */ | |
57 | struct tcphdr ti_t; /* tcp header */ | |
58 | }; | |
59 | #define ti_mbuf ih_mbuf.mptr | |
60 | #define ti_pr ti.ti_i4.ih_pr | |
61 | #define ti_src ti.ti_i4.ih_src | |
62 | #define ti_dst ti.ti_i4.ih_dst | |
63 | #define ti_src6 ti.ti_i6.ih_src | |
64 | #define ti_dst6 ti.ti_i6.ih_dst | |
65 | #define ti_nh6 ti.ti_i6.ih_nh | |
66 | #define ti_sport ti_t.th_sport | |
67 | #define ti_dport ti_t.th_dport | |
68 | #define ti_seq ti_t.th_seq | |
69 | #define ti_ack ti_t.th_ack | |
70 | #define ti_x2 ti_t.th_x2 | |
71 | #define ti_off ti_t.th_off | |
72 | #define ti_flags ti_t.th_flags | |
73 | #define ti_win ti_t.th_win | |
74 | #define ti_sum ti_t.th_sum | |
75 | #define ti_urp ti_t.th_urp | |
76 | ||
77 | #define tcpiphdr2qlink(T) \ | |
78 | ((struct qlink *)(((char *)(T)) - sizeof(struct qlink))) | |
79 | #define qlink2tcpiphdr(Q) \ | |
80 | ((struct tcpiphdr *)(((char *)(Q)) + sizeof(struct qlink))) | |
81 | #define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next) | |
82 | #define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev) | |
83 | #define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next) | |
84 | #define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink *)(T)) | |
85 | #define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr *)(T)) | |
86 | ||
87 | /* This is the difference between the size of a tcpiphdr structure, and the | |
88 | * size of actual ip+tcp headers, rounded up since we need to align data. */ | |
89 | #define TCPIPHDR_DELTA \ | |
90 | (MAX(0, (sizeof(struct tcpiphdr) - sizeof(struct ip) - \ | |
91 | sizeof(struct tcphdr) + 3) & \ | |
92 | ~3)) | |
93 | ||
94 | /* | |
95 | * Just a clean way to get to the first byte | |
96 | * of the packet | |
97 | */ | |
98 | struct tcpiphdr_2 { | |
99 | struct tcpiphdr dummy; | |
100 | char first_char; | |
101 | }; | |
102 | ||
103 | #endif |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * tftp.c - a simple, read-only tftp server for qemu | |
3 | * | |
4 | * Copyright (c) 2004 Magnus Damm <damm@opensource.se> | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "slirp.h" | |
26 | ||
27 | #include <sys/types.h> | |
28 | #include <sys/stat.h> | |
29 | #include <fcntl.h> | |
30 | ||
31 | static inline int tftp_session_in_use(struct tftp_session *spt) | |
32 | { | |
33 | return (spt->slirp != NULL); | |
34 | } | |
35 | ||
36 | static inline void tftp_session_update(struct tftp_session *spt) | |
37 | { | |
38 | spt->timestamp = curtime; | |
39 | } | |
40 | ||
41 | static void tftp_session_terminate(struct tftp_session *spt) | |
42 | { | |
43 | if (spt->fd >= 0) { | |
44 | close(spt->fd); | |
45 | spt->fd = -1; | |
46 | } | |
47 | g_free(spt->filename); | |
48 | spt->slirp = NULL; | |
49 | } | |
50 | ||
51 | static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas, | |
52 | struct tftp_t *tp) | |
53 | { | |
54 | struct tftp_session *spt; | |
55 | int k; | |
56 | ||
57 | for (k = 0; k < TFTP_SESSIONS_MAX; k++) { | |
58 | spt = &slirp->tftp_sessions[k]; | |
59 | ||
60 | if (!tftp_session_in_use(spt)) | |
61 | goto found; | |
62 | ||
63 | /* sessions time out after 5 inactive seconds */ | |
64 | if ((int)(curtime - spt->timestamp) > 5000) { | |
65 | tftp_session_terminate(spt); | |
66 | goto found; | |
67 | } | |
68 | } | |
69 | ||
70 | return -1; | |
71 | ||
72 | found: | |
73 | memset(spt, 0, sizeof(*spt)); | |
74 | memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas)); | |
75 | spt->fd = -1; | |
76 | spt->block_size = 512; | |
77 | spt->client_port = tp->udp.uh_sport; | |
78 | spt->slirp = slirp; | |
79 | ||
80 | tftp_session_update(spt); | |
81 | ||
82 | return k; | |
83 | } | |
84 | ||
85 | static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas, | |
86 | struct tftp_t *tp) | |
87 | { | |
88 | struct tftp_session *spt; | |
89 | int k; | |
90 | ||
91 | for (k = 0; k < TFTP_SESSIONS_MAX; k++) { | |
92 | spt = &slirp->tftp_sessions[k]; | |
93 | ||
94 | if (tftp_session_in_use(spt)) { | |
95 | if (sockaddr_equal(&spt->client_addr, srcsas)) { | |
96 | if (spt->client_port == tp->udp.uh_sport) { | |
97 | return k; | |
98 | } | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | return -1; | |
104 | } | |
105 | ||
106 | static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr, | |
107 | uint8_t *buf, int len) | |
108 | { | |
109 | int bytes_read = 0; | |
110 | ||
111 | if (spt->fd < 0) { | |
112 | spt->fd = open(spt->filename, O_RDONLY | O_BINARY); | |
113 | } | |
114 | ||
115 | if (spt->fd < 0) { | |
116 | return -1; | |
117 | } | |
118 | ||
119 | if (len) { | |
120 | lseek(spt->fd, block_nr * spt->block_size, SEEK_SET); | |
121 | ||
122 | bytes_read = read(spt->fd, buf, len); | |
123 | } | |
124 | ||
125 | return bytes_read; | |
126 | } | |
127 | ||
128 | static struct tftp_t *tftp_prep_mbuf_data(struct tftp_session *spt, | |
129 | struct mbuf *m) | |
130 | { | |
131 | struct tftp_t *tp; | |
132 | ||
133 | memset(m->m_data, 0, m->m_size); | |
134 | ||
135 | m->m_data += IF_MAXLINKHDR; | |
136 | if (spt->client_addr.ss_family == AF_INET6) { | |
137 | m->m_data += sizeof(struct ip6); | |
138 | } else { | |
139 | m->m_data += sizeof(struct ip); | |
140 | } | |
141 | tp = (void *)m->m_data; | |
142 | m->m_data += sizeof(struct udphdr); | |
143 | ||
144 | return tp; | |
145 | } | |
146 | ||
147 | static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m, | |
148 | struct tftp_t *recv_tp) | |
149 | { | |
150 | if (spt->client_addr.ss_family == AF_INET6) { | |
151 | struct sockaddr_in6 sa6, da6; | |
152 | ||
153 | sa6.sin6_addr = spt->slirp->vhost_addr6; | |
154 | sa6.sin6_port = recv_tp->udp.uh_dport; | |
155 | da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr; | |
156 | da6.sin6_port = spt->client_port; | |
157 | ||
158 | udp6_output(NULL, m, &sa6, &da6); | |
159 | } else { | |
160 | struct sockaddr_in sa4, da4; | |
161 | ||
162 | sa4.sin_addr = spt->slirp->vhost_addr; | |
163 | sa4.sin_port = recv_tp->udp.uh_dport; | |
164 | da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr; | |
165 | da4.sin_port = spt->client_port; | |
166 | ||
167 | udp_output(NULL, m, &sa4, &da4, IPTOS_LOWDELAY); | |
168 | } | |
169 | } | |
170 | ||
171 | static int tftp_send_oack(struct tftp_session *spt, const char *keys[], | |
172 | uint32_t values[], int nb, struct tftp_t *recv_tp) | |
173 | { | |
174 | struct mbuf *m; | |
175 | struct tftp_t *tp; | |
176 | int i, n = 0; | |
177 | ||
178 | m = m_get(spt->slirp); | |
179 | ||
180 | if (!m) | |
181 | return -1; | |
182 | ||
183 | tp = tftp_prep_mbuf_data(spt, m); | |
184 | ||
185 | tp->tp_op = htons(TFTP_OACK); | |
186 | for (i = 0; i < nb; i++) { | |
187 | n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", | |
188 | keys[i]) + | |
189 | 1; | |
190 | n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", | |
191 | values[i]) + | |
192 | 1; | |
193 | } | |
194 | ||
195 | m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + n - | |
196 | sizeof(struct udphdr); | |
197 | tftp_udp_output(spt, m, recv_tp); | |
198 | ||
199 | return 0; | |
200 | } | |
201 | ||
202 | static void tftp_send_error(struct tftp_session *spt, uint16_t errorcode, | |
203 | const char *msg, struct tftp_t *recv_tp) | |
204 | { | |
205 | struct mbuf *m; | |
206 | struct tftp_t *tp; | |
207 | ||
208 | DEBUG_TFTP("tftp error msg: %s", msg); | |
209 | ||
210 | m = m_get(spt->slirp); | |
211 | ||
212 | if (!m) { | |
213 | goto out; | |
214 | } | |
215 | ||
216 | tp = tftp_prep_mbuf_data(spt, m); | |
217 | ||
218 | tp->tp_op = htons(TFTP_ERROR); | |
219 | tp->x.tp_error.tp_error_code = htons(errorcode); | |
220 | slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), | |
221 | msg); | |
222 | ||
223 | m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 + | |
224 | strlen(msg) - sizeof(struct udphdr); | |
225 | tftp_udp_output(spt, m, recv_tp); | |
226 | ||
227 | out: | |
228 | tftp_session_terminate(spt); | |
229 | } | |
230 | ||
231 | static void tftp_send_next_block(struct tftp_session *spt, | |
232 | struct tftp_t *recv_tp) | |
233 | { | |
234 | struct mbuf *m; | |
235 | struct tftp_t *tp; | |
236 | int nobytes; | |
237 | ||
238 | m = m_get(spt->slirp); | |
239 | ||
240 | if (!m) { | |
241 | return; | |
242 | } | |
243 | ||
244 | tp = tftp_prep_mbuf_data(spt, m); | |
245 | ||
246 | tp->tp_op = htons(TFTP_DATA); | |
247 | tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff); | |
248 | ||
249 | nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, | |
250 | spt->block_size); | |
251 | ||
252 | if (nobytes < 0) { | |
253 | m_free(m); | |
254 | ||
255 | /* send "file not found" error back */ | |
256 | ||
257 | tftp_send_error(spt, 1, "File not found", tp); | |
258 | ||
259 | return; | |
260 | } | |
261 | ||
262 | m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes) - | |
263 | sizeof(struct udphdr); | |
264 | tftp_udp_output(spt, m, recv_tp); | |
265 | ||
266 | if (nobytes == spt->block_size) { | |
267 | tftp_session_update(spt); | |
268 | } else { | |
269 | tftp_session_terminate(spt); | |
270 | } | |
271 | ||
272 | spt->block_nr++; | |
273 | } | |
274 | ||
275 | static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, | |
276 | struct tftp_t *tp, int pktlen) | |
277 | { | |
278 | struct tftp_session *spt; | |
279 | int s, k; | |
280 | size_t prefix_len; | |
281 | char *req_fname; | |
282 | const char *option_name[2]; | |
283 | uint32_t option_value[2]; | |
284 | int nb_options = 0; | |
285 | ||
286 | /* check if a session already exists and if so terminate it */ | |
287 | s = tftp_session_find(slirp, srcsas, tp); | |
288 | if (s >= 0) { | |
289 | tftp_session_terminate(&slirp->tftp_sessions[s]); | |
290 | } | |
291 | ||
292 | s = tftp_session_allocate(slirp, srcsas, tp); | |
293 | ||
294 | if (s < 0) { | |
295 | return; | |
296 | } | |
297 | ||
298 | spt = &slirp->tftp_sessions[s]; | |
299 | ||
300 | /* unspecified prefix means service disabled */ | |
301 | if (!slirp->tftp_prefix) { | |
302 | tftp_send_error(spt, 2, "Access violation", tp); | |
303 | return; | |
304 | } | |
305 | ||
306 | /* skip header fields */ | |
307 | k = 0; | |
308 | pktlen -= offsetof(struct tftp_t, x.tp_buf); | |
309 | ||
310 | /* prepend tftp_prefix */ | |
311 | prefix_len = strlen(slirp->tftp_prefix); | |
312 | spt->filename = g_malloc(prefix_len + TFTP_FILENAME_MAX + 2); | |
313 | memcpy(spt->filename, slirp->tftp_prefix, prefix_len); | |
314 | spt->filename[prefix_len] = '/'; | |
315 | ||
316 | /* get name */ | |
317 | req_fname = spt->filename + prefix_len + 1; | |
318 | ||
319 | while (1) { | |
320 | if (k >= TFTP_FILENAME_MAX || k >= pktlen) { | |
321 | tftp_send_error(spt, 2, "Access violation", tp); | |
322 | return; | |
323 | } | |
324 | req_fname[k] = tp->x.tp_buf[k]; | |
325 | if (req_fname[k++] == '\0') { | |
326 | break; | |
327 | } | |
328 | } | |
329 | ||
330 | DEBUG_TFTP("tftp rrq file: %s", req_fname); | |
331 | ||
332 | /* check mode */ | |
333 | if ((pktlen - k) < 6) { | |
334 | tftp_send_error(spt, 2, "Access violation", tp); | |
335 | return; | |
336 | } | |
337 | ||
338 | if (strcasecmp(&tp->x.tp_buf[k], "octet") != 0) { | |
339 | tftp_send_error(spt, 4, "Unsupported transfer mode", tp); | |
340 | return; | |
341 | } | |
342 | ||
343 | k += 6; /* skipping octet */ | |
344 | ||
345 | /* do sanity checks on the filename */ | |
346 | if (!strncmp(req_fname, "../", 3) || | |
347 | req_fname[strlen(req_fname) - 1] == '/' || strstr(req_fname, "/../")) { | |
348 | tftp_send_error(spt, 2, "Access violation", tp); | |
349 | return; | |
350 | } | |
351 | ||
352 | /* check if the file exists */ | |
353 | if (tftp_read_data(spt, 0, NULL, 0) < 0) { | |
354 | tftp_send_error(spt, 1, "File not found", tp); | |
355 | return; | |
356 | } | |
357 | ||
358 | if (tp->x.tp_buf[pktlen - 1] != 0) { | |
359 | tftp_send_error(spt, 2, "Access violation", tp); | |
360 | return; | |
361 | } | |
362 | ||
363 | while (k < pktlen && nb_options < G_N_ELEMENTS(option_name)) { | |
364 | const char *key, *value; | |
365 | ||
366 | key = &tp->x.tp_buf[k]; | |
367 | k += strlen(key) + 1; | |
368 | ||
369 | if (k >= pktlen) { | |
370 | tftp_send_error(spt, 2, "Access violation", tp); | |
371 | return; | |
372 | } | |
373 | ||
374 | value = &tp->x.tp_buf[k]; | |
375 | k += strlen(value) + 1; | |
376 | ||
377 | if (strcasecmp(key, "tsize") == 0) { | |
378 | int tsize = atoi(value); | |
379 | struct stat stat_p; | |
380 | ||
381 | if (tsize == 0) { | |
382 | if (stat(spt->filename, &stat_p) == 0) | |
383 | tsize = stat_p.st_size; | |
384 | else { | |
385 | tftp_send_error(spt, 1, "File not found", tp); | |
386 | return; | |
387 | } | |
388 | } | |
389 | ||
390 | option_name[nb_options] = "tsize"; | |
391 | option_value[nb_options] = tsize; | |
392 | nb_options++; | |
393 | } else if (strcasecmp(key, "blksize") == 0) { | |
394 | int blksize = atoi(value); | |
395 | ||
396 | /* Accept blksize up to our maximum size */ | |
397 | if (blksize > 0) { | |
398 | spt->block_size = MIN(blksize, TFTP_BLOCKSIZE_MAX); | |
399 | option_name[nb_options] = "blksize"; | |
400 | option_value[nb_options] = spt->block_size; | |
401 | nb_options++; | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | if (nb_options > 0) { | |
407 | assert(nb_options <= G_N_ELEMENTS(option_name)); | |
408 | tftp_send_oack(spt, option_name, option_value, nb_options, tp); | |
409 | return; | |
410 | } | |
411 | ||
412 | spt->block_nr = 0; | |
413 | tftp_send_next_block(spt, tp); | |
414 | } | |
415 | ||
416 | static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas, | |
417 | struct tftp_t *tp, int pktlen) | |
418 | { | |
419 | int s; | |
420 | ||
421 | s = tftp_session_find(slirp, srcsas, tp); | |
422 | ||
423 | if (s < 0) { | |
424 | return; | |
425 | } | |
426 | ||
427 | tftp_send_next_block(&slirp->tftp_sessions[s], tp); | |
428 | } | |
429 | ||
430 | static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas, | |
431 | struct tftp_t *tp, int pktlen) | |
432 | { | |
433 | int s; | |
434 | ||
435 | s = tftp_session_find(slirp, srcsas, tp); | |
436 | ||
437 | if (s < 0) { | |
438 | return; | |
439 | } | |
440 | ||
441 | tftp_session_terminate(&slirp->tftp_sessions[s]); | |
442 | } | |
443 | ||
444 | void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m) | |
445 | { | |
446 | struct tftp_t *tp = (struct tftp_t *)m->m_data; | |
447 | ||
448 | switch (ntohs(tp->tp_op)) { | |
449 | case TFTP_RRQ: | |
450 | tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len); | |
451 | break; | |
452 | ||
453 | case TFTP_ACK: | |
454 | tftp_handle_ack(m->slirp, srcsas, tp, m->m_len); | |
455 | break; | |
456 | ||
457 | case TFTP_ERROR: | |
458 | tftp_handle_error(m->slirp, srcsas, tp, m->m_len); | |
459 | break; | |
460 | } | |
461 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* tftp defines */ | |
2 | ||
3 | #ifndef SLIRP_TFTP_H | |
4 | #define SLIRP_TFTP_H | |
5 | ||
6 | #define TFTP_SESSIONS_MAX 20 | |
7 | ||
8 | #define TFTP_SERVER 69 | |
9 | ||
10 | #define TFTP_RRQ 1 | |
11 | #define TFTP_WRQ 2 | |
12 | #define TFTP_DATA 3 | |
13 | #define TFTP_ACK 4 | |
14 | #define TFTP_ERROR 5 | |
15 | #define TFTP_OACK 6 | |
16 | ||
17 | #define TFTP_FILENAME_MAX 512 | |
18 | #define TFTP_BLOCKSIZE_MAX 1428 | |
19 | ||
20 | struct tftp_t { | |
21 | struct udphdr udp; | |
22 | uint16_t tp_op; | |
23 | union { | |
24 | struct { | |
25 | uint16_t tp_block_nr; | |
26 | uint8_t tp_buf[TFTP_BLOCKSIZE_MAX]; | |
27 | } tp_data; | |
28 | struct { | |
29 | uint16_t tp_error_code; | |
30 | uint8_t tp_msg[TFTP_BLOCKSIZE_MAX]; | |
31 | } tp_error; | |
32 | char tp_buf[TFTP_BLOCKSIZE_MAX + 2]; | |
33 | } x; | |
34 | } __attribute__((packed)); | |
35 | ||
36 | struct tftp_session { | |
37 | Slirp *slirp; | |
38 | char *filename; | |
39 | int fd; | |
40 | uint16_t block_size; | |
41 | ||
42 | struct sockaddr_storage client_addr; | |
43 | uint16_t client_port; | |
44 | uint32_t block_nr; | |
45 | ||
46 | int timestamp; | |
47 | }; | |
48 | ||
49 | void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m); | |
50 | ||
51 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1988, 1990, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94 | |
30 | * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp | |
31 | */ | |
32 | ||
33 | /* | |
34 | * Changes and additions relating to SLiRP | |
35 | * Copyright (c) 1995 Danny Gasparovski. | |
36 | * | |
37 | * Please read the file COPYRIGHT for the | |
38 | * terms and conditions of the copyright. | |
39 | */ | |
40 | ||
41 | #include "slirp.h" | |
42 | #include "ip_icmp.h" | |
43 | ||
44 | static uint8_t udp_tos(struct socket *so); | |
45 | ||
46 | void udp_init(Slirp *slirp) | |
47 | { | |
48 | slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb; | |
49 | slirp->udp_last_so = &slirp->udb; | |
50 | } | |
51 | ||
52 | void udp_cleanup(Slirp *slirp) | |
53 | { | |
54 | while (slirp->udb.so_next != &slirp->udb) { | |
55 | udp_detach(slirp->udb.so_next); | |
56 | } | |
57 | } | |
58 | ||
59 | /* m->m_data points at ip packet header | |
60 | * m->m_len length ip packet | |
61 | * ip->ip_len length data (IPDU) | |
62 | */ | |
63 | void udp_input(register struct mbuf *m, int iphlen) | |
64 | { | |
65 | Slirp *slirp = m->slirp; | |
66 | register struct ip *ip; | |
67 | register struct udphdr *uh; | |
68 | int len; | |
69 | struct ip save_ip; | |
70 | struct socket *so; | |
71 | struct sockaddr_storage lhost; | |
72 | struct sockaddr_in *lhost4; | |
73 | ||
74 | DEBUG_CALL("udp_input"); | |
75 | DEBUG_ARG("m = %p", m); | |
76 | DEBUG_ARG("iphlen = %d", iphlen); | |
77 | ||
78 | /* | |
79 | * Strip IP options, if any; should skip this, | |
80 | * make available to user, and use on returned packets, | |
81 | * but we don't yet have a way to check the checksum | |
82 | * with options still present. | |
83 | */ | |
84 | if (iphlen > sizeof(struct ip)) { | |
85 | ip_stripoptions(m, (struct mbuf *)0); | |
86 | iphlen = sizeof(struct ip); | |
87 | } | |
88 | ||
89 | /* | |
90 | * Get IP and UDP header together in first mbuf. | |
91 | */ | |
92 | ip = mtod(m, struct ip *); | |
93 | uh = (struct udphdr *)((char *)ip + iphlen); | |
94 | ||
95 | /* | |
96 | * Make mbuf data length reflect UDP length. | |
97 | * If not enough data to reflect UDP length, drop. | |
98 | */ | |
99 | len = ntohs((uint16_t)uh->uh_ulen); | |
100 | ||
101 | if (ip->ip_len != len) { | |
102 | if (len > ip->ip_len) { | |
103 | goto bad; | |
104 | } | |
105 | m_adj(m, len - ip->ip_len); | |
106 | ip->ip_len = len; | |
107 | } | |
108 | ||
109 | /* | |
110 | * Save a copy of the IP header in case we want restore it | |
111 | * for sending an ICMP error message in response. | |
112 | */ | |
113 | save_ip = *ip; | |
114 | save_ip.ip_len += iphlen; /* tcp_input subtracts this */ | |
115 | ||
116 | /* | |
117 | * Checksum extended UDP header and data. | |
118 | */ | |
119 | if (uh->uh_sum) { | |
120 | memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr)); | |
121 | ((struct ipovly *)ip)->ih_x1 = 0; | |
122 | ((struct ipovly *)ip)->ih_len = uh->uh_ulen; | |
123 | if (cksum(m, len + sizeof(struct ip))) { | |
124 | goto bad; | |
125 | } | |
126 | } | |
127 | ||
128 | lhost.ss_family = AF_INET; | |
129 | lhost4 = (struct sockaddr_in *)&lhost; | |
130 | lhost4->sin_addr = ip->ip_src; | |
131 | lhost4->sin_port = uh->uh_sport; | |
132 | ||
133 | /* | |
134 | * handle DHCP/BOOTP | |
135 | */ | |
136 | if (ntohs(uh->uh_dport) == BOOTP_SERVER && | |
137 | (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr || | |
138 | ip->ip_dst.s_addr == 0xffffffff)) { | |
139 | bootp_input(m); | |
140 | goto bad; | |
141 | } | |
142 | ||
143 | /* | |
144 | * handle TFTP | |
145 | */ | |
146 | if (ntohs(uh->uh_dport) == TFTP_SERVER && | |
147 | ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) { | |
148 | m->m_data += iphlen; | |
149 | m->m_len -= iphlen; | |
150 | tftp_input(&lhost, m); | |
151 | m->m_data -= iphlen; | |
152 | m->m_len += iphlen; | |
153 | goto bad; | |
154 | } | |
155 | ||
156 | if (slirp->restricted) { | |
157 | goto bad; | |
158 | } | |
159 | ||
160 | /* | |
161 | * Locate pcb for datagram. | |
162 | */ | |
163 | so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL); | |
164 | ||
165 | if (so == NULL) { | |
166 | /* | |
167 | * If there's no socket for this packet, | |
168 | * create one | |
169 | */ | |
170 | so = socreate(slirp); | |
171 | if (udp_attach(so, AF_INET) == -1) { | |
172 | DEBUG_MISC(" udp_attach errno = %d-%s", errno, strerror(errno)); | |
173 | sofree(so); | |
174 | goto bad; | |
175 | } | |
176 | ||
177 | /* | |
178 | * Setup fields | |
179 | */ | |
180 | so->so_lfamily = AF_INET; | |
181 | so->so_laddr = ip->ip_src; | |
182 | so->so_lport = uh->uh_sport; | |
183 | ||
184 | if ((so->so_iptos = udp_tos(so)) == 0) | |
185 | so->so_iptos = ip->ip_tos; | |
186 | ||
187 | /* | |
188 | * XXXXX Here, check if it's in udpexec_list, | |
189 | * and if it is, do the fork_exec() etc. | |
190 | */ | |
191 | } | |
192 | ||
193 | so->so_ffamily = AF_INET; | |
194 | so->so_faddr = ip->ip_dst; /* XXX */ | |
195 | so->so_fport = uh->uh_dport; /* XXX */ | |
196 | ||
197 | iphlen += sizeof(struct udphdr); | |
198 | m->m_len -= iphlen; | |
199 | m->m_data += iphlen; | |
200 | ||
201 | /* | |
202 | * Now we sendto() the packet. | |
203 | */ | |
204 | if (sosendto(so, m) == -1) { | |
205 | m->m_len += iphlen; | |
206 | m->m_data -= iphlen; | |
207 | *ip = save_ip; | |
208 | DEBUG_MISC("udp tx errno = %d-%s", errno, strerror(errno)); | |
209 | icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); | |
210 | goto bad; | |
211 | } | |
212 | ||
213 | m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ | |
214 | ||
215 | /* restore the orig mbuf packet */ | |
216 | m->m_len += iphlen; | |
217 | m->m_data -= iphlen; | |
218 | *ip = save_ip; | |
219 | so->so_m = m; /* ICMP backup */ | |
220 | ||
221 | return; | |
222 | bad: | |
223 | m_free(m); | |
224 | } | |
225 | ||
226 | int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, | |
227 | struct sockaddr_in *daddr, int iptos) | |
228 | { | |
229 | register struct udpiphdr *ui; | |
230 | int error = 0; | |
231 | ||
232 | DEBUG_CALL("udp_output"); | |
233 | DEBUG_ARG("so = %p", so); | |
234 | DEBUG_ARG("m = %p", m); | |
235 | DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr)); | |
236 | DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr)); | |
237 | ||
238 | /* | |
239 | * Adjust for header | |
240 | */ | |
241 | m->m_data -= sizeof(struct udpiphdr); | |
242 | m->m_len += sizeof(struct udpiphdr); | |
243 | ||
244 | /* | |
245 | * Fill in mbuf with extended UDP header | |
246 | * and addresses and length put into network format. | |
247 | */ | |
248 | ui = mtod(m, struct udpiphdr *); | |
249 | memset(&ui->ui_i.ih_mbuf, 0, sizeof(struct mbuf_ptr)); | |
250 | ui->ui_x1 = 0; | |
251 | ui->ui_pr = IPPROTO_UDP; | |
252 | ui->ui_len = htons(m->m_len - sizeof(struct ip)); | |
253 | /* XXXXX Check for from-one-location sockets, or from-any-location sockets | |
254 | */ | |
255 | ui->ui_src = saddr->sin_addr; | |
256 | ui->ui_dst = daddr->sin_addr; | |
257 | ui->ui_sport = saddr->sin_port; | |
258 | ui->ui_dport = daddr->sin_port; | |
259 | ui->ui_ulen = ui->ui_len; | |
260 | ||
261 | /* | |
262 | * Stuff checksum and output datagram. | |
263 | */ | |
264 | ui->ui_sum = 0; | |
265 | if ((ui->ui_sum = cksum(m, m->m_len)) == 0) | |
266 | ui->ui_sum = 0xffff; | |
267 | ((struct ip *)ui)->ip_len = m->m_len; | |
268 | ||
269 | ((struct ip *)ui)->ip_ttl = IPDEFTTL; | |
270 | ((struct ip *)ui)->ip_tos = iptos; | |
271 | ||
272 | error = ip_output(so, m); | |
273 | ||
274 | return (error); | |
275 | } | |
276 | ||
277 | int udp_attach(struct socket *so, unsigned short af) | |
278 | { | |
279 | so->s = slirp_socket(af, SOCK_DGRAM, 0); | |
280 | if (so->s != -1) { | |
281 | so->so_expire = curtime + SO_EXPIRE; | |
282 | insque(so, &so->slirp->udb); | |
283 | } | |
284 | return (so->s); | |
285 | } | |
286 | ||
287 | void udp_detach(struct socket *so) | |
288 | { | |
289 | so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); | |
290 | closesocket(so->s); | |
291 | sofree(so); | |
292 | } | |
293 | ||
294 | static const struct tos_t udptos[] = { { 0, 53, IPTOS_LOWDELAY, 0 }, /* DNS */ | |
295 | { 0, 0, 0, 0 } }; | |
296 | ||
297 | static uint8_t udp_tos(struct socket *so) | |
298 | { | |
299 | int i = 0; | |
300 | ||
301 | while (udptos[i].tos) { | |
302 | if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) || | |
303 | (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) { | |
304 | if (so->slirp->enable_emu) | |
305 | so->so_emu = udptos[i].emu; | |
306 | return udptos[i].tos; | |
307 | } | |
308 | i++; | |
309 | } | |
310 | ||
311 | return 0; | |
312 | } | |
313 | ||
314 | struct socket *udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, | |
315 | uint32_t laddr, unsigned lport, int flags) | |
316 | { | |
317 | /* TODO: IPv6 */ | |
318 | struct sockaddr_in addr; | |
319 | struct socket *so; | |
320 | socklen_t addrlen = sizeof(struct sockaddr_in); | |
321 | ||
322 | so = socreate(slirp); | |
323 | so->s = slirp_socket(AF_INET, SOCK_DGRAM, 0); | |
324 | if (so->s < 0) { | |
325 | sofree(so); | |
326 | return NULL; | |
327 | } | |
328 | so->so_expire = curtime + SO_EXPIRE; | |
329 | insque(so, &slirp->udb); | |
330 | ||
331 | addr.sin_family = AF_INET; | |
332 | addr.sin_addr.s_addr = haddr; | |
333 | addr.sin_port = hport; | |
334 | ||
335 | if (bind(so->s, (struct sockaddr *)&addr, addrlen) < 0) { | |
336 | udp_detach(so); | |
337 | return NULL; | |
338 | } | |
339 | slirp_socket_set_fast_reuse(so->s); | |
340 | ||
341 | getsockname(so->s, (struct sockaddr *)&addr, &addrlen); | |
342 | so->fhost.sin = addr; | |
343 | sotranslate_accept(so); | |
344 | so->so_lfamily = AF_INET; | |
345 | so->so_lport = lport; | |
346 | so->so_laddr.s_addr = laddr; | |
347 | if (flags != SS_FACCEPTONCE) | |
348 | so->so_expire = 0; | |
349 | ||
350 | so->so_state &= SS_PERSISTENT_MASK; | |
351 | so->so_state |= SS_ISFCONNECTED | flags; | |
352 | ||
353 | return so; | |
354 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 1982, 1986, 1993 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the University nor the names of its contributors | |
14 | * may be used to endorse or promote products derived from this software | |
15 | * without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
29 | * @(#)udp.h 8.1 (Berkeley) 6/10/93 | |
30 | * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp | |
31 | */ | |
32 | ||
33 | #ifndef UDP_H | |
34 | #define UDP_H | |
35 | ||
36 | #define UDP_TTL 0x60 | |
37 | #define UDP_UDPDATALEN 16192 | |
38 | ||
39 | /* | |
40 | * Udp protocol header. | |
41 | * Per RFC 768, September, 1981. | |
42 | */ | |
43 | struct udphdr { | |
44 | uint16_t uh_sport; /* source port */ | |
45 | uint16_t uh_dport; /* destination port */ | |
46 | int16_t uh_ulen; /* udp length */ | |
47 | uint16_t uh_sum; /* udp checksum */ | |
48 | }; | |
49 | ||
50 | /* | |
51 | * UDP kernel structures and variables. | |
52 | */ | |
53 | struct udpiphdr { | |
54 | struct ipovly ui_i; /* overlaid ip structure */ | |
55 | struct udphdr ui_u; /* udp header */ | |
56 | }; | |
57 | #define ui_mbuf ui_i.ih_mbuf.mptr | |
58 | #define ui_x1 ui_i.ih_x1 | |
59 | #define ui_pr ui_i.ih_pr | |
60 | #define ui_len ui_i.ih_len | |
61 | #define ui_src ui_i.ih_src | |
62 | #define ui_dst ui_i.ih_dst | |
63 | #define ui_sport ui_u.uh_sport | |
64 | #define ui_dport ui_u.uh_dport | |
65 | #define ui_ulen ui_u.uh_ulen | |
66 | #define ui_sum ui_u.uh_sum | |
67 | ||
68 | /* | |
69 | * Names for UDP sysctl objects | |
70 | */ | |
71 | #define UDPCTL_CHECKSUM 1 /* checksum UDP packets */ | |
72 | #define UDPCTL_MAXID 2 | |
73 | ||
74 | struct mbuf; | |
75 | ||
76 | void udp_init(Slirp *); | |
77 | void udp_cleanup(Slirp *); | |
78 | void udp_input(register struct mbuf *, int); | |
79 | int udp_attach(struct socket *, unsigned short af); | |
80 | void udp_detach(struct socket *); | |
81 | struct socket *udp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int); | |
82 | int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, | |
83 | struct sockaddr_in *daddr, int iptos); | |
84 | ||
85 | void udp6_input(register struct mbuf *); | |
86 | int udp6_output(struct socket *so, struct mbuf *m, struct sockaddr_in6 *saddr, | |
87 | struct sockaddr_in6 *daddr); | |
88 | ||
89 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * Copyright (c) 2013 | |
3 | * Guillaume Subiron | |
4 | */ | |
5 | ||
6 | #include "slirp.h" | |
7 | #include "udp.h" | |
8 | #include "dhcpv6.h" | |
9 | ||
10 | void udp6_input(struct mbuf *m) | |
11 | { | |
12 | Slirp *slirp = m->slirp; | |
13 | struct ip6 *ip, save_ip; | |
14 | struct udphdr *uh; | |
15 | int iphlen = sizeof(struct ip6); | |
16 | int len; | |
17 | struct socket *so; | |
18 | struct sockaddr_in6 lhost; | |
19 | ||
20 | DEBUG_CALL("udp6_input"); | |
21 | DEBUG_ARG("m = %p", m); | |
22 | ||
23 | if (slirp->restricted) { | |
24 | goto bad; | |
25 | } | |
26 | ||
27 | ip = mtod(m, struct ip6 *); | |
28 | m->m_len -= iphlen; | |
29 | m->m_data += iphlen; | |
30 | uh = mtod(m, struct udphdr *); | |
31 | m->m_len += iphlen; | |
32 | m->m_data -= iphlen; | |
33 | ||
34 | if (ip6_cksum(m)) { | |
35 | goto bad; | |
36 | } | |
37 | ||
38 | len = ntohs((uint16_t)uh->uh_ulen); | |
39 | ||
40 | /* | |
41 | * Make mbuf data length reflect UDP length. | |
42 | * If not enough data to reflect UDP length, drop. | |
43 | */ | |
44 | if (ntohs(ip->ip_pl) != len) { | |
45 | if (len > ntohs(ip->ip_pl)) { | |
46 | goto bad; | |
47 | } | |
48 | m_adj(m, len - ntohs(ip->ip_pl)); | |
49 | ip->ip_pl = htons(len); | |
50 | } | |
51 | ||
52 | /* | |
53 | * Save a copy of the IP header in case we want restore it | |
54 | * for sending an ICMP error message in response. | |
55 | */ | |
56 | save_ip = *ip; | |
57 | ||
58 | /* Locate pcb for datagram. */ | |
59 | lhost.sin6_family = AF_INET6; | |
60 | lhost.sin6_addr = ip->ip_src; | |
61 | lhost.sin6_port = uh->uh_sport; | |
62 | ||
63 | /* handle DHCPv6 */ | |
64 | if (ntohs(uh->uh_dport) == DHCPV6_SERVER_PORT && | |
65 | (in6_equal(&ip->ip_dst, &slirp->vhost_addr6) || | |
66 | in6_dhcp_multicast(&ip->ip_dst))) { | |
67 | m->m_data += iphlen; | |
68 | m->m_len -= iphlen; | |
69 | dhcpv6_input(&lhost, m); | |
70 | m->m_data -= iphlen; | |
71 | m->m_len += iphlen; | |
72 | goto bad; | |
73 | } | |
74 | ||
75 | /* handle TFTP */ | |
76 | if (ntohs(uh->uh_dport) == TFTP_SERVER && | |
77 | !memcmp(ip->ip_dst.s6_addr, slirp->vhost_addr6.s6_addr, 16)) { | |
78 | m->m_data += iphlen; | |
79 | m->m_len -= iphlen; | |
80 | tftp_input((struct sockaddr_storage *)&lhost, m); | |
81 | m->m_data -= iphlen; | |
82 | m->m_len += iphlen; | |
83 | goto bad; | |
84 | } | |
85 | ||
86 | so = solookup(&slirp->udp_last_so, &slirp->udb, | |
87 | (struct sockaddr_storage *)&lhost, NULL); | |
88 | ||
89 | if (so == NULL) { | |
90 | /* If there's no socket for this packet, create one. */ | |
91 | so = socreate(slirp); | |
92 | if (udp_attach(so, AF_INET6) == -1) { | |
93 | DEBUG_MISC(" udp6_attach errno = %d-%s", errno, strerror(errno)); | |
94 | sofree(so); | |
95 | goto bad; | |
96 | } | |
97 | ||
98 | /* Setup fields */ | |
99 | so->so_lfamily = AF_INET6; | |
100 | so->so_laddr6 = ip->ip_src; | |
101 | so->so_lport6 = uh->uh_sport; | |
102 | } | |
103 | ||
104 | so->so_ffamily = AF_INET6; | |
105 | so->so_faddr6 = ip->ip_dst; /* XXX */ | |
106 | so->so_fport6 = uh->uh_dport; /* XXX */ | |
107 | ||
108 | iphlen += sizeof(struct udphdr); | |
109 | m->m_len -= iphlen; | |
110 | m->m_data += iphlen; | |
111 | ||
112 | /* | |
113 | * Now we sendto() the packet. | |
114 | */ | |
115 | if (sosendto(so, m) == -1) { | |
116 | m->m_len += iphlen; | |
117 | m->m_data -= iphlen; | |
118 | *ip = save_ip; | |
119 | DEBUG_MISC("udp tx errno = %d-%s", errno, strerror(errno)); | |
120 | icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); | |
121 | goto bad; | |
122 | } | |
123 | ||
124 | m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ | |
125 | ||
126 | /* restore the orig mbuf packet */ | |
127 | m->m_len += iphlen; | |
128 | m->m_data -= iphlen; | |
129 | *ip = save_ip; | |
130 | so->so_m = m; | |
131 | ||
132 | return; | |
133 | bad: | |
134 | m_free(m); | |
135 | } | |
136 | ||
137 | int udp6_output(struct socket *so, struct mbuf *m, struct sockaddr_in6 *saddr, | |
138 | struct sockaddr_in6 *daddr) | |
139 | { | |
140 | struct ip6 *ip; | |
141 | struct udphdr *uh; | |
142 | ||
143 | DEBUG_CALL("udp6_output"); | |
144 | DEBUG_ARG("so = %p", so); | |
145 | DEBUG_ARG("m = %p", m); | |
146 | ||
147 | /* adjust for header */ | |
148 | m->m_data -= sizeof(struct udphdr); | |
149 | m->m_len += sizeof(struct udphdr); | |
150 | uh = mtod(m, struct udphdr *); | |
151 | m->m_data -= sizeof(struct ip6); | |
152 | m->m_len += sizeof(struct ip6); | |
153 | ip = mtod(m, struct ip6 *); | |
154 | ||
155 | /* Build IP header */ | |
156 | ip->ip_pl = htons(m->m_len - sizeof(struct ip6)); | |
157 | ip->ip_nh = IPPROTO_UDP; | |
158 | ip->ip_src = saddr->sin6_addr; | |
159 | ip->ip_dst = daddr->sin6_addr; | |
160 | ||
161 | /* Build UDP header */ | |
162 | uh->uh_sport = saddr->sin6_port; | |
163 | uh->uh_dport = daddr->sin6_port; | |
164 | uh->uh_ulen = ip->ip_pl; | |
165 | uh->uh_sum = 0; | |
166 | uh->uh_sum = ip6_cksum(m); | |
167 | if (uh->uh_sum == 0) { | |
168 | uh->uh_sum = 0xffff; | |
169 | } | |
170 | ||
171 | return ip6_output(so, m, 0); | |
172 | } |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * util.c (mostly based on QEMU os-win32.c) | |
3 | * | |
4 | * Copyright (c) 2003-2008 Fabrice Bellard | |
5 | * Copyright (c) 2010-2016 Red Hat, Inc. | |
6 | * | |
7 | * QEMU library functions for win32 which are shared between QEMU and | |
8 | * the QEMU tools. | |
9 | * | |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
16 | * | |
17 | * The above copyright notice and this permission notice shall be included in | |
18 | * all copies or substantial portions of the Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
23 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
26 | * THE SOFTWARE. | |
27 | */ | |
28 | #include "util.h" | |
29 | ||
30 | #include <glib.h> | |
31 | #include <fcntl.h> | |
32 | #include <stdint.h> | |
33 | ||
34 | #if defined(_WIN32) | |
35 | int slirp_inet_aton(const char *cp, struct in_addr *ia) | |
36 | { | |
37 | uint32_t addr = inet_addr(cp); | |
38 | if (addr == 0xffffffff) { | |
39 | return 0; | |
40 | } | |
41 | ia->s_addr = addr; | |
42 | return 1; | |
43 | } | |
44 | #endif | |
45 | ||
46 | void slirp_set_nonblock(int fd) | |
47 | { | |
48 | #ifndef _WIN32 | |
49 | int f; | |
50 | f = fcntl(fd, F_GETFL); | |
51 | assert(f != -1); | |
52 | f = fcntl(fd, F_SETFL, f | O_NONBLOCK); | |
53 | assert(f != -1); | |
54 | #else | |
55 | unsigned long opt = 1; | |
56 | ioctlsocket(fd, FIONBIO, &opt); | |
57 | #endif | |
58 | } | |
59 | ||
60 | static void slirp_set_cloexec(int fd) | |
61 | { | |
62 | #ifndef _WIN32 | |
63 | int f; | |
64 | f = fcntl(fd, F_GETFD); | |
65 | assert(f != -1); | |
66 | f = fcntl(fd, F_SETFD, f | FD_CLOEXEC); | |
67 | assert(f != -1); | |
68 | #endif | |
69 | } | |
70 | ||
71 | /* | |
72 | * Opens a socket with FD_CLOEXEC set | |
73 | */ | |
74 | int slirp_socket(int domain, int type, int protocol) | |
75 | { | |
76 | int ret; | |
77 | ||
78 | #ifdef SOCK_CLOEXEC | |
79 | ret = socket(domain, type | SOCK_CLOEXEC, protocol); | |
80 | if (ret != -1 || errno != EINVAL) { | |
81 | return ret; | |
82 | } | |
83 | #endif | |
84 | ret = socket(domain, type, protocol); | |
85 | if (ret >= 0) { | |
86 | slirp_set_cloexec(ret); | |
87 | } | |
88 | ||
89 | return ret; | |
90 | } | |
91 | ||
92 | #ifdef _WIN32 | |
93 | static int socket_error(void) | |
94 | { | |
95 | switch (WSAGetLastError()) { | |
96 | case 0: | |
97 | return 0; | |
98 | case WSAEINTR: | |
99 | return EINTR; | |
100 | case WSAEINVAL: | |
101 | return EINVAL; | |
102 | case WSA_INVALID_HANDLE: | |
103 | return EBADF; | |
104 | case WSA_NOT_ENOUGH_MEMORY: | |
105 | return ENOMEM; | |
106 | case WSA_INVALID_PARAMETER: | |
107 | return EINVAL; | |
108 | case WSAENAMETOOLONG: | |
109 | return ENAMETOOLONG; | |
110 | case WSAENOTEMPTY: | |
111 | return ENOTEMPTY; | |
112 | case WSAEWOULDBLOCK: | |
113 | /* not using EWOULDBLOCK as we don't want code to have | |
114 | * to check both EWOULDBLOCK and EAGAIN */ | |
115 | return EAGAIN; | |
116 | case WSAEINPROGRESS: | |
117 | return EINPROGRESS; | |
118 | case WSAEALREADY: | |
119 | return EALREADY; | |
120 | case WSAENOTSOCK: | |
121 | return ENOTSOCK; | |
122 | case WSAEDESTADDRREQ: | |
123 | return EDESTADDRREQ; | |
124 | case WSAEMSGSIZE: | |
125 | return EMSGSIZE; | |
126 | case WSAEPROTOTYPE: | |
127 | return EPROTOTYPE; | |
128 | case WSAENOPROTOOPT: | |
129 | return ENOPROTOOPT; | |
130 | case WSAEPROTONOSUPPORT: | |
131 | return EPROTONOSUPPORT; | |
132 | case WSAEOPNOTSUPP: | |
133 | return EOPNOTSUPP; | |
134 | case WSAEAFNOSUPPORT: | |
135 | return EAFNOSUPPORT; | |
136 | case WSAEADDRINUSE: | |
137 | return EADDRINUSE; | |
138 | case WSAEADDRNOTAVAIL: | |
139 | return EADDRNOTAVAIL; | |
140 | case WSAENETDOWN: | |
141 | return ENETDOWN; | |
142 | case WSAENETUNREACH: | |
143 | return ENETUNREACH; | |
144 | case WSAENETRESET: | |
145 | return ENETRESET; | |
146 | case WSAECONNABORTED: | |
147 | return ECONNABORTED; | |
148 | case WSAECONNRESET: | |
149 | return ECONNRESET; | |
150 | case WSAENOBUFS: | |
151 | return ENOBUFS; | |
152 | case WSAEISCONN: | |
153 | return EISCONN; | |
154 | case WSAENOTCONN: | |
155 | return ENOTCONN; | |
156 | case WSAETIMEDOUT: | |
157 | return ETIMEDOUT; | |
158 | case WSAECONNREFUSED: | |
159 | return ECONNREFUSED; | |
160 | case WSAELOOP: | |
161 | return ELOOP; | |
162 | case WSAEHOSTUNREACH: | |
163 | return EHOSTUNREACH; | |
164 | default: | |
165 | return EIO; | |
166 | } | |
167 | } | |
168 | ||
169 | #undef ioctlsocket | |
170 | int slirp_ioctlsocket_wrap(int fd, int req, void *val) | |
171 | { | |
172 | int ret; | |
173 | ret = ioctlsocket(fd, req, val); | |
174 | if (ret < 0) { | |
175 | errno = socket_error(); | |
176 | } | |
177 | return ret; | |
178 | } | |
179 | ||
180 | #undef closesocket | |
181 | int slirp_closesocket_wrap(int fd) | |
182 | { | |
183 | int ret; | |
184 | ret = closesocket(fd); | |
185 | if (ret < 0) { | |
186 | errno = socket_error(); | |
187 | } | |
188 | return ret; | |
189 | } | |
190 | ||
191 | #undef connect | |
192 | int slirp_connect_wrap(int sockfd, const struct sockaddr *addr, int addrlen) | |
193 | { | |
194 | int ret; | |
195 | ret = connect(sockfd, addr, addrlen); | |
196 | if (ret < 0) { | |
197 | errno = socket_error(); | |
198 | } | |
199 | return ret; | |
200 | } | |
201 | ||
202 | #undef listen | |
203 | int slirp_listen_wrap(int sockfd, int backlog) | |
204 | { | |
205 | int ret; | |
206 | ret = listen(sockfd, backlog); | |
207 | if (ret < 0) { | |
208 | errno = socket_error(); | |
209 | } | |
210 | return ret; | |
211 | } | |
212 | ||
213 | #undef bind | |
214 | int slirp_bind_wrap(int sockfd, const struct sockaddr *addr, int addrlen) | |
215 | { | |
216 | int ret; | |
217 | ret = bind(sockfd, addr, addrlen); | |
218 | if (ret < 0) { | |
219 | errno = socket_error(); | |
220 | } | |
221 | return ret; | |
222 | } | |
223 | ||
224 | #undef socket | |
225 | int slirp_socket_wrap(int domain, int type, int protocol) | |
226 | { | |
227 | int ret; | |
228 | ret = socket(domain, type, protocol); | |
229 | if (ret < 0) { | |
230 | errno = socket_error(); | |
231 | } | |
232 | return ret; | |
233 | } | |
234 | ||
235 | #undef accept | |
236 | int slirp_accept_wrap(int sockfd, struct sockaddr *addr, int *addrlen) | |
237 | { | |
238 | int ret; | |
239 | ret = accept(sockfd, addr, addrlen); | |
240 | if (ret < 0) { | |
241 | errno = socket_error(); | |
242 | } | |
243 | return ret; | |
244 | } | |
245 | ||
246 | #undef shutdown | |
247 | int slirp_shutdown_wrap(int sockfd, int how) | |
248 | { | |
249 | int ret; | |
250 | ret = shutdown(sockfd, how); | |
251 | if (ret < 0) { | |
252 | errno = socket_error(); | |
253 | } | |
254 | return ret; | |
255 | } | |
256 | ||
257 | #undef getsockopt | |
258 | int slirp_getsockopt_wrap(int sockfd, int level, int optname, void *optval, | |
259 | int *optlen) | |
260 | { | |
261 | int ret; | |
262 | ret = getsockopt(sockfd, level, optname, optval, optlen); | |
263 | if (ret < 0) { | |
264 | errno = socket_error(); | |
265 | } | |
266 | return ret; | |
267 | } | |
268 | ||
269 | #undef setsockopt | |
270 | int slirp_setsockopt_wrap(int sockfd, int level, int optname, | |
271 | const void *optval, int optlen) | |
272 | { | |
273 | int ret; | |
274 | ret = setsockopt(sockfd, level, optname, optval, optlen); | |
275 | if (ret < 0) { | |
276 | errno = socket_error(); | |
277 | } | |
278 | return ret; | |
279 | } | |
280 | ||
281 | #undef getpeername | |
282 | int slirp_getpeername_wrap(int sockfd, struct sockaddr *addr, int *addrlen) | |
283 | { | |
284 | int ret; | |
285 | ret = getpeername(sockfd, addr, addrlen); | |
286 | if (ret < 0) { | |
287 | errno = socket_error(); | |
288 | } | |
289 | return ret; | |
290 | } | |
291 | ||
292 | #undef getsockname | |
293 | int slirp_getsockname_wrap(int sockfd, struct sockaddr *addr, int *addrlen) | |
294 | { | |
295 | int ret; | |
296 | ret = getsockname(sockfd, addr, addrlen); | |
297 | if (ret < 0) { | |
298 | errno = socket_error(); | |
299 | } | |
300 | return ret; | |
301 | } | |
302 | ||
303 | #undef send | |
304 | ssize_t slirp_send_wrap(int sockfd, const void *buf, size_t len, int flags) | |
305 | { | |
306 | int ret; | |
307 | ret = send(sockfd, buf, len, flags); | |
308 | if (ret < 0) { | |
309 | errno = socket_error(); | |
310 | } | |
311 | return ret; | |
312 | } | |
313 | ||
314 | #undef sendto | |
315 | ssize_t slirp_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, | |
316 | const struct sockaddr *addr, int addrlen) | |
317 | { | |
318 | int ret; | |
319 | ret = sendto(sockfd, buf, len, flags, addr, addrlen); | |
320 | if (ret < 0) { | |
321 | errno = socket_error(); | |
322 | } | |
323 | return ret; | |
324 | } | |
325 | ||
326 | #undef recv | |
327 | ssize_t slirp_recv_wrap(int sockfd, void *buf, size_t len, int flags) | |
328 | { | |
329 | int ret; | |
330 | ret = recv(sockfd, buf, len, flags); | |
331 | if (ret < 0) { | |
332 | errno = socket_error(); | |
333 | } | |
334 | return ret; | |
335 | } | |
336 | ||
337 | #undef recvfrom | |
338 | ssize_t slirp_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, | |
339 | struct sockaddr *addr, int *addrlen) | |
340 | { | |
341 | int ret; | |
342 | ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); | |
343 | if (ret < 0) { | |
344 | errno = socket_error(); | |
345 | } | |
346 | return ret; | |
347 | } | |
348 | #endif /* WIN32 */ | |
349 | ||
350 | void slirp_pstrcpy(char *buf, int buf_size, const char *str) | |
351 | { | |
352 | int c; | |
353 | char *q = buf; | |
354 | ||
355 | if (buf_size <= 0) | |
356 | return; | |
357 | ||
358 | for (;;) { | |
359 | c = *str++; | |
360 | if (c == 0 || q >= buf + buf_size - 1) | |
361 | break; | |
362 | *q++ = c; | |
363 | } | |
364 | *q = '\0'; | |
365 | } |
0 | /* SPDX-License-Identifier: MIT */ | |
1 | /* | |
2 | * Copyright (c) 2003-2008 Fabrice Bellard | |
3 | * Copyright (c) 2010-2019 Red Hat, Inc. | |
4 | * | |
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
6 | * of this software and associated documentation files (the "Software"), to deal | |
7 | * in the Software without restriction, including without limitation the rights | |
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
9 | * copies of the Software, and to permit persons to whom the Software is | |
10 | * furnished to do so, subject to the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice shall be included in | |
13 | * all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
21 | * THE SOFTWARE. | |
22 | */ | |
23 | #ifndef UTIL_H_ | |
24 | #define UTIL_H_ | |
25 | ||
26 | #include <stdlib.h> | |
27 | #include <stdio.h> | |
28 | #include <assert.h> | |
29 | #include <errno.h> | |
30 | #include <unistd.h> | |
31 | #include <sys/types.h> | |
32 | #include <sys/stat.h> | |
33 | #include <unistd.h> | |
34 | #include <inttypes.h> | |
35 | ||
36 | #ifdef _WIN32 | |
37 | #include <winsock2.h> | |
38 | #include <windows.h> | |
39 | #else | |
40 | #include <sys/socket.h> | |
41 | #include <netinet/tcp.h> | |
42 | #include <netinet/in.h> | |
43 | #endif | |
44 | ||
45 | #if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) | |
46 | #define SLIRP_PACKED __attribute__((gcc_struct, packed)) | |
47 | #else | |
48 | #define SLIRP_PACKED __attribute__((packed)) | |
49 | #endif | |
50 | ||
51 | #ifndef DIV_ROUND_UP | |
52 | #define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) | |
53 | #endif | |
54 | ||
55 | #ifndef container_of | |
56 | #define container_of(ptr, type, member) \ | |
57 | __extension__({ \ | |
58 | void *__mptr = (void *)(ptr); \ | |
59 | ((type *)(__mptr - offsetof(type, member))); \ | |
60 | }) | |
61 | #endif | |
62 | ||
63 | #if defined(_WIN32) /* CONFIG_IOVEC */ | |
64 | #if !defined(IOV_MAX) /* XXX: to avoid duplicate with QEMU osdep.h */ | |
65 | struct iovec { | |
66 | void *iov_base; | |
67 | size_t iov_len; | |
68 | }; | |
69 | #endif | |
70 | #else | |
71 | #include <sys/uio.h> | |
72 | #endif | |
73 | ||
74 | #define stringify(s) tostring(s) | |
75 | #define tostring(s) #s | |
76 | ||
77 | #define SCALE_MS 1000000 | |
78 | ||
79 | #define ETH_ALEN 6 | |
80 | #define ETH_HLEN 14 | |
81 | #define ETH_P_IP (0x0800) /* Internet Protocol packet */ | |
82 | #define ETH_P_ARP (0x0806) /* Address Resolution packet */ | |
83 | #define ETH_P_IPV6 (0x86dd) | |
84 | #define ETH_P_VLAN (0x8100) | |
85 | #define ETH_P_DVLAN (0x88a8) | |
86 | #define ETH_P_NCSI (0x88f8) | |
87 | #define ETH_P_UNKNOWN (0xffff) | |
88 | ||
89 | /* FIXME: remove me when made standalone */ | |
90 | #ifdef _WIN32 | |
91 | #undef accept | |
92 | #undef bind | |
93 | #undef closesocket | |
94 | #undef connect | |
95 | #undef getpeername | |
96 | #undef getsockname | |
97 | #undef getsockopt | |
98 | #undef ioctlsocket | |
99 | #undef listen | |
100 | #undef recv | |
101 | #undef recvfrom | |
102 | #undef send | |
103 | #undef sendto | |
104 | #undef setsockopt | |
105 | #undef shutdown | |
106 | #undef socket | |
107 | #endif | |
108 | ||
109 | #ifdef _WIN32 | |
110 | #define connect slirp_connect_wrap | |
111 | int slirp_connect_wrap(int fd, const struct sockaddr *addr, int addrlen); | |
112 | #define listen slirp_listen_wrap | |
113 | int slirp_listen_wrap(int fd, int backlog); | |
114 | #define bind slirp_bind_wrap | |
115 | int slirp_bind_wrap(int fd, const struct sockaddr *addr, int addrlen); | |
116 | #define socket slirp_socket_wrap | |
117 | int slirp_socket_wrap(int domain, int type, int protocol); | |
118 | #define accept slirp_accept_wrap | |
119 | int slirp_accept_wrap(int fd, struct sockaddr *addr, int *addrlen); | |
120 | #define shutdown slirp_shutdown_wrap | |
121 | int slirp_shutdown_wrap(int fd, int how); | |
122 | #define getpeername slirp_getpeername_wrap | |
123 | int slirp_getpeername_wrap(int fd, struct sockaddr *addr, int *addrlen); | |
124 | #define getsockname slirp_getsockname_wrap | |
125 | int slirp_getsockname_wrap(int fd, struct sockaddr *addr, int *addrlen); | |
126 | #define send slirp_send_wrap | |
127 | ssize_t slirp_send_wrap(int fd, const void *buf, size_t len, int flags); | |
128 | #define sendto slirp_sendto_wrap | |
129 | ssize_t slirp_sendto_wrap(int fd, const void *buf, size_t len, int flags, | |
130 | const struct sockaddr *dest_addr, int addrlen); | |
131 | #define recv slirp_recv_wrap | |
132 | ssize_t slirp_recv_wrap(int fd, void *buf, size_t len, int flags); | |
133 | #define recvfrom slirp_recvfrom_wrap | |
134 | ssize_t slirp_recvfrom_wrap(int fd, void *buf, size_t len, int flags, | |
135 | struct sockaddr *src_addr, int *addrlen); | |
136 | #define closesocket slirp_closesocket_wrap | |
137 | int slirp_closesocket_wrap(int fd); | |
138 | #define ioctlsocket slirp_ioctlsocket_wrap | |
139 | int slirp_ioctlsocket_wrap(int fd, int req, void *val); | |
140 | #define getsockopt slirp_getsockopt_wrap | |
141 | int slirp_getsockopt_wrap(int sockfd, int level, int optname, void *optval, | |
142 | int *optlen); | |
143 | #define setsockopt slirp_setsockopt_wrap | |
144 | int slirp_setsockopt_wrap(int sockfd, int level, int optname, | |
145 | const void *optval, int optlen); | |
146 | #define inet_aton slirp_inet_aton | |
147 | int slirp_inet_aton(const char *cp, struct in_addr *ia); | |
148 | #else | |
149 | #define closesocket(s) close(s) | |
150 | #define ioctlsocket(s, r, v) ioctl(s, r, v) | |
151 | #endif | |
152 | ||
153 | int slirp_socket(int domain, int type, int protocol); | |
154 | void slirp_set_nonblock(int fd); | |
155 | ||
156 | static inline int slirp_socket_set_nodelay(int fd) | |
157 | { | |
158 | int v = 1; | |
159 | return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); | |
160 | } | |
161 | ||
162 | static inline int slirp_socket_set_fast_reuse(int fd) | |
163 | { | |
164 | #ifndef _WIN32 | |
165 | int v = 1; | |
166 | return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); | |
167 | #else | |
168 | /* Enabling the reuse of an endpoint that was used by a socket still in | |
169 | * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows | |
170 | * fast reuse is the default and SO_REUSEADDR does strange things. So we | |
171 | * don't have to do anything here. More info can be found at: | |
172 | * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */ | |
173 | return 0; | |
174 | #endif | |
175 | } | |
176 | ||
177 | void slirp_pstrcpy(char *buf, int buf_size, const char *str); | |
178 | ||
179 | #endif |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | #include "libslirp.h" | |
2 | #include "util.h" | |
3 | ||
4 | const char * | |
5 | slirp_version_string(void) | |
6 | { | |
7 | return stringify(SLIRP_MAJOR_VERSION) "." | |
8 | stringify(SLIRP_MINOR_VERSION) "." | |
9 | stringify(SLIRP_MICRO_VERSION); | |
10 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * VMState interpreter | |
3 | * | |
4 | * Copyright (c) 2009-2018 Red Hat Inc | |
5 | * | |
6 | * Authors: | |
7 | * Juan Quintela <quintela@redhat.com> | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * | |
13 | * 1. Redistributions of source code must retain the above | |
14 | * copyright notice, this list of conditions and the following | |
15 | * disclaimer. | |
16 | * | |
17 | * 2. Redistributions in binary form must reproduce the above | |
18 | * copyright notice, this list of conditions and the following | |
19 | * disclaimer in the documentation and/or other materials provided | |
20 | * with the distribution. | |
21 | * | |
22 | * 3. Neither the name of the copyright holder nor the names of its | |
23 | * contributors may be used to endorse or promote products derived | |
24 | * from this software without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
27 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
28 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
29 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
30 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
31 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
32 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
33 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
35 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
36 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
37 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
38 | */ | |
39 | #include <assert.h> | |
40 | #include <errno.h> | |
41 | #include <string.h> | |
42 | #include <glib.h> | |
43 | ||
44 | #include "stream.h" | |
45 | #include "vmstate.h" | |
46 | ||
47 | static int get_nullptr(SlirpIStream *f, void *pv, size_t size, | |
48 | const VMStateField *field) | |
49 | { | |
50 | if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) { | |
51 | return 0; | |
52 | } | |
53 | g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER"); | |
54 | return -EINVAL; | |
55 | } | |
56 | ||
57 | static int put_nullptr(SlirpOStream *f, void *pv, size_t size, | |
58 | const VMStateField *field) | |
59 | ||
60 | { | |
61 | if (pv == NULL) { | |
62 | slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER); | |
63 | return 0; | |
64 | } | |
65 | g_warning("vmstate: put_nullptr must be called with pv == NULL"); | |
66 | return -EINVAL; | |
67 | } | |
68 | ||
69 | const VMStateInfo slirp_vmstate_info_nullptr = { | |
70 | .name = "uint64", | |
71 | .get = get_nullptr, | |
72 | .put = put_nullptr, | |
73 | }; | |
74 | ||
75 | /* 8 bit unsigned int */ | |
76 | ||
77 | static int get_uint8(SlirpIStream *f, void *pv, size_t size, | |
78 | const VMStateField *field) | |
79 | { | |
80 | uint8_t *v = pv; | |
81 | *v = slirp_istream_read_u8(f); | |
82 | return 0; | |
83 | } | |
84 | ||
85 | static int put_uint8(SlirpOStream *f, void *pv, size_t size, | |
86 | const VMStateField *field) | |
87 | { | |
88 | uint8_t *v = pv; | |
89 | slirp_ostream_write_u8(f, *v); | |
90 | return 0; | |
91 | } | |
92 | ||
93 | const VMStateInfo slirp_vmstate_info_uint8 = { | |
94 | .name = "uint8", | |
95 | .get = get_uint8, | |
96 | .put = put_uint8, | |
97 | }; | |
98 | ||
99 | /* 16 bit unsigned int */ | |
100 | ||
101 | static int get_uint16(SlirpIStream *f, void *pv, size_t size, | |
102 | const VMStateField *field) | |
103 | { | |
104 | uint16_t *v = pv; | |
105 | *v = slirp_istream_read_u16(f); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | static int put_uint16(SlirpOStream *f, void *pv, size_t size, | |
110 | const VMStateField *field) | |
111 | { | |
112 | uint16_t *v = pv; | |
113 | slirp_ostream_write_u16(f, *v); | |
114 | return 0; | |
115 | } | |
116 | ||
117 | const VMStateInfo slirp_vmstate_info_uint16 = { | |
118 | .name = "uint16", | |
119 | .get = get_uint16, | |
120 | .put = put_uint16, | |
121 | }; | |
122 | ||
123 | /* 32 bit unsigned int */ | |
124 | ||
125 | static int get_uint32(SlirpIStream *f, void *pv, size_t size, | |
126 | const VMStateField *field) | |
127 | { | |
128 | uint32_t *v = pv; | |
129 | *v = slirp_istream_read_u32(f); | |
130 | return 0; | |
131 | } | |
132 | ||
133 | static int put_uint32(SlirpOStream *f, void *pv, size_t size, | |
134 | const VMStateField *field) | |
135 | { | |
136 | uint32_t *v = pv; | |
137 | slirp_ostream_write_u32(f, *v); | |
138 | return 0; | |
139 | } | |
140 | ||
141 | const VMStateInfo slirp_vmstate_info_uint32 = { | |
142 | .name = "uint32", | |
143 | .get = get_uint32, | |
144 | .put = put_uint32, | |
145 | }; | |
146 | ||
147 | /* 16 bit int */ | |
148 | ||
149 | static int get_int16(SlirpIStream *f, void *pv, size_t size, | |
150 | const VMStateField *field) | |
151 | { | |
152 | int16_t *v = pv; | |
153 | *v = slirp_istream_read_i16(f); | |
154 | return 0; | |
155 | } | |
156 | ||
157 | static int put_int16(SlirpOStream *f, void *pv, size_t size, | |
158 | const VMStateField *field) | |
159 | { | |
160 | int16_t *v = pv; | |
161 | slirp_ostream_write_i16(f, *v); | |
162 | return 0; | |
163 | } | |
164 | ||
165 | const VMStateInfo slirp_vmstate_info_int16 = { | |
166 | .name = "int16", | |
167 | .get = get_int16, | |
168 | .put = put_int16, | |
169 | }; | |
170 | ||
171 | /* 32 bit int */ | |
172 | ||
173 | static int get_int32(SlirpIStream *f, void *pv, size_t size, | |
174 | const VMStateField *field) | |
175 | { | |
176 | int32_t *v = pv; | |
177 | *v = slirp_istream_read_i32(f); | |
178 | return 0; | |
179 | } | |
180 | ||
181 | static int put_int32(SlirpOStream *f, void *pv, size_t size, | |
182 | const VMStateField *field) | |
183 | { | |
184 | int32_t *v = pv; | |
185 | slirp_ostream_write_i32(f, *v); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | const VMStateInfo slirp_vmstate_info_int32 = { | |
190 | .name = "int32", | |
191 | .get = get_int32, | |
192 | .put = put_int32, | |
193 | }; | |
194 | ||
195 | /* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate | |
196 | * a temporary buffer and the pre_load/pre_save methods in the child vmsd | |
197 | * copy stuff from the parent into the child and do calculations to fill | |
198 | * in fields that don't really exist in the parent but need to be in the | |
199 | * stream. | |
200 | */ | |
201 | static int get_tmp(SlirpIStream *f, void *pv, size_t size, | |
202 | const VMStateField *field) | |
203 | { | |
204 | int ret; | |
205 | const VMStateDescription *vmsd = field->vmsd; | |
206 | int version_id = field->version_id; | |
207 | void *tmp = g_malloc(size); | |
208 | ||
209 | /* Writes the parent field which is at the start of the tmp */ | |
210 | *(void **)tmp = pv; | |
211 | ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id); | |
212 | g_free(tmp); | |
213 | return ret; | |
214 | } | |
215 | ||
216 | static int put_tmp(SlirpOStream *f, void *pv, size_t size, | |
217 | const VMStateField *field) | |
218 | { | |
219 | const VMStateDescription *vmsd = field->vmsd; | |
220 | void *tmp = g_malloc(size); | |
221 | int ret; | |
222 | ||
223 | /* Writes the parent field which is at the start of the tmp */ | |
224 | *(void **)tmp = pv; | |
225 | ret = slirp_vmstate_save_state(f, vmsd, tmp); | |
226 | g_free(tmp); | |
227 | ||
228 | return ret; | |
229 | } | |
230 | ||
231 | const VMStateInfo slirp_vmstate_info_tmp = { | |
232 | .name = "tmp", | |
233 | .get = get_tmp, | |
234 | .put = put_tmp, | |
235 | }; | |
236 | ||
237 | /* uint8_t buffers */ | |
238 | ||
239 | static int get_buffer(SlirpIStream *f, void *pv, size_t size, | |
240 | const VMStateField *field) | |
241 | { | |
242 | slirp_istream_read(f, pv, size); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | static int put_buffer(SlirpOStream *f, void *pv, size_t size, | |
247 | const VMStateField *field) | |
248 | { | |
249 | slirp_ostream_write(f, pv, size); | |
250 | return 0; | |
251 | } | |
252 | ||
253 | const VMStateInfo slirp_vmstate_info_buffer = { | |
254 | .name = "buffer", | |
255 | .get = get_buffer, | |
256 | .put = put_buffer, | |
257 | }; | |
258 | ||
259 | static int vmstate_n_elems(void *opaque, const VMStateField *field) | |
260 | { | |
261 | int n_elems = 1; | |
262 | ||
263 | if (field->flags & VMS_ARRAY) { | |
264 | n_elems = field->num; | |
265 | } else if (field->flags & VMS_VARRAY_INT32) { | |
266 | n_elems = *(int32_t *)(opaque + field->num_offset); | |
267 | } else if (field->flags & VMS_VARRAY_UINT32) { | |
268 | n_elems = *(uint32_t *)(opaque + field->num_offset); | |
269 | } else if (field->flags & VMS_VARRAY_UINT16) { | |
270 | n_elems = *(uint16_t *)(opaque + field->num_offset); | |
271 | } else if (field->flags & VMS_VARRAY_UINT8) { | |
272 | n_elems = *(uint8_t *)(opaque + field->num_offset); | |
273 | } | |
274 | ||
275 | if (field->flags & VMS_MULTIPLY_ELEMENTS) { | |
276 | n_elems *= field->num; | |
277 | } | |
278 | ||
279 | return n_elems; | |
280 | } | |
281 | ||
282 | static int vmstate_size(void *opaque, const VMStateField *field) | |
283 | { | |
284 | int size = field->size; | |
285 | ||
286 | if (field->flags & VMS_VBUFFER) { | |
287 | size = *(int32_t *)(opaque + field->size_offset); | |
288 | if (field->flags & VMS_MULTIPLY) { | |
289 | size *= field->size; | |
290 | } | |
291 | } | |
292 | ||
293 | return size; | |
294 | } | |
295 | ||
296 | static int vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd, | |
297 | void *opaque, int version_id) | |
298 | { | |
299 | int ret = 0; | |
300 | const VMStateField *field = vmsd->fields; | |
301 | ||
302 | if (vmsd->pre_save) { | |
303 | ret = vmsd->pre_save(opaque); | |
304 | if (ret) { | |
305 | g_warning("pre-save failed: %s", vmsd->name); | |
306 | return ret; | |
307 | } | |
308 | } | |
309 | ||
310 | while (field->name) { | |
311 | if ((field->field_exists && field->field_exists(opaque, version_id)) || | |
312 | (!field->field_exists && field->version_id <= version_id)) { | |
313 | void *first_elem = opaque + field->offset; | |
314 | int i, n_elems = vmstate_n_elems(opaque, field); | |
315 | int size = vmstate_size(opaque, field); | |
316 | ||
317 | if (field->flags & VMS_POINTER) { | |
318 | first_elem = *(void **)first_elem; | |
319 | assert(first_elem || !n_elems || !size); | |
320 | } | |
321 | for (i = 0; i < n_elems; i++) { | |
322 | void *curr_elem = first_elem + size * i; | |
323 | ret = 0; | |
324 | ||
325 | if (field->flags & VMS_ARRAY_OF_POINTER) { | |
326 | assert(curr_elem); | |
327 | curr_elem = *(void **)curr_elem; | |
328 | } | |
329 | if (!curr_elem && size) { | |
330 | /* if null pointer write placeholder and do not follow */ | |
331 | assert(field->flags & VMS_ARRAY_OF_POINTER); | |
332 | ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size, | |
333 | NULL); | |
334 | } else if (field->flags & VMS_STRUCT) { | |
335 | ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem); | |
336 | } else if (field->flags & VMS_VSTRUCT) { | |
337 | ret = vmstate_save_state_v(f, field->vmsd, curr_elem, | |
338 | field->struct_version_id); | |
339 | } else { | |
340 | ret = field->info->put(f, curr_elem, size, field); | |
341 | } | |
342 | if (ret) { | |
343 | g_warning("Save of field %s/%s failed", vmsd->name, | |
344 | field->name); | |
345 | return ret; | |
346 | } | |
347 | } | |
348 | } else { | |
349 | if (field->flags & VMS_MUST_EXIST) { | |
350 | g_warning("Output state validation failed: %s/%s", vmsd->name, | |
351 | field->name); | |
352 | assert(!(field->flags & VMS_MUST_EXIST)); | |
353 | } | |
354 | } | |
355 | field++; | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, | |
362 | void *opaque) | |
363 | { | |
364 | return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id); | |
365 | } | |
366 | ||
367 | static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) | |
368 | { | |
369 | if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) { | |
370 | size_t size = vmstate_size(opaque, field); | |
371 | size *= vmstate_n_elems(opaque, field); | |
372 | if (size) { | |
373 | *(void **)ptr = g_malloc(size); | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, | |
379 | void *opaque, int version_id) | |
380 | { | |
381 | VMStateField *field = vmsd->fields; | |
382 | int ret = 0; | |
383 | ||
384 | if (version_id > vmsd->version_id) { | |
385 | g_warning("%s: incoming version_id %d is too new " | |
386 | "for local version_id %d", | |
387 | vmsd->name, version_id, vmsd->version_id); | |
388 | return -EINVAL; | |
389 | } | |
390 | if (vmsd->pre_load) { | |
391 | int ret = vmsd->pre_load(opaque); | |
392 | if (ret) { | |
393 | return ret; | |
394 | } | |
395 | } | |
396 | while (field->name) { | |
397 | if ((field->field_exists && field->field_exists(opaque, version_id)) || | |
398 | (!field->field_exists && field->version_id <= version_id)) { | |
399 | void *first_elem = opaque + field->offset; | |
400 | int i, n_elems = vmstate_n_elems(opaque, field); | |
401 | int size = vmstate_size(opaque, field); | |
402 | ||
403 | vmstate_handle_alloc(first_elem, field, opaque); | |
404 | if (field->flags & VMS_POINTER) { | |
405 | first_elem = *(void **)first_elem; | |
406 | assert(first_elem || !n_elems || !size); | |
407 | } | |
408 | for (i = 0; i < n_elems; i++) { | |
409 | void *curr_elem = first_elem + size * i; | |
410 | ||
411 | if (field->flags & VMS_ARRAY_OF_POINTER) { | |
412 | curr_elem = *(void **)curr_elem; | |
413 | } | |
414 | if (!curr_elem && size) { | |
415 | /* if null pointer check placeholder and do not follow */ | |
416 | assert(field->flags & VMS_ARRAY_OF_POINTER); | |
417 | ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size, | |
418 | NULL); | |
419 | } else if (field->flags & VMS_STRUCT) { | |
420 | ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, | |
421 | field->vmsd->version_id); | |
422 | } else if (field->flags & VMS_VSTRUCT) { | |
423 | ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, | |
424 | field->struct_version_id); | |
425 | } else { | |
426 | ret = field->info->get(f, curr_elem, size, field); | |
427 | } | |
428 | if (ret < 0) { | |
429 | g_warning("Failed to load %s:%s", vmsd->name, field->name); | |
430 | return ret; | |
431 | } | |
432 | } | |
433 | } else if (field->flags & VMS_MUST_EXIST) { | |
434 | g_warning("Input validation failed: %s/%s", vmsd->name, | |
435 | field->name); | |
436 | return -1; | |
437 | } | |
438 | field++; | |
439 | } | |
440 | if (vmsd->post_load) { | |
441 | ret = vmsd->post_load(opaque, version_id); | |
442 | } | |
443 | return ret; | |
444 | } |
0 | /* SPDX-License-Identifier: BSD-3-Clause */ | |
1 | /* | |
2 | * QEMU migration/snapshot declarations | |
3 | * | |
4 | * Copyright (c) 2009-2011 Red Hat, Inc. | |
5 | * | |
6 | * Original author: Juan Quintela <quintela@redhat.com> | |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * | |
12 | * 1. Redistributions of source code must retain the above | |
13 | * copyright notice, this list of conditions and the following | |
14 | * disclaimer. | |
15 | * | |
16 | * 2. Redistributions in binary form must reproduce the above | |
17 | * copyright notice, this list of conditions and the following | |
18 | * disclaimer in the documentation and/or other materials provided | |
19 | * with the distribution. | |
20 | * | |
21 | * 3. Neither the name of the copyright holder nor the names of its | |
22 | * contributors may be used to endorse or promote products derived | |
23 | * from this software without specific prior written permission. | |
24 | * | |
25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
26 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
27 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
28 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
29 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
30 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
31 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
32 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
34 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
35 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | |
36 | * OF THE POSSIBILITY OF SUCH DAMAGE. | |
37 | */ | |
38 | #ifndef VMSTATE_H_ | |
39 | #define VMSTATE_H_ | |
40 | ||
41 | #include <unistd.h> | |
42 | #include <stdint.h> | |
43 | #include <stdbool.h> | |
44 | #include "slirp.h" | |
45 | #include "stream.h" | |
46 | ||
47 | #define stringify(s) tostring(s) | |
48 | #define tostring(s) #s | |
49 | ||
50 | typedef struct VMStateInfo VMStateInfo; | |
51 | typedef struct VMStateDescription VMStateDescription; | |
52 | typedef struct VMStateField VMStateField; | |
53 | ||
54 | int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, | |
55 | void *opaque); | |
56 | int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, | |
57 | void *opaque, int version_id); | |
58 | ||
59 | /* VMStateInfo allows customized migration of objects that don't fit in | |
60 | * any category in VMStateFlags. Additional information is always passed | |
61 | * into get and put in terms of field and vmdesc parameters. However | |
62 | * these two parameters should only be used in cases when customized | |
63 | * handling is needed, such as QTAILQ. For primitive data types such as | |
64 | * integer, field and vmdesc parameters should be ignored inside get/put. | |
65 | */ | |
66 | struct VMStateInfo { | |
67 | const char *name; | |
68 | int (*get)(SlirpIStream *f, void *pv, size_t size, | |
69 | const VMStateField *field); | |
70 | int (*put)(SlirpOStream *f, void *pv, size_t size, | |
71 | const VMStateField *field); | |
72 | }; | |
73 | ||
74 | enum VMStateFlags { | |
75 | /* Ignored */ | |
76 | VMS_SINGLE = 0x001, | |
77 | ||
78 | /* The struct member at opaque + VMStateField.offset is a pointer | |
79 | * to the actual field (e.g. struct a { uint8_t *b; | |
80 | * }). Dereference the pointer before using it as basis for | |
81 | * further pointer arithmetic (see e.g. VMS_ARRAY). Does not | |
82 | * affect the meaning of VMStateField.num_offset or | |
83 | * VMStateField.size_offset; see VMS_VARRAY* and VMS_VBUFFER for | |
84 | * those. */ | |
85 | VMS_POINTER = 0x002, | |
86 | ||
87 | /* The field is an array of fixed size. VMStateField.num contains | |
88 | * the number of entries in the array. The size of each entry is | |
89 | * given by VMStateField.size and / or opaque + | |
90 | * VMStateField.size_offset; see VMS_VBUFFER and | |
91 | * VMS_MULTIPLY. Each array entry will be processed individually | |
92 | * (VMStateField.info.get()/put() if VMS_STRUCT is not set, | |
93 | * recursion into VMStateField.vmsd if VMS_STRUCT is set). May not | |
94 | * be combined with VMS_VARRAY*. */ | |
95 | VMS_ARRAY = 0x004, | |
96 | ||
97 | /* The field is itself a struct, containing one or more | |
98 | * fields. Recurse into VMStateField.vmsd. Most useful in | |
99 | * combination with VMS_ARRAY / VMS_VARRAY*, recursing into each | |
100 | * array entry. */ | |
101 | VMS_STRUCT = 0x008, | |
102 | ||
103 | /* The field is an array of variable size. The int32_t at opaque + | |
104 | * VMStateField.num_offset contains the number of entries in the | |
105 | * array. See the VMS_ARRAY description regarding array handling | |
106 | * in general. May not be combined with VMS_ARRAY or any other | |
107 | * VMS_VARRAY*. */ | |
108 | VMS_VARRAY_INT32 = 0x010, | |
109 | ||
110 | /* Ignored */ | |
111 | VMS_BUFFER = 0x020, | |
112 | ||
113 | /* The field is a (fixed-size or variable-size) array of pointers | |
114 | * (e.g. struct a { uint8_t *b[]; }). Dereference each array entry | |
115 | * before using it. Note: Does not imply any one of VMS_ARRAY / | |
116 | * VMS_VARRAY*; these need to be set explicitly. */ | |
117 | VMS_ARRAY_OF_POINTER = 0x040, | |
118 | ||
119 | /* The field is an array of variable size. The uint16_t at opaque | |
120 | * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) | |
121 | * contains the number of entries in the array. See the VMS_ARRAY | |
122 | * description regarding array handling in general. May not be | |
123 | * combined with VMS_ARRAY or any other VMS_VARRAY*. */ | |
124 | VMS_VARRAY_UINT16 = 0x080, | |
125 | ||
126 | /* The size of the individual entries (a single array entry if | |
127 | * VMS_ARRAY or any of VMS_VARRAY* are set, or the field itself if | |
128 | * neither is set) is variable (i.e. not known at compile-time), | |
129 | * but the same for all entries. Use the int32_t at opaque + | |
130 | * VMStateField.size_offset (subject to VMS_MULTIPLY) to determine | |
131 | * the size of each (and every) entry. */ | |
132 | VMS_VBUFFER = 0x100, | |
133 | ||
134 | /* Multiply the entry size given by the int32_t at opaque + | |
135 | * VMStateField.size_offset (see VMS_VBUFFER description) with | |
136 | * VMStateField.size to determine the number of bytes to be | |
137 | * allocated. Only valid in combination with VMS_VBUFFER. */ | |
138 | VMS_MULTIPLY = 0x200, | |
139 | ||
140 | /* The field is an array of variable size. The uint8_t at opaque + | |
141 | * VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) | |
142 | * contains the number of entries in the array. See the VMS_ARRAY | |
143 | * description regarding array handling in general. May not be | |
144 | * combined with VMS_ARRAY or any other VMS_VARRAY*. */ | |
145 | VMS_VARRAY_UINT8 = 0x400, | |
146 | ||
147 | /* The field is an array of variable size. The uint32_t at opaque | |
148 | * + VMStateField.num_offset (subject to VMS_MULTIPLY_ELEMENTS) | |
149 | * contains the number of entries in the array. See the VMS_ARRAY | |
150 | * description regarding array handling in general. May not be | |
151 | * combined with VMS_ARRAY or any other VMS_VARRAY*. */ | |
152 | VMS_VARRAY_UINT32 = 0x800, | |
153 | ||
154 | /* Fail loading the serialised VM state if this field is missing | |
155 | * from the input. */ | |
156 | VMS_MUST_EXIST = 0x1000, | |
157 | ||
158 | /* When loading serialised VM state, allocate memory for the | |
159 | * (entire) field. Only valid in combination with | |
160 | * VMS_POINTER. Note: Not all combinations with other flags are | |
161 | * currently supported, e.g. VMS_ALLOC|VMS_ARRAY_OF_POINTER won't | |
162 | * cause the individual entries to be allocated. */ | |
163 | VMS_ALLOC = 0x2000, | |
164 | ||
165 | /* Multiply the number of entries given by the integer at opaque + | |
166 | * VMStateField.num_offset (see VMS_VARRAY*) with VMStateField.num | |
167 | * to determine the number of entries in the array. Only valid in | |
168 | * combination with one of VMS_VARRAY*. */ | |
169 | VMS_MULTIPLY_ELEMENTS = 0x4000, | |
170 | ||
171 | /* A structure field that is like VMS_STRUCT, but uses | |
172 | * VMStateField.struct_version_id to tell which version of the | |
173 | * structure we are referencing to use. */ | |
174 | VMS_VSTRUCT = 0x8000, | |
175 | }; | |
176 | ||
177 | struct VMStateField { | |
178 | const char *name; | |
179 | size_t offset; | |
180 | size_t size; | |
181 | size_t start; | |
182 | int num; | |
183 | size_t num_offset; | |
184 | size_t size_offset; | |
185 | const VMStateInfo *info; | |
186 | enum VMStateFlags flags; | |
187 | const VMStateDescription *vmsd; | |
188 | int version_id; | |
189 | int struct_version_id; | |
190 | bool (*field_exists)(void *opaque, int version_id); | |
191 | }; | |
192 | ||
193 | struct VMStateDescription { | |
194 | const char *name; | |
195 | int version_id; | |
196 | int (*pre_load)(void *opaque); | |
197 | int (*post_load)(void *opaque, int version_id); | |
198 | int (*pre_save)(void *opaque); | |
199 | VMStateField *fields; | |
200 | }; | |
201 | ||
202 | ||
203 | extern const VMStateInfo slirp_vmstate_info_int16; | |
204 | extern const VMStateInfo slirp_vmstate_info_int32; | |
205 | extern const VMStateInfo slirp_vmstate_info_uint8; | |
206 | extern const VMStateInfo slirp_vmstate_info_uint16; | |
207 | extern const VMStateInfo slirp_vmstate_info_uint32; | |
208 | ||
209 | /** Put this in the stream when migrating a null pointer.*/ | |
210 | #define VMS_NULLPTR_MARKER (0x30U) /* '0' */ | |
211 | extern const VMStateInfo slirp_vmstate_info_nullptr; | |
212 | ||
213 | extern const VMStateInfo slirp_vmstate_info_buffer; | |
214 | extern const VMStateInfo slirp_vmstate_info_tmp; | |
215 | ||
216 | #define type_check_array(t1, t2, n) ((t1(*)[n])0 - (t2 *)0) | |
217 | #define type_check_pointer(t1, t2) ((t1 **)0 - (t2 *)0) | |
218 | #define typeof_field(type, field) typeof(((type *)0)->field) | |
219 | #define type_check(t1, t2) ((t1 *)0 - (t2 *)0) | |
220 | ||
221 | #define vmstate_offset_value(_state, _field, _type) \ | |
222 | (offsetof(_state, _field) + type_check(_type, typeof_field(_state, _field))) | |
223 | ||
224 | #define vmstate_offset_pointer(_state, _field, _type) \ | |
225 | (offsetof(_state, _field) + \ | |
226 | type_check_pointer(_type, typeof_field(_state, _field))) | |
227 | ||
228 | #define vmstate_offset_array(_state, _field, _type, _num) \ | |
229 | (offsetof(_state, _field) + \ | |
230 | type_check_array(_type, typeof_field(_state, _field), _num)) | |
231 | ||
232 | #define vmstate_offset_buffer(_state, _field) \ | |
233 | vmstate_offset_array(_state, _field, uint8_t, \ | |
234 | sizeof(typeof_field(_state, _field))) | |
235 | ||
236 | /* In the macros below, if there is a _version, that means the macro's | |
237 | * field will be processed only if the version being received is >= | |
238 | * the _version specified. In general, if you add a new field, you | |
239 | * would increment the structure's version and put that version | |
240 | * number into the new field so it would only be processed with the | |
241 | * new version. | |
242 | * | |
243 | * In particular, for VMSTATE_STRUCT() and friends the _version does | |
244 | * *NOT* pick the version of the sub-structure. It works just as | |
245 | * specified above. The version of the top-level structure received | |
246 | * is passed down to all sub-structures. This means that the | |
247 | * sub-structures must have version that are compatible with all the | |
248 | * structures that use them. | |
249 | * | |
250 | * If you want to specify the version of the sub-structure, use | |
251 | * VMSTATE_VSTRUCT(), which allows the specific sub-structure version | |
252 | * to be directly specified. | |
253 | */ | |
254 | ||
255 | #define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) \ | |
256 | { \ | |
257 | .name = (stringify(_field)), .version_id = (_version), \ | |
258 | .field_exists = (_test), .size = sizeof(_type), .info = &(_info), \ | |
259 | .flags = VMS_SINGLE, \ | |
260 | .offset = vmstate_offset_value(_state, _field, _type), \ | |
261 | } | |
262 | ||
263 | #define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) \ | |
264 | { \ | |
265 | .name = (stringify(_field)), .version_id = (_version), .num = (_num), \ | |
266 | .info = &(_info), .size = sizeof(_type), .flags = VMS_ARRAY, \ | |
267 | .offset = vmstate_offset_array(_state, _field, _type, _num), \ | |
268 | } | |
269 | ||
270 | #define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) \ | |
271 | { \ | |
272 | .name = (stringify(_field)), .version_id = (_version), \ | |
273 | .field_exists = (_test), .vmsd = &(_vmsd), .size = sizeof(_type), \ | |
274 | .flags = VMS_STRUCT, \ | |
275 | .offset = vmstate_offset_value(_state, _field, _type), \ | |
276 | } | |
277 | ||
278 | #define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) \ | |
279 | { \ | |
280 | .name = (stringify(_field)), .version_id = (_version), \ | |
281 | .vmsd = &(_vmsd), .size = sizeof(_type *), \ | |
282 | .flags = VMS_STRUCT | VMS_POINTER, \ | |
283 | .offset = vmstate_offset_pointer(_state, _field, _type), \ | |
284 | } | |
285 | ||
286 | #define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, \ | |
287 | _vmsd, _type) \ | |
288 | { \ | |
289 | .name = (stringify(_field)), .num = (_num), .field_exists = (_test), \ | |
290 | .version_id = (_version), .vmsd = &(_vmsd), .size = sizeof(_type), \ | |
291 | .flags = VMS_STRUCT | VMS_ARRAY, \ | |
292 | .offset = vmstate_offset_array(_state, _field, _type, _num), \ | |
293 | } | |
294 | ||
295 | #define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) \ | |
296 | { \ | |
297 | .name = (stringify(_field)), .version_id = (_version), \ | |
298 | .field_exists = (_test), .size = (_size - _start), \ | |
299 | .info = &slirp_vmstate_info_buffer, .flags = VMS_BUFFER, \ | |
300 | .offset = vmstate_offset_buffer(_state, _field) + _start, \ | |
301 | } | |
302 | ||
303 | #define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _field_size) \ | |
304 | { \ | |
305 | .name = (stringify(_field)), .version_id = (_version), \ | |
306 | .field_exists = (_test), \ | |
307 | .size_offset = vmstate_offset_value(_state, _field_size, uint32_t), \ | |
308 | .info = &slirp_vmstate_info_buffer, \ | |
309 | .flags = VMS_VBUFFER | VMS_POINTER, \ | |
310 | .offset = offsetof(_state, _field), \ | |
311 | } | |
312 | ||
313 | #define QEMU_BUILD_BUG_ON_STRUCT(x) \ | |
314 | struct { \ | |
315 | int : (x) ? -1 : 1; \ | |
316 | } | |
317 | ||
318 | #define QEMU_BUILD_BUG_ON_ZERO(x) \ | |
319 | (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) | |
320 | ||
321 | /* Allocate a temporary of type 'tmp_type', set tmp->parent to _state | |
322 | * and execute the vmsd on the temporary. Note that we're working with | |
323 | * the whole of _state here, not a field within it. | |
324 | * We compile time check that: | |
325 | * That _tmp_type contains a 'parent' member that's a pointer to the | |
326 | * '_state' type | |
327 | * That the pointer is right at the start of _tmp_type. | |
328 | */ | |
329 | #define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) \ | |
330 | { \ | |
331 | .name = "tmp", \ | |
332 | .size = sizeof(_tmp_type) + \ | |
333 | QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \ | |
334 | type_check_pointer(_state, typeof_field(_tmp_type, parent)), \ | |
335 | .vmsd = &(_vmsd), .info = &slirp_vmstate_info_tmp, \ | |
336 | } | |
337 | ||
338 | #define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ | |
339 | VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) | |
340 | ||
341 | #define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ | |
342 | VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) | |
343 | ||
344 | #define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ | |
345 | VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type) | |
346 | ||
347 | #define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ | |
348 | VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, _vmsd, \ | |
349 | _type) | |
350 | ||
351 | #define VMSTATE_INT16_V(_f, _s, _v) \ | |
352 | VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int16, int16_t) | |
353 | #define VMSTATE_INT32_V(_f, _s, _v) \ | |
354 | VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int32, int32_t) | |
355 | ||
356 | #define VMSTATE_UINT8_V(_f, _s, _v) \ | |
357 | VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint8, uint8_t) | |
358 | #define VMSTATE_UINT16_V(_f, _s, _v) \ | |
359 | VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint16, uint16_t) | |
360 | #define VMSTATE_UINT32_V(_f, _s, _v) \ | |
361 | VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint32, uint32_t) | |
362 | ||
363 | #define VMSTATE_INT16(_f, _s) VMSTATE_INT16_V(_f, _s, 0) | |
364 | #define VMSTATE_INT32(_f, _s) VMSTATE_INT32_V(_f, _s, 0) | |
365 | ||
366 | #define VMSTATE_UINT8(_f, _s) VMSTATE_UINT8_V(_f, _s, 0) | |
367 | #define VMSTATE_UINT16(_f, _s) VMSTATE_UINT16_V(_f, _s, 0) | |
368 | #define VMSTATE_UINT32(_f, _s) VMSTATE_UINT32_V(_f, _s, 0) | |
369 | ||
370 | #define VMSTATE_UINT16_TEST(_f, _s, _t) \ | |
371 | VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint16, uint16_t) | |
372 | ||
373 | #define VMSTATE_UINT32_TEST(_f, _s, _t) \ | |
374 | VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint32, uint32_t) | |
375 | ||
376 | #define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \ | |
377 | VMSTATE_ARRAY(_f, _s, _n, _v, slirp_vmstate_info_int16, int16_t) | |
378 | ||
379 | #define VMSTATE_INT16_ARRAY(_f, _s, _n) VMSTATE_INT16_ARRAY_V(_f, _s, _n, 0) | |
380 | ||
381 | #define VMSTATE_BUFFER_V(_f, _s, _v) \ | |
382 | VMSTATE_STATIC_BUFFER(_f, _s, _v, NULL, 0, sizeof(typeof_field(_s, _f))) | |
383 | ||
384 | #define VMSTATE_BUFFER(_f, _s) VMSTATE_BUFFER_V(_f, _s, 0) | |
385 | ||
386 | #define VMSTATE_END_OF_LIST() \ | |
387 | { \ | |
388 | } | |
389 | ||
390 | #endif |
0 | 0 | /* |
1 | 1 | SPDX-License-Identifier: MIT |
2 | 2 | |
3 | Parson ( http://kgabis.github.com/parson/ ) | |
3 | Parson 1.0.2 ( http://kgabis.github.com/parson/ ) | |
4 | 4 | Copyright (c) 2012 - 2019 Krzysztof Gabis |
5 | 5 | |
6 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy |
155 | 155 | return NULL; |
156 | 156 | } |
157 | 157 | output_string[n] = '\0'; |
158 | strncpy(output_string, string, n); | |
158 | memcpy(output_string, string, n); | |
159 | 159 | return output_string; |
160 | 160 | } |
161 | 161 | |
1495 | 1495 | size_t json_serialization_size(const JSON_Value *value) { |
1496 | 1496 | char num_buf[NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ |
1497 | 1497 | int res = json_serialize_to_buffer_r(value, NULL, 0, 0, num_buf); |
1498 | return res < 0 ? 0 : (size_t)(res + 1); | |
1498 | return res < 0 ? 0 : (size_t)(res) + 1; | |
1499 | 1499 | } |
1500 | 1500 | |
1501 | 1501 | JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { |
1555 | 1555 | size_t json_serialization_size_pretty(const JSON_Value *value) { |
1556 | 1556 | char num_buf[NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */ |
1557 | 1557 | int res = json_serialize_to_buffer_r(value, NULL, 0, 1, num_buf); |
1558 | return res < 0 ? 0 : (size_t)(res + 1); | |
1558 | return res < 0 ? 0 : (size_t)(res) + 1; | |
1559 | 1559 | } |
1560 | 1560 | |
1561 | 1561 | JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) { |
1775 | 1775 | } |
1776 | 1776 | |
1777 | 1777 | JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) { |
1778 | return json_object_set_value(object, name, json_value_init_string(string)); | |
1778 | JSON_Value *value = json_value_init_string(string); | |
1779 | JSON_Status status = json_object_set_value(object, name, value); | |
1780 | if (status == JSONFailure) { | |
1781 | json_value_free(value); | |
1782 | } | |
1783 | return status; | |
1779 | 1784 | } |
1780 | 1785 | |
1781 | 1786 | JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) { |
1782 | return json_object_set_value(object, name, json_value_init_number(number)); | |
1787 | JSON_Value *value = json_value_init_number(number); | |
1788 | JSON_Status status = json_object_set_value(object, name, value); | |
1789 | if (status == JSONFailure) { | |
1790 | json_value_free(value); | |
1791 | } | |
1792 | return status; | |
1783 | 1793 | } |
1784 | 1794 | |
1785 | 1795 | JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) { |
1786 | return json_object_set_value(object, name, json_value_init_boolean(boolean)); | |
1796 | JSON_Value *value = json_value_init_boolean(boolean); | |
1797 | JSON_Status status = json_object_set_value(object, name, value); | |
1798 | if (status == JSONFailure) { | |
1799 | json_value_free(value); | |
1800 | } | |
1801 | return status; | |
1787 | 1802 | } |
1788 | 1803 | |
1789 | 1804 | JSON_Status json_object_set_null(JSON_Object *object, const char *name) { |
1790 | return json_object_set_value(object, name, json_value_init_null()); | |
1805 | JSON_Value *value = json_value_init_null(); | |
1806 | JSON_Status status = json_object_set_value(object, name, value); | |
1807 | if (status == JSONFailure) { | |
1808 | json_value_free(value); | |
1809 | } | |
1810 | return status; | |
1791 | 1811 | } |
1792 | 1812 | |
1793 | 1813 | JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) { |
0 | 0 | /* |
1 | 1 | SPDX-License-Identifier: MIT |
2 | 2 | |
3 | Parson ( http://kgabis.github.com/parson/ ) | |
3 | Parson 1.0.2 ( http://kgabis.github.com/parson/ ) | |
4 | 4 | Copyright (c) 2012 - 2019 Krzysztof Gabis |
5 | 5 | |
6 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy |
0 | # Vendor | |
1 | ||
2 | The `*.patch` files in `vendor_patches` directory (if exists) are applied to `vendor` via [`vendor.sh`](./vendor.sh). | |
3 | Please DO NOT edit files under `vendor`. | |
4 | ||
5 | ## Update vendor | |
6 | ||
7 | Steps: | |
8 | * Update commits specified in [`vendor.sh`](./vendor.sh). | |
9 | * Rebase `*.patch` under `vendor_patches` if needed (see below). | |
10 | * Run [`vendor.sh`](./vendor.sh). | |
11 | ||
12 | ## Modify `*.patch` | |
13 | ||
14 | Please feel free to replace/add/remove `*.patch` files in `vendor_patches` directory. | |
15 | ||
16 | Steps: | |
17 | * Clone the upstream [libslirp](https://gitlab.freedesktop.org/slirp/libslirp) repo. | |
18 | * Checkout `LIBSLIRP_COMMIT` specified in [`vendor.sh`](./vendor.sh) | |
19 | * Apply patches in this directory (`git am *.patch`). | |
20 | * Commit your own change with `Signed-off-by` line (`git commit -a -s`). See [`https://wiki.qemu.org/Contribute/SubmitAPatch#Patch_emails_must_include_a_Signed-off-by:_line`](https://wiki.qemu.org/Contribute/SubmitAPatch#Patch_emails_must_include_a_Signed-off-by:_line). | |
21 | * Consider melding your change into existing patches if your change is trivial (`git rebase -i ...`). | |
22 | * Run `git format-patch upstream/master` and put the new patch set into this directory. | |
23 | * Run [`vendor.sh`](./vendor.sh). | |
24 | * Open a PR to the slirp4netns repo. | |
25 | ||
26 | Note: We may squash your patch to another patch but we will keep your `Signed-off-by` line. |
0 | 0 | #!/bin/bash |
1 | 1 | set -eux -o pipefail |
2 | # Aug 2, 2019 | |
3 | LIBSLIRP_COMMIT=76462e2f16c6fce6856fb914cbef6207d0be4bc5 | |
4 | LIBSLIRP_REPO=https://gitlab.freedesktop.org/slirp/libslirp.git | |
5 | 2 | |
6 | # Jul 12, 2019 | |
7 | PARSON_COMMIT=c5bb9557fe98367aa8e041c65863909f12ee76b2 | |
3 | # Feb 21, 2020 | |
4 | PARSON_COMMIT=70dc239f8f54c80bf58477b25435fd3dd3102804 | |
8 | 5 | PARSON_REPO=https://github.com/kgabis/parson.git |
9 | 6 | |
10 | 7 | # prepare |
13 | 10 | tmp_git=$tmp/git |
14 | 11 | tmp_vendor=$tmp/vendor |
15 | 12 | mkdir -p $tmp_git $tmp_vendor |
16 | ||
17 | # vendor libslirp | |
18 | git clone $LIBSLIRP_REPO $tmp_git/libslirp | |
19 | ( | |
20 | cd $tmp_git/libslirp | |
21 | git checkout $LIBSLIRP_COMMIT | |
22 | if ls $slirp4netns_root/vendor_patches/libslirp/*.patch >/dev/null; then | |
23 | git am $slirp4netns_root/vendor_patches/libslirp/*.patch | |
24 | fi | |
25 | # run make to generate src/libslirp-version.h | |
26 | make | |
27 | mkdir -p $tmp_vendor/libslirp/src | |
28 | cp -a .clang-format COPYRIGHT README.md $tmp_vendor/libslirp | |
29 | cp -a src/{*.c,*.h} $tmp_vendor/libslirp/src | |
30 | ) | |
31 | 13 | |
32 | 14 | # vendor parson |
33 | 15 | git clone $PARSON_REPO $tmp_git/parson |
43 | 25 | # DO NOT EDIT MANUALLY |
44 | 26 | |
45 | 27 | Vendored components: |
46 | * libslirp: $LIBSLIRP_REPO (\`$LIBSLIRP_COMMIT\`) | |
47 | 28 | * parson: $PARSON_REPO (\`$PARSON_COMMIT\`) |
48 | 29 | |
49 | 30 | EOF |
50 | 31 | |
51 | if ls $slirp4netns_root/vendor_patches/libslirp/*.patch >/dev/null; then | |
52 | cat <<EOF >>$tmp_vendor/README.md | |
53 | Applied patches (sha256sum): | |
54 | \`\`\` | |
55 | $( | |
56 | cd $slirp4netns_root | |
57 | sha256sum vendor_patches/*/* | |
58 | ) | |
59 | \`\`\` | |
60 | ||
61 | EOF | |
62 | fi | |
63 | ||
64 | 32 | cat <<EOF >>$tmp_vendor/README.md |
65 | 33 | Please do not edit the contents under this directory manually. |
66 | 34 | |
67 | See also [\`../vendor.md\`](../vendor.md). | |
35 | Use [\`../vendor.sh\`](../vendor.sh) to update the contents. | |
68 | 36 | EOF |
69 | 37 | |
70 | 38 | # fix up |