aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/tcp/tcp_output.c
blob: dbcf1f74975febd4d52962746e29ec72ed913955 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
# Copyright (c) 2016 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

export WS_ROOT=$(CURDIR)
export BR=$(WS_ROOT)/build-root
CCACHE_DIR?=$(BR)/.ccache
GDB?=gdb
PLATFORM?=vpp
SAMPLE_PLUGIN?=no
MACHINE=$(shell uname -m)

,:=,
define disable_plugins
$(if $(1), \
  "plugins {" \
  $(patsubst %,"plugin %_plugin.so { disable }",$(subst $(,), ,$(1))) \
  " }" \
  ,)
endef

MINIMAL_STARTUP_CONF="							\
unix { 									\
	interactive 							\
	cli-listen /run/vpp/cli.sock					\
	gid $(shell id -g)						\
	$(if $(wildcard startup.vpp),"exec startup.vpp",)		\
}									\
$(if $(DPDK_CONFIG), "dpdk { $(DPDK_CONFIG) }",)			\
$(call disable_plugins,$(DISABLED_PLUGINS))				\
"

GDB_ARGS= -ex "handle SIGUSR1 noprint nostop"

#
# OS Detection
#
# We allow Darwin (MacOS) for docs generation; VPP build will still fail.
ifneq ($(shell uname),Darwin)
OS_ID        = $(shell grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
OS_VERSION_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
endif

ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
PKG=deb
else ifeq ($(filter rhel centos fedora opensuse,$(OS_ID)),$(OS_ID))
PKG=rpm
endif

# +libganglia1-dev if building the gmond plugin

DEB_DEPENDS  = curl build-essential autoconf automake ccache
DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd
DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config
DEB_DEPENDS += lcov chrpath autoconf indent clang-format libnuma-dev
DEB_DEPENDS += python-all python-dev python-virtualenv python-pip libffi6 check
DEB_DEPENDS += libboost-all-dev libffi-dev python-ply
ifeq ($(OS_VERSION_ID),14.04)
	DEB_DEPENDS += openjdk-8-jdk-headless
	DEB_DEPENDS += libssl-dev
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8)
	DEB_DEPENDS += openjdk-8-jdk-headless
	DEB_DEPENDS += libssl-dev
	APT_ARGS = -t jessie-backports
else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-9)
	DEB_DEPENDS += default-jdk-headless
	DEB_DEPENDS += libssl1.0-dev
else 
	DEB_DEPENDS += default-jdk-headless
	DEB_DEPENDS += libssl-dev
endif

RPM_DEPENDS  = redhat-lsb glibc-static java-1.8.0-openjdk-devel yum-utils
RPM_DEPENDS += apr-devel
RPM_DEPENDS += numactl-devel
RPM_DEPENDS += check check-devel
RPM_DEPENDS += boost boost-devel
RPM_DEPENDS += subunit subunit-devel

ifeq ($(OS_ID)-$(OS_VERSION_ID),fedora-25)
	RPM_DEPENDS += openssl-devel
	RPM_DEPENDS += python-devel python2-ply
	RPM_DEPENDS += python2-virtualenv
	RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
else ifeq ($(shell if [ "$(OS_ID)" = "fedora" ]; then test $(OS_VERSION_ID) -gt 25; echo $$?; fi),0)
	RPM_DEPENDS += compat-openssl10-devel
	RPM_DEPENDS += python2-devel python2-ply
	RPM_DEPENDS += python2-virtualenv
	RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
else
	RPM_DEPENDS += openssl-devel
	RPM_DEPENDS += python-devel python-ply
	RPM_DEPENDS += python-virtualenv
	RPM_DEPENDS_GROUPS = 'Development Tools'
endif

# +ganglia-devel if building the ganglia plugin

RPM_DEPENDS += chrpath libffi-devel rpm-build

SUSE_NAME= $(shell grep '^NAME=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | cut -d' ' -f2)
RPM_SUSE_BUILDTOOLS_DEPS = autoconf automake ccache check-devel chrpath
RPM_SUSE_BUILDTOOLS_DEPS += clang indent libtool make python-ply

RPM_SUSE_DEVEL_DEPS = glibc-devel-static java-1_8_0-openjdk-devel libnuma-devel
RPM_SUSE_DEVEL_DEPS += libopenssl-devel openssl-devel

RPM_SUSE_PYTHON_DEPS = python-devel python3-devel python-pip python3-pip
RPM_SUSE_PYTHON_DEPS += python-rpm-macros python3-rpm-macros

RPM_SUSE_PLATFORM_DEPS = distribution-release shadow rpm-build

ifeq ($(OS_ID),opensuse)
ifneq ($(SUSE_NAME),Tumbleweed)
	RPM_SUSE_DEVEL_DEPS += boost_1_61-devel gcc6
	RPM_SUSE_PYTHON_DEPS += python-virtualenv
else
	RPM_SUSE_DEVEL_DEPS = libboost_headers-devel libboost_thread-devel gcc
	RPM_SUSE_PYTHON_DEPS += python2-virtualenv
endif
endif

RPM_SUSE_DEPENDS += $(RPM_SUSE_BUILDTOOLS_DEPS) $(RPM_SUSE_DEVEL_DEPS) $(RPM_SUSE_PYTHON_DEPS) $(RPM_SUSE_PLATFORM_DEPS)

ifneq ($(wildcard $(STARTUP_DIR)/startup.conf),)
        STARTUP_CONF ?= $(STARTUP_DIR)/startup.conf
endif

ifeq ($(findstring y,$(UNATTENDED)),y)
CONFIRM=-y
FORCE=--force-yes
endif

TARGETS = vpp

ifneq ($(SAMPLE_PLUGIN),no)
TARGETS += sample-plugin
endif

.PHONY: help bootstrap wipe wipe-release build build-release rebuild rebuild-release
.PHONY: run run-release debug debug-release build-vat run-vat pkg-deb pkg-rpm
.PHONY: ctags cscope
.PHONY: test test-debug retest retest-debug test-doc test-wipe-doc test-help test-wipe
.PHONY: test-cov test-wipe-cov

help:
	@echo "Make Targets:"
	@echo " bootstrap           - prepare tree for build"
	@echo " install-dep         - install software dependencies"
	@echo " wipe                - wipe all products of debug build "
	@echo " wipe-release        - wipe all products of release build "
	@echo " build               - build debug binaries"
	@echo " build-release       - build release binaries"
	@echo " build-coverity      - build coverity artifacts"
	@echo " rebuild             - wipe and build debug binares"
	@echo " rebuild-release     - wipe and build release binares"
	@echo " run                 - run debug binary"
	@echo " run-release         - run release binary"
	@echo " debug               - run debug binary with debugger"
	@echo " debug-release       - run release binary with debugger"
	@echo " test                - build and run (basic) functional tests"
	@echo " test-debug          - build and run (basic) functional tests (debug build)"
	@echo " test-all            - build and run (all) functional tests"
	@echo " test-all-debug      - build and run (all) functional tests (debug build)"
	@echo " test-shell          - enter shell with test environment"
	@echo " test-shell-debug    - enter shell with test environment (debug build)"
	@echo " test-wipe           - wipe files generated by unit tests"
	@echo " retest              - run functional tests"
	@echo " retest-debug        - run functional tests (debug build)"
	@echo " test-help           - show help on test framework"
	@echo " run-vat             - run vpp-api-test tool"
	@echo " pkg-deb             - build DEB packages"
	@echo " pkg-rpm             - build RPM packages"
	@echo " dpdk-install-dev    - install DPDK development packages"
	@echo " ctags               - (re)generate ctags database"
	@echo " gtags               - (re)generate gtags database"
	@echo " cscope              - (re)generate cscope database"
	@echo " checkstyle          - check coding style"
	@echo " fixstyle            - fix coding style"
	@echo " doxygen             - (re)generate documentation"
	@echo " bootstrap-doxygen   - setup Doxygen dependencies"
	@echo " wipe-doxygen        - wipe all generated documentation"
	@echo " test-doc            - generate documentation for test framework"
	@echo " test-wipe-doc       - wipe documentation for test framework"
	@echo " test-cov            - generate code coverage report for test framework"
	@echo " test-wipe-cov       - wipe code coverage report for test framework"
	@echo " test-checkstyle     - check PEP8 compliance for test framework"
	@echo ""
	@echo "Make Arguments:"
	@echo " V=[0|1]                  - set build verbosity level"
	@echo " STARTUP_CONF=<path>      - startup configuration file"
	@echo "                            (e.g. /etc/vpp/startup.conf)"
	@echo " STARTUP_DIR=<path>       - startup drectory (e.g. /etc/vpp)"
	@echo "                            It also sets STARTUP_CONF if"
	@echo "                            startup.conf file is present"
	@echo " GDB=<path>               - gdb binary to use for debugging"
	@echo " PLATFORM=<name>          - target platform. default is vpp"
	@echo " TEST=<filter>            - apply filter to test set, see test-help"
	@echo " DPDK_CONFIG=<conf>       - add specified dpdk config commands to"
	@echo "                            autogenerated startup.conf"
	@echo "                            (e.g. \"no-pci\" )"
	@echo " SAMPLE_PLUGIN=yes        - in addition build/run/debug sample plugin"
	@echo " DISABLED_PLUGINS=<list>  - comma separated list of plugins which"
	@echo "                            should not be loaded"
	@echo ""
	@echo "Current Argument Values:"
	@echo " V                 = $(V)"
	@echo " STARTUP_CONF      = $(STARTUP_CONF)"
	@echo " STARTUP_DIR       = $(STARTUP_DIR)"
	@echo " GDB               = $(GDB)"
	@echo " PLATFORM          = $(PLATFORM)"
	@echo " DPDK_VERSION      = $(DPDK_VERSION)"
	@echo " DPDK_CONFIG       = $(DPDK_CONFIG)"
	@echo " SAMPLE_PLUGIN     = $(SAMPLE_PLUGIN)"
	@echo " DISABLED_PLUGINS  = $(DISABLED_PLUGINS)"

$(BR)/.bootstrap.ok:
ifeq ($(findstring y,$(UNATTENDED)),y)
	make install-dep
endif
ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
	@MISSING=$$(apt-get install -y -qq -s $(DEB_DEPENDS) | grep "^Inst ") ; \
	if [ -n "$$MISSING" ] ; then \
	  echo "\nPlease install missing packages: \n$$MISSING\n" ; \
	  echo "by executing \"make install-dep\"\n" ; \
	  exit 1 ; \
	fi ; \
	exit 0
else ifneq ("$(wildcard /etc/redhat-release)","")
	@for i in $(RPM_DEPENDS) ; do \
	    RPM=$$(basename -s .rpm "$${i##*/}" | cut -d- -f1,2,3)  ;	\
	    MISSING+=$$(rpm -q $$RPM | grep "^package")	   ;    \
	done							   ;	\
	if [ -n "$$MISSING" ] ; then \
	  echo "Please install missing RPMs: \n$$MISSING\n" ; \
	  echo "by executing \"make install-dep\"\n" ; \
	  exit 1 ; \
	fi ; \
	exit 0
endif
	@echo "SOURCE_PATH = $(WS_ROOT)"                   > $(BR)/build-config.mk
	@echo "#!/bin/bash\n"                              > $(BR)/path_setup
	@echo 'export PATH=$(BR)/tools/ccache-bin:$$PATH' >> $(BR)/path_setup
	@echo 'export PATH=$(BR)/tools/bin:$$PATH'        >> $(BR)/path_setup
	@echo 'export CCACHE_DIR=$(CCACHE_DIR)'           >> $(BR)/path_setup

ifeq ("$(wildcard /usr/bin/ccache )","")
	@echo "WARNING: Please install ccache AYEC and re-run this script"
else
	@rm -rf $(BR)/tools/ccache-bin
	@mkdir -p $(BR)/tools/ccache-bin
	@ln -s /usr/bin/ccache $(BR)/tools/ccache-bin/gcc
	@ln -s /usr/bin/ccache $(BR)/tools/ccache-bin/g++
endif
	@make -C $(BR) V=$(V) is_build_tool=yes tools-install
	@touch $@

bootstrap: $(BR)/.bootstrap.ok

install-dep:
ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
ifeq ($(OS_VERSION_ID),14.04)
	@sudo -E apt-get $(CONFIRM) $(FORCE) install software-properties-common
	@sudo -E add-apt-repository ppa:openjdk-r/ppa $(CONFIRM)
endif
ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8)
	@grep -q jessie-backports /etc/apt/sources.list /etc/apt/sources.list.d/* 2> /dev/null \
           || ( echo "Please install jessie-backports" ; exit 1 )
endif
	@sudo -E apt-get update
	@sudo -E apt-get $(APT_ARGS) $(CONFIRM) $(FORCE) install $(DEB_DEPENDS)
else ifneq ("$(wildcard /etc/redhat-release)","")
	@sudo -E yum groupinstall $(CONFIRM) $(RPM_DEPENDS_GROUPS)
	@sudo -E yum install $(CONFIRM) $(RPM_DEPENDS)
	@sudo -E debuginfo-install $(CONFIRM) glibc openssl-libs zlib
else ifeq ($(filter opensuse,$(OS_ID)),$(OS_ID))
	@sudo -E zypper refresh
	@sudo -E zypper install -y $(RPM_SUSE_DEPENDS)
else
	$(error "This option currently works only on Ubuntu, Debian, Centos or openSUSE systems")
endif

define make
	@make -C $(BR) PLATFORM=$(PLATFORM) TAG=$(1) $(2)
endef

$(BR)/scripts/.version:
ifneq ("$(wildcard /etc/redhat-release)","")
	$(shell $(BR)/scripts/version rpm-string > $(BR)/scripts/.version)
else
	$(shell $(BR)/scripts/version > $(BR)/scripts/.version)
endif

DIST_FILE = $(BR)/vpp-$(shell src/scripts/version).tar
DIST_SUBDIR = vpp-$(shell src/scripts/version|cut -f1 -d-)

dist:
	@if git rev-parse 2> /dev/null ; then \
	    git archive \
	      --prefix=$(DIST_SUBDIR)/ \
	      --format=tar \
	      -o $(DIST_FILE) \
	    HEAD ; \
	    git describe > $(BR)/.version ; \
	else \
	    (cd .. ; tar -cf $(DIST_FILE) $(DIST_SUBDIR) --exclude=*.tar) ; \
	    src/scripts/version > $(BR)/.version ; \
	fi
	@tar --append \
	  --file $(DIST_FILE) \
	  --transform='s,.*/.version,$(DIST_SUBDIR)/src/scripts/.version,' \
	  $(BR)/.version
	@$(RM) $(BR)/.version $(DIST_FILE).xz
	@xz -v --threads=0 $(DIST_FILE)
	@$(RM) $(BR)/vpp-latest.tar.xz
	@ln -rs $(DIST_FILE).xz $(BR)/vpp-latest.tar.xz

build: $(BR)/.bootstrap.ok
	$(call make,$(PLATFORM)_debug,$(addsuffix -install,$(TARGETS)))

wipedist:
	@$(RM) $(BR)/*.tar.xz

wipe: wipedist test-wipe $(BR)/.bootstrap.ok
	$(call make,$(PLATFORM)_debug,$(addsuffix -wipe,$(TARGETS)))

rebuild: wipe build

build-release: $(BR)/.bootstrap.ok
	$(call make,$(PLATFORM),$(addsuffix -install,$(TARGETS)))

wipe-release: test-wipe $(BR)/.bootstrap.ok
	$(call make,$(PLATFORM),$(addsuffix -wipe,$(TARGETS)))

rebuild-release: wipe-release build-release

export VPP_PYTHON_PREFIX=$(BR)/python

libexpand = $(subst $(subst ,, ),:,$(foreach lib,$(1),$(BR)/install-$(2)-native/vpp/$(lib)/$(3)))

define test
	$(if $(filter-out $(3),retest),make -C $(BR) PLATFORM=$(1) TAG=$(2) vpp-install,)
	$(eval libs:=lib lib64)
	make -C test \
	  TEST_DIR=$(WS_ROOT)/test \
	  VPP_TEST_BUILD_DIR=$(BR)/build-$(2)-native \
	  VPP_TEST_BIN=$(BR)/install-$(2)-native/vpp/bin/vpp \
	  VPP_TEST_PLUGIN_PATH=$(call libexpand,$(libs),$(2),vpp_plugins) \
	  VPP_TEST_INSTALL_PATH=$(BR)/install-$(2)-native/ \
	  LD_LIBRARY_PATH=$(call libexpand,$(libs),$(
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/tcp/tcp.h>
#include <vnet/lisp-cp/packets.h>

vlib_node_registration_t tcp4_output_node;
vlib_node_registration_t tcp6_output_node;

typedef enum _tcp_output_nect
{
  TCP_OUTPUT_NEXT_DROP,
  TCP_OUTPUT_NEXT_IP_LOOKUP,
  TCP_OUTPUT_N_NEXT
} tcp_output_next_t;

#define foreach_tcp4_output_next              	\
  _ (DROP, "error-drop")                        \
  _ (IP_LOOKUP, "ip4-lookup")

#define foreach_tcp6_output_next              	\
  _ (DROP, "error-drop")                        \
  _ (IP_LOOKUP, "ip6-lookup")

static char *tcp_error_strings[] = {
#define tcp_error(n,s) s,
#include <vnet/tcp/tcp_error.def>
#undef tcp_error
};

typedef struct
{
  u16 src_port;
  u16 dst_port;
  u8 state;
} tcp_tx_trace_t;

u16 dummy_mtu = 400;

u8 *
format_tcp_tx_trace (u8 * s, va_list * args)
{
  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);

  s = format (s, "TBD\n");

  return s;
}

void
tcp_set_snd_mss (tcp_connection_t * tc)
{
  u16 snd_mss;

  /* TODO find our iface MTU */
  snd_mss = dummy_mtu;

  /* TODO cache mss and consider PMTU discovery */
  snd_mss = tc->opt.mss < snd_mss ? tc->opt.mss : snd_mss;

  tc->snd_mss = snd_mss;

  if (tc->snd_mss == 0)
    {
      clib_warning ("snd mss is 0");
      tc->snd_mss = dummy_mtu;
    }
}

static u8
tcp_window_compute_scale (u32 available_space)
{
  u8 wnd_scale = 0;
  while (wnd_scale < TCP_MAX_WND_SCALE
	 && (available_space >> wnd_scale) > TCP_WND_MAX)
    wnd_scale++;
  return wnd_scale;
}

/**
 * Compute initial window and scale factor. As per RFC1323, window field in
 * SYN and SYN-ACK segments is never scaled.
 */
u32
tcp_initial_window_to_advertise (tcp_connection_t * tc)
{
  u32 available_space;

  /* Initial wnd for SYN. Fifos are not allocated yet.
   * Use some predefined value */
  if (tc->state != TCP_STATE_SYN_RCVD)
    {
      return TCP_DEFAULT_RX_FIFO_SIZE;
    }

  available_space = stream_session_max_enqueue (&tc->connection);
  tc->rcv_wscale = tcp_window_compute_scale (available_space);
  tc->rcv_wnd = clib_min (available_space, TCP_WND_MAX << tc->rcv_wscale);

  return clib_min (tc->rcv_wnd, TCP_WND_MAX);
}

/**
 * Compute and return window to advertise, scaled as per RFC1323
 */
u32
tcp_window_to_advertise (tcp_connection_t * tc, tcp_state_t state)
{
  u32 available_space, wnd, scaled_space;

  if (state != TCP_STATE_ESTABLISHED)
    return tcp_initial_window_to_advertise (tc);

  available_space = stream_session_max_enqueue (&tc->connection);
  scaled_space = available_space >> tc->rcv_wscale;

  /* Need to update scale */
  if (PREDICT_FALSE ((scaled_space == 0 && available_space != 0))
      || (scaled_space >= TCP_WND_MAX))
    tc->rcv_wscale = tcp_window_compute_scale (available_space);

  wnd = clib_min (available_space, TCP_WND_MAX << tc->rcv_wscale);
  tc->rcv_wnd = wnd;

  return wnd >> tc-><
span class="p">(tcp_opts_mss (opts)) { *data++ = TCP_OPTION_MSS; *data++ = TCP_OPTION_LEN_MSS; buf = clib_host_to_net_u16 (opts->mss); clib_memcpy (data, &buf, sizeof (opts->mss)); data += sizeof (opts->mss); opts_len += TCP_OPTION_LEN_MSS; } if (tcp_opts_wscale (opts)) { *data++ = TCP_OPTION_WINDOW_SCALE; *data++ = TCP_OPTION_LEN_WINDOW_SCALE; *data++ = opts->wscale; opts_len += TCP_OPTION_LEN_WINDOW_SCALE; } if (tcp_opts_sack_permitted (opts)) { *data++ = TCP_OPTION_SACK_PERMITTED; *data++ = TCP_OPTION_LEN_SACK_PERMITTED; opts_len += TCP_OPTION_LEN_SACK_PERMITTED; } if (tcp_opts_tstamp (opts)) { *data++ = TCP_OPTION_TIMESTAMP; *data++ = TCP_OPTION_LEN_TIMESTAMP; buf = clib_host_to_net_u32 (opts->tsval); clib_memcpy (data, &buf, sizeof (opts->tsval)); data += sizeof (opts->tsval); buf = clib_host_to_net_u32 (opts->tsecr); clib_memcpy (data, &buf, sizeof (opts->tsecr)); data += sizeof (opts->tsecr); opts_len += TCP_OPTION_LEN_TIMESTAMP; } if (tcp_opts_sack (opts)) { int i; u32 n_sack_blocks = clib_min (vec_len (opts->sacks), TCP_OPTS_MAX_SACK_BLOCKS); if (n_sack_blocks != 0) { *data++ = TCP_OPTION_SACK_BLOCK; *data++ = 2 + n_sack_blocks * TCP_OPTION_LEN_SACK_BLOCK; for (i = 0; i < n_sack_blocks; i++) { buf = clib_host_to_net_u32 (opts->sacks[i].start); clib_memcpy (data, &buf, seq_len); data += seq_len; buf = clib_host_to_net_u32 (opts->sacks[i].end); clib_memcpy (data, &buf, seq_len); data += seq_len; } opts_len += 2 + n_sack_blocks * TCP_OPTION_LEN_SACK_BLOCK; } } /* Terminate TCP options */ if (opts_len % 4) { *data++ = TCP_OPTION_EOL; opts_len += TCP_OPTION_LEN_EOL; } /* Pad with zeroes to a u32 boundary */ while (opts_len % 4) { *data++ = TCP_OPTION_NOOP; opts_len += TCP_OPTION_LEN_NOOP; } return opts_len; } always_inline int tcp_make_syn_options (tcp_options_t * opts, u32 initial_wnd) { u8 len = 0; opts->flags |= TCP_OPTS_FLAG_MSS; opts->mss = dummy_mtu; /*XXX discover that */ len += TCP_OPTION_LEN_MSS; opts->flags |= TCP_OPTS_FLAG_WSCALE; opts->wscale = tcp_window_compute_scale (initial_wnd); len += TCP_OPTION_LEN_WINDOW_SCALE; opts->flags |= TCP_OPTS_FLAG_TSTAMP; opts->tsval = tcp_time_now (); opts->tsecr = 0; len += TCP_OPTION_LEN_TIMESTAMP; opts->flags |= TCP_OPTS_FLAG_SACK_PERMITTED; len += TCP_OPTION_LEN_SACK_PERMITTED; /* Align to needed boundary */ len += (TCP_OPTS_ALIGN - len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN; return len; } always_inline int tcp_make_synack_options (tcp_connection_t * tc, tcp_options_t * opts) { u8 len = 0; opts->flags |= TCP_OPTS_FLAG_MSS; opts->mss = dummy_mtu; /*XXX discover that */ len += TCP_OPTION_LEN_MSS; if (tcp_opts_wscale (&tc->opt)) { opts->flags |= TCP_OPTS_FLAG_WSCALE; opts->wscale = tc->rcv_wscale; len += TCP_OPTION_LEN_WINDOW_SCALE; } if (tcp_opts_tstamp (&tc->opt)) { opts->flags |= TCP_OPTS_FLAG_TSTAMP; opts->tsval = tcp_time_now (); opts->tsecr = tc->tsval_recent; len += TCP_OPTION_LEN_TIMESTAMP; } if (tcp_opts_sack_permitted (&tc->opt)) { opts->flags |= TCP_OPTS_FLAG_SACK_PERMITTED; len += TCP_OPTION_LEN_SACK_PERMITTED; } /* Align to needed boundary */ len += (TCP_OPTS_ALIGN - len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN; return len; } always_inline int tcp_make_established_options (tcp_connection_t * tc, tcp_options_t * opts) { u8 len = 0; opts->flags = 0; if (tcp_opts_tstamp (&tc->opt)) { opts->flags |= TCP_OPTS_FLAG_TSTAMP; opts->tsval = tcp_time_now (); opts->tsecr = tc->tsval_recent; len += TCP_OPTION_LEN_TIMESTAMP; } if (tcp_opts_sack_permitted (&tc->opt)) { if (vec_len (tc->snd_sacks)) { opts->flags |= TCP_OPTS_FLAG_SACK; opts->sacks = tc->snd_sacks; opts->n_sack_blocks = vec_len (tc->snd_sacks); len += 2 + TCP_OPTION_LEN_SACK_BLOCK * opts->n_sack_blocks; } } /* Align to needed boundary */ len += (TCP_OPTS_ALIGN - len % TCP_OPTS_ALIGN) % TCP_OPTS_ALIGN; return len; } always_inline int tcp_make_options (tcp_connection_t * tc, tcp_options_t * opts, tcp_state_t state) { switch (state) { case TCP_STATE_ESTABLISHED: case TCP_STATE_FIN_WAIT_1: return tcp_make_established_options (tc, opts); case TCP_STATE_SYN_RCVD: return tcp_make_synack_options (tc, opts); case TCP_STATE_SYN_SENT: return tcp_make_syn_options (opts, tcp_initial_window_to_advertise (tc)); default: clib_warning ("Not handled!"); return 0; } } #define tcp_get_free_buffer_index(tm, bidx) \ do { \ u32 *my_tx_buffers, n_free_buffers; \ u32 cpu_index = tm->vlib_main->cpu_index; \ my_tx_buffers = tm->tx_buffers[cpu_index]; \ if (PREDICT_FALSE(vec_len (my_tx_buffers) == 0)) \ { \ n_free_buffers = 32; /* TODO config or macro */ \ vec_validate (my_tx_buffers, n_free_buffers - 1); \ _vec_len(my_tx_buffers) = vlib_buffer_alloc_from_free_list ( \ tm->vlib_main, my_tx_buffers, n_free_buffers, \ VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); \ tm->tx_buffers[cpu_index] = my_tx_buffers; \ } \ /* buffer shortage */ \ if (PREDICT_FALSE (vec_len (my_tx_buffers) == 0)) \ return; \ *bidx = my_tx_buffers[_vec_len (my_tx_buffers)-1]; \ _vec_len (my_tx_buffers) -= 1; \ } while (0) always_inline void tcp_reuse_buffer (vlib_main_t * vm, vlib_buffer_t * b) { vlib_buffer_t *it = b; do { it->current_data = 0; it->current_length = 0; it->total_length_not_including_first_buffer = 0; } while ((it->flags & VLIB_BUFFER_NEXT_PRESENT) && (it = vlib_get_buffer (vm, it->next_buffer))); /* Leave enough space for headers */ vlib_buffer_make_headroom (b, MAX_HDRS_LEN); } /** * Prepare ACK */ void tcp_make_ack_i (tcp_connection_t * tc, vlib_buffer_t * b, tcp_state_t state, u8 flags) { tcp_options_t _snd_opts, *snd_opts = &_snd_opts; u8 tcp_opts_len, tcp_hdr_opts_len; tcp_header_t *th; u16 wnd; wnd = tcp_window_to_advertise (tc, state); /* Make and write options */ tcp_opts_len = tcp_make_established_options (tc, snd_opts); tcp_hdr_opts_len = tcp_opts_len + sizeof (tcp_header_t); th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->snd_nxt, tc->rcv_nxt, tcp_hdr_opts_len, flags, wnd); tcp_options_write ((u8 *) (th + 1), snd_opts); /* Mark as ACK */ vnet_buffer (b)->tcp.connection_index = tc->c_c_index; } /** * Convert buffer to ACK */ void tcp_make_ack (tcp_connection_t * tc, vlib_buffer_t * b) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; tcp_reuse_buffer (vm, b); tcp_make_ack_i (tc, b, TCP_STATE_ESTABLISHED, TCP_FLAG_ACK); vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_ACK; } /** * Convert buffer to FIN-ACK */ void tcp_make_finack (tcp_connection_t * tc, vlib_buffer_t * b) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; tcp_reuse_buffer (vm, b); tcp_make_ack_i (tc, b, TCP_STATE_ESTABLISHED, TCP_FLAG_ACK | TCP_FLAG_FIN); /* Reset flags, make sure ack is sent */ tc->flags = TCP_CONN_SNDACK; vnet_buffer (b)->tcp.flags &= ~TCP_BUF_FLAG_DUPACK; tc->snd_nxt += 1; } /** * Convert buffer to SYN-ACK */ void tcp_make_synack (tcp_connection_t * tc, vlib_buffer_t * b) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; tcp_options_t _snd_opts, *snd_opts = &_snd_opts; u8 tcp_opts_len, tcp_hdr_opts_len; tcp_header_t *th; u16 initial_wnd; u32 time_now; memset (snd_opts, 0, sizeof (*snd_opts)); tcp_reuse_buffer (vm, b); /* Set random initial sequence */ time_now = tcp_time_now (); tc->iss = random_u32 (&time_now); tc->snd_una = tc->iss; tc->snd_nxt = tc->iss + 1; tc->snd_una_max = tc->snd_nxt; initial_wnd = tcp_initial_window_to_advertise (tc); /* Make and write options */ tcp_opts_len = tcp_make_synack_options (tc, snd_opts); tcp_hdr_opts_len = tcp_opts_len + sizeof (tcp_header_t); th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->iss, tc->rcv_nxt, tcp_hdr_opts_len, TCP_FLAG_SYN | TCP_FLAG_ACK, initial_wnd); tcp_options_write ((u8 *) (th + 1), snd_opts); vnet_buffer (b)->tcp.connection_index = tc->c_c_index; vnet_buffer (b)->tcp.flags = TCP_BUF_FLAG_ACK; /* Init retransmit timer */ tcp_retransmit_timer_set (tm, tc); } always_inline void tcp_enqueue_to_ip_lookup (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, u8 is_ip4) { u32 *to_next, next_index; vlib_frame_t *f; b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; b->error = 0; /* Default FIB for now */ vnet_buffer (b)->sw_if_index[VLIB_TX] = 0; /* Send to IP lookup */ next_index = is_ip4 ? ip4_lookup_node.index : ip6_lookup_node.index; f = vlib_get_frame_to_node (vm, next_index); /* Enqueue the packet */ to_next = vlib_frame_vector_args (f); to_next[0] = bi; f->n_vectors = 1; vlib_put_frame_to_node (vm, next_index, f); } int tcp_make_reset_in_place (vlib_main_t * vm, vlib_buffer_t * b0, tcp_state_t state, u32 my_thread_index, u8 is_ip4) { u8 tcp_hdr_len = sizeof (tcp_header_t); ip4_header_t *ih4; ip6_header_t *ih6; tcp_header_t *th0; ip4_address_t src_ip40; ip6_address_t src_ip60; u16 src_port0; u32 tmp; /* Find IP and TCP headers */ if (is_ip4) { ih4 = vlib_buffer_get_current (b0); th0 = ip4_next_header (ih4); } else { ih6 = vlib_buffer_get_current (b0); th0 = ip6_next_header (ih6); } /* Swap src and dst ip */ if (is_ip4) { ASSERT ((ih4->ip_version_and_header_length & 0xF0) == 0x40); src_ip40.as_u32 = ih4->src_address.as_u32; ih4->src_address.as_u32 = ih4->dst_address.as_u32; ih4->dst_address.as_u32 = src_ip40.as_u32; /* Chop the end of the pkt */ b0->current_length += ip4_header_bytes (ih4) + tcp_hdr_len; } else { ASSERT ((ih6->ip_version_traffic_class_and_flow_label & 0xF0) == 0x60); clib_memcpy (&src_ip60, &ih6->src_address, sizeof (ip6_address_t)); clib_memcpy (&ih6->src_address, &ih6->dst_address, sizeof (ip6_address_t)); clib_memcpy (&ih6->dst_address, &src_ip60, sizeof (ip6_address_t)); /* Chop the end of the pkt */ b0->current_length += sizeof (ip6_header_t) + tcp_hdr_len; } /* Try to determine what/why we're actually resetting and swap * src and dst ports */ if (state == TCP_STATE_CLOSED) { if (!tcp_syn (th0)) return -1; tmp = clib_net_to_host_u32 (th0->seq_number); /* Got a SYN for no listener. */ th0->flags = TCP_FLAG_RST | TCP_FLAG_ACK; th0->ack_number = clib_host_to_net_u32 (tmp + 1); th0->seq_number = 0; } else if (state >= TCP_STATE_SYN_SENT) { th0->flags = TCP_FLAG_RST | TCP_FLAG_ACK; th0->seq_number = th0->ack_number; th0->ack_number = 0; } src_port0 = th0->src_port; th0->src_port = th0->dst_port; th0->dst_port = src_port0; th0->window = 0; th0->data_offset_and_reserved = (tcp_hdr_len >> 2) << 4; th0->urgent_pointer = 0; /* Compute checksum */ if (is_ip4) { th0->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ih4); } else { int bogus = ~0; th0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ih6, &bogus); ASSERT (!bogus); } return 0; } /** * Send reset without reusing existing buffer */ void tcp_send_reset (vlib_buffer_t * pkt, u8 is_ip4) { vlib_buffer_t *b; u32 bi; tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; u8 tcp_hdr_len, flags = 0; tcp_header_t *th, *pkt_th; u32 seq, ack; ip4_header_t *ih4, *pkt_ih4; ip6_header_t *ih6, *pkt_ih6; tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (vm, bi); /* Leave enough space for headers */ vlib_buffer_make_headroom (b, MAX_HDRS_LEN); /* Make and write options */ tcp_hdr_len = sizeof (tcp_header_t); if (is_ip4) { pkt_ih4 = vlib_buffer_get_current (pkt); pkt_th = ip4_next_header (pkt_ih4); } else { pkt_ih6 = vlib_buffer_get_current (pkt); pkt_th = ip6_next_header (pkt_ih6); } if (tcp_ack (pkt_th)) { flags = TCP_FLAG_RST; seq = pkt_th->ack_number; ack = 0; } else { flags = TCP_FLAG_RST | TCP_FLAG_ACK; seq = 0; ack = clib_host_to_net_u32 (vnet_buffer (pkt)->tcp.seq_end); } th = vlib_buffer_push_tcp_net_order (b, pkt_th->dst_port, pkt_th->src_port, seq, ack, tcp_hdr_len, flags, 0); /* Swap src and dst ip */ if (is_ip4) { ASSERT ((pkt_ih4->ip_version_and_header_length & 0xF0) == 0x40); ih4 = vlib_buffer_push_ip4 (vm, b, &pkt_ih4->dst_address, &pkt_ih4->src_address, IP_PROTOCOL_TCP); th->checksum = ip4_tcp_udp_compute_checksum (vm, b, ih4); } else { int bogus = ~0; pkt_ih6 = (ip6_header_t *) (pkt_th - 1); ASSERT ((pkt_ih6->ip_version_traffic_class_and_flow_label & 0xF0) == 0x60); ih6 = vlib_buffer_push_ip6 (vm, b, &pkt_ih6->dst_address, &pkt_ih6->src_address, IP_PROTOCOL_TCP); th->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ih6, &bogus); ASSERT (!bogus); } tcp_enqueue_to_ip_lookup (vm, b, bi, is_ip4); } void tcp_push_ip_hdr (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b) { tcp_header_t *th = vlib_buffer_get_current (b); if (tc->c_is_ip4) { ip4_header_t *ih; ih = vlib_buffer_push_ip4 (tm->vlib_main, b, &tc->c_lcl_ip4, &tc->c_rmt_ip4, IP_PROTOCOL_TCP); th->checksum = ip4_tcp_udp_compute_checksum (tm->vlib_main, b, ih); } else { ip6_header_t *ih; int bogus = ~0; ih = vlib_buffer_push_ip6 (tm->vlib_main, b, &tc->c_lcl_ip6, &tc->c_rmt_ip6, IP_PROTOCOL_TCP); th->checksum = ip6_tcp_udp_icmp_compute_checksum (tm->vlib_main, b, ih, &bogus); ASSERT (!bogus); } } /** * Send SYN * * Builds a SYN packet for a half-open connection and sends it to ipx_lookup. * The packet is not forwarded through tcpx_output to avoid doing lookups * in the half_open pool. */ void tcp_send_syn (tcp_connection_t * tc) { vlib_buffer_t *b; u32 bi; tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; u8 tcp_hdr_opts_len, tcp_opts_len; tcp_header_t *th; u32 time_now; u16 initial_wnd; tcp_options_t snd_opts; tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (vm, bi); /* Leave enough space for headers */ vlib_buffer_make_headroom (b, MAX_HDRS_LEN); /* Set random initial sequence */ time_now = tcp_time_now (); tc->iss = random_u32 (&time_now); tc->snd_una = tc->iss; tc->snd_una_max = tc->snd_nxt = tc->iss + 1; initial_wnd = tcp_initial_window_to_advertise (tc); /* Make and write options */ memset (&snd_opts, 0, sizeof (snd_opts)); tcp_opts_len = tcp_make_syn_options (&snd_opts, initial_wnd); tcp_hdr_opts_len = tcp_opts_len + sizeof (tcp_header_t); th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->iss, tc->rcv_nxt, tcp_hdr_opts_len, TCP_FLAG_SYN, initial_wnd); tcp_options_write ((u8 *) (th + 1), &snd_opts); /* Measure RTT with this */ tc->rtt_ts = tcp_time_now (); tc->rtt_seq = tc->snd_nxt; /* Start retransmit trimer */ tcp_timer_set (tc, TCP_TIMER_RETRANSMIT_SYN, tc->rto * TCP_TO_TIMER_TICK); tc->rto_boff = 0; /* Set the connection establishment timer */ tcp_timer_set (tc, TCP_TIMER_ESTABLISH, TCP_ESTABLISH_TIME); tcp_push_ip_hdr (tm, tc, b); tcp_enqueue_to_ip_lookup (vm, b, bi, tc->c_is_ip4); } always_inline void tcp_enqueue_to_output (vlib_main_t * vm, vlib_buffer_t * b, u32 bi, u8 is_ip4) { u32 *to_next, next_index; vlib_frame_t *f; b->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; b->error = 0; /* Decide where to send the packet */ next_index = is_ip4 ? tcp4_output_node.index : tcp6_output_node.index; f = vlib_get_frame_to_node (vm, next_index); /* Enqueue the packet */ to_next = vlib_frame_vector_args (f); to_next[0] = bi; f->n_vectors = 1; vlib_put_frame_to_node (vm, next_index, f); } /** * Send FIN */ void tcp_send_fin (tcp_connection_t * tc) { vlib_buffer_t *b; u32 bi; tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (vm, bi); /* Leave enough space for headers */ vlib_buffer_make_headroom (b, MAX_HDRS_LEN); tcp_make_finack (tc, b); tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4); } always_inline u8 tcp_make_state_flags (tcp_state_t next_state) { switch (next_state) { case TCP_STATE_ESTABLISHED: return TCP_FLAG_ACK; case TCP_STATE_SYN_RCVD: return TCP_FLAG_SYN | TCP_FLAG_ACK; case TCP_STATE_SYN_SENT: return TCP_FLAG_SYN; case TCP_STATE_LAST_ACK: case TCP_STATE_FIN_WAIT_1: return TCP_FLAG_FIN; default: clib_warning ("Shouldn't be here!"); } return 0; } /** * Push TCP header and update connection variables */ static void tcp_push_hdr_i (tcp_connection_t * tc, vlib_buffer_t * b, tcp_state_t next_state) { u32 advertise_wnd, data_len; u8 tcp_opts_len, tcp_hdr_opts_len, opts_write_len, flags; tcp_options_t _snd_opts, *snd_opts = &_snd_opts; tcp_header_t *th; data_len = b->current_length; vnet_buffer (b)->tcp.flags = 0; /* Make and write options */ memset (snd_opts, 0, sizeof (*snd_opts)); tcp_opts_len = tcp_make_options (tc, snd_opts, next_state); tcp_hdr_opts_len = tcp_opts_len + sizeof (tcp_header_t); /* Get rcv window to advertise */ advertise_wnd = tcp_window_to_advertise (tc, next_state); flags = tcp_make_state_flags (next_state); /* Push header and options */ th = vlib_buffer_push_tcp (b, tc->c_lcl_port, tc->c_rmt_port, tc->snd_nxt, tc->rcv_nxt, tcp_hdr_opts_len, flags, advertise_wnd); opts_write_len = tcp_options_write ((u8 *) (th + 1), snd_opts); ASSERT (opts_write_len == tcp_opts_len); /* Tag the buffer with the connection index */ vnet_buffer (b)->tcp.connection_index = tc->c_c_index; tc->snd_nxt += data_len; } /* Send delayed ACK when timer expires */ void tcp_timer_delack_handler (u32 index) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; u32 thread_index = os_get_cpu_number (); tcp_connection_t *tc; vlib_buffer_t *b; u32 bi; tc = tcp_connection_get (index, thread_index); /* Get buffer */ tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (vm, bi); /* Fill in the ACK */ tcp_make_ack (tc, b); tc->timers[TCP_TIMER_DELACK] = TCP_TIMER_HANDLE_INVALID; tc->flags &= ~TCP_CONN_DELACK; tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4); } /** Build a retransmit segment * * @return the number of bytes in the segment or 0 if there's nothing to * retransmit * */ u32 tcp_prepare_retransmit_segment (tcp_connection_t * tc, vlib_buffer_t * b, u32 max_bytes) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; u32 n_bytes, offset = 0; sack_scoreboard_hole_t *hole; u32 hole_size; tcp_reuse_buffer (vm, b); ASSERT (tc->state == TCP_STATE_ESTABLISHED); ASSERT (max_bytes != 0); if (tcp_opts_sack_permitted (&tc->opt)) { /* XXX get first hole not retransmitted yet */ hole = scoreboard_first_hole (&tc->sack_sb); if (!hole) return 0; offset = hole->start - tc->snd_una; hole_size = hole->end - hole->start; ASSERT (hole_size); if (hole_size < max_bytes) max_bytes = hole_size; } else { if (seq_geq (tc->snd_nxt, tc->snd_una_max)) return 0; } n_bytes = stream_session_peek_bytes (&tc->connection, vlib_buffer_get_current (b), offset, max_bytes); ASSERT (n_bytes != 0); tc->snd_nxt += n_bytes; tcp_push_hdr_i (tc, b, tc->state); return n_bytes; } static void tcp_timer_retransmit_handler_i (u32 index, u8 is_syn) { tcp_main_t *tm = vnet_get_tcp_main (); vlib_main_t *vm = tm->vlib_main; u32 thread_index = os_get_cpu_number (); tcp_connection_t *tc; vlib_buffer_t *b; u32 bi, max_bytes, snd_space; if (is_syn) { tc = tcp_half_open_connection_get (index); } else { tc = tcp_connection_get (index, thread_index); } /* Make sure timer handle is set to invalid */ tc->timers[TCP_TIMER_RETRANSMIT] = TCP_TIMER_HANDLE_INVALID; /* Increment RTO backoff (also equal to number of retries) */ tc->rto_boff += 1; /* Go back to first un-acked byte */ tc->snd_nxt = tc->snd_una; /* Get buffer */ tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (vm, bi); if (tc->state == TCP_STATE_ESTABLISHED) { tcp_fastrecovery_off (tc); /* Exponential backoff */ tc->rto = clib_min (tc->rto << 1, TCP_RTO_MAX); /* Figure out what and how many bytes we can send */ snd_space = tcp_available_snd_space (tc); max_bytes = clib_min (tc->snd_mss, snd_space); tcp_prepare_retransmit_segment (tc, b, max_bytes); tc->rtx_bytes += max_bytes; /* No fancy recovery for now! */ scoreboard_clear (&tc->sack_sb); } else { /* Retransmit for SYN/SYNACK */ ASSERT (tc->state == TCP_STATE_SYN_RCVD || tc->state == TCP_STATE_SYN_SENT); /* Try without increasing RTO a number of times. If this fails, * start growing RTO exponentially */ if (tc->rto_boff > TCP_RTO_SYN_RETRIES) tc->rto = clib_min (tc->rto << 1, TCP_RTO_MAX); vlib_buffer_make_headroom (b, MAX_HDRS_LEN); tcp_push_hdr_i (tc, b, tc->state); } if (!is_syn) { tcp_enqueue_to_output (vm, b, bi, tc->c_is_ip4); /* Re-enable retransmit timer */ tcp_retransmit_timer_set (tm, tc); } else { ASSERT (tc->state == TCP_STATE_SYN_SENT); /* This goes straight to ipx_lookup */ tcp_push_ip_hdr (tm, tc, b); tcp_enqueue_to_ip_lookup (vm, b, bi, tc->c_is_ip4); /* Re-enable retransmit timer */ tcp_timer_set (tc, TCP_TIMER_RETRANSMIT_SYN, tc->rto * TCP_TO_TIMER_TICK); } } void tcp_timer_retransmit_handler (u32 index) { tcp_timer_retransmit_handler_i (index, 0); } void tcp_timer_retransmit_syn_handler (u32 index) { tcp_timer_retransmit_handler_i (index, 1); } /** * Retansmit first unacked segment */ void tcp_retransmit_first_unacked (tcp_connection_t * tc) { tcp_main_t *tm = vnet_get_tcp_main (); u32 snd_nxt = tc->snd_nxt; vlib_buffer_t *b; u32 bi; tc->snd_nxt = tc->snd_una; /* Get buffer */ tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (tm->vlib_main, bi); tcp_prepare_retransmit_segment (tc, b, tc->snd_mss); tcp_enqueue_to_output (tm->vlib_main, b, bi, tc->c_is_ip4); tc->snd_nxt = snd_nxt; tc->rtx_bytes += tc->snd_mss; } void tcp_fast_retransmit (tcp_connection_t * tc) { tcp_main_t *tm = vnet_get_tcp_main (); u32 snd_space, max_bytes, n_bytes, bi; vlib_buffer_t *b; ASSERT (tcp_in_fastrecovery (tc)); clib_warning ("fast retransmit!"); /* Start resending from first un-acked segment */ tc->snd_nxt = tc->snd_una; snd_space = tcp_available_snd_space (tc); while (snd_space) { tcp_get_free_buffer_index (tm, &bi); b = vlib_get_buffer (tm->vlib_main, bi); max_bytes = clib_min (tc->snd_mss, snd_space); n_bytes = tcp_prepare_retransmit_segment (tc, b, max_bytes); /* Nothing left to retransmit */ if (n_bytes == 0) return; tcp_enqueue_to_output (tm->vlib_main, b, bi, tc->c_is_ip4); snd_space -= n_bytes; } /* If window allows, send new data */ tc->snd_nxt = tc->snd_una_max; } always_inline u32 tcp_session_has_ooo_data (tcp_connection_t * tc) { stream_session_t *s = stream_session_get (tc->c_s_index, tc->c_thread_index); return svm_fifo_has_ooo_data (s->server_rx_fifo); } always_inline uword tcp46_output_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, int is_ip4) { tcp_main_t *tm = vnet_get_tcp_main (); u32 n_left_from, next_index, *from, *to_next; u32 my_thread_index = vm->cpu_index; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; next_index = node->cached_next_index; while (n_left_from > 0) { u32 n_left_to_next; vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; tcp_connection_t *tc0; tcp_header_t *th0; u32 error0 = TCP_ERROR_PKTS_SENT, next0 = TCP_OUTPUT_NEXT_IP_LOOKUP; bi0 = from[0]; to_next[0] = bi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); tc0 = tcp_connection_get (vnet_buffer (b0)->tcp.connection_index, my_thread_index); th0 = vlib_buffer_get_current (b0); if (is_ip4) { ip4_header_t *ih0; ih0 = vlib_buffer_push_ip4 (vm, b0, &tc0->c_lcl_ip4, &tc0->c_rmt_ip4, IP_PROTOCOL_TCP); th0->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ih0); } else { ip6_header_t *ih0; int bogus = ~0; ih0 = vlib_buffer_push_ip6 (vm, b0, &tc0->c_lcl_ip6, &tc0->c_rmt_ip6, IP_PROTOCOL_TCP); th0->checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ih0, &bogus); ASSERT (!bogus); } /* Filter out DUPACKs if there are no OOO segments left */ if (PREDICT_FALSE (vnet_buffer (b0)->tcp.flags & TCP_BUF_FLAG_DUPACK)) { tc0->snt_dupacks--; ASSERT (tc0->snt_dupacks >= 0); if (!tcp_session_has_ooo_data (tc0)) { error0 = TCP_ERROR_FILTERED_DUPACKS; next0 = TCP_OUTPUT_NEXT_DROP; goto done; } } /* Retransmitted SYNs do reach this but it should be harmless */ tc0->rcv_las = tc0->rcv_nxt; /* Stop DELACK timer and fix flags */ tc0->flags &= ~(TCP_CONN_SNDACK | TCP_CONN_DELACK | TCP_CONN_BURSTACK); if (tcp_timer_is_active (tc0, TCP_TIMER_DELACK)) { tcp_timer_reset (tc0, TCP_TIMER_DELACK); } /* If not retransmitting * 1) update snd_una_max (SYN, SYNACK, new data, FIN) * 2) If we're not tracking an ACK, start tracking */ if (seq_lt (tc0->snd_una_max, tc0->snd_nxt)) { tc0->snd_una_max = tc0->snd_nxt; if (tc0->rtt_ts == 0) { tc0->rtt_ts = tcp_time_now (); tc0->rtt_seq = tc0->snd_nxt; } } /* Set the retransmit timer if not set already and not * doing a pure ACK */ if (!tcp_timer_is_active (tc0, TCP_TIMER_RETRANSMIT) && tc0->snd_nxt != tc0->snd_una) { tcp_retransmit_timer_set (tm, tc0); tc0->rto_boff = 0; } /* set fib index to default and lookup node */ /* XXX network virtualization (vrf/vni) */ vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; b0->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; done: b0->error = error0 != 0 ? node->errors[error0] : 0; if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { } vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } return from_frame->n_vectors; } static uword tcp4_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return tcp46_output_inline (vm, node, from_frame, 1 /* is_ip4 */ ); } static uword tcp6_output (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return tcp46_output_inline (vm, node, from_frame, 0 /* is_ip4 */ ); } VLIB_REGISTER_NODE (tcp4_output_node) = { .function = tcp4_output,.name = "tcp4-output", /* Takes a vector of packets. */ .vector_size = sizeof (u32),.n_errors = TCP_N_ERROR,.error_strings = tcp_error_strings,.n_next_nodes = TCP_OUTPUT_N_NEXT,.next_nodes = { #define _(s,n) [TCP_OUTPUT_NEXT_##s] = n, foreach_tcp4_output_next #undef _ } ,.format_buffer = format_tcp_header,.format_trace = format_tcp_tx_trace,}; VLIB_NODE_FUNCTION_MULTIARCH (tcp4_output_node, tcp4_output) VLIB_REGISTER_NODE (tcp6_output_node) = { .function = tcp6_output,.name = "tcp6-output", /* Takes a vector of packets. */ .vector_size = sizeof (u32),.n_errors = TCP_N_ERROR,.error_strings = tcp_error_strings,.n_next_nodes = TCP_OUTPUT_N_NEXT,.next_nodes = { #define _(s,n) [TCP_OUTPUT_NEXT_##s] = n, foreach_tcp6_output_next #undef _ } ,.format_buffer = format_tcp_header,.format_trace = format_tcp_tx_trace,}; VLIB_NODE_FUNCTION_MULTIARCH (tcp6_output_node, tcp6_output) u32 tcp_push_header (transport_connection_t * tconn, vlib_buffer_t * b) { tcp_connection_t *tc; tc = (tcp_connection_t *) tconn; tcp_push_hdr_i (tc, b, TCP_STATE_ESTABLISHED); return 0; } typedef enum _tcp_reset_next { TCP_RESET_NEXT_DROP, TCP_RESET_NEXT_IP_LOOKUP, TCP_RESET_N_NEXT } tcp_reset_next_t; #define foreach_tcp4_reset_next \ _(DROP, "error-drop") \ _(IP_LOOKUP, "ip4-lookup") #define foreach_tcp6_reset_next \ _(DROP, "error-drop") \ _(IP_LOOKUP, "ip6-lookup") static uword tcp46_send_reset_inline (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame, u8 is_ip4) { u32 n_left_from, next_index, *from, *to_next; u32 my_thread_index = vm->cpu_index; from = vlib_frame_vector_args (from_frame); n_left_from = from_frame->n_vectors; next_index = node->cached_next_index; while (n_left_from > 0) { u32 n_left_to_next; vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); while (n_left_from > 0 && n_left_to_next > 0) { u32 bi0; vlib_buffer_t *b0; u32 error0 = TCP_ERROR_RST_SENT, next0 = TCP_RESET_NEXT_IP_LOOKUP; bi0 = from[0]; to_next[0] = bi0; from += 1; to_next += 1; n_left_from -= 1; n_left_to_next -= 1; b0 = vlib_get_buffer (vm, bi0); if (tcp_make_reset_in_place (vm, b0, vnet_buffer (b0)->tcp.flags, my_thread_index, is_ip4)) { error0 = TCP_ERROR_LOOKUP_DROPS; next0 = TCP_RESET_NEXT_DROP; goto done; } /* Prepare to send to IP lookup */ vnet_buffer (b0)->sw_if_index[VLIB_TX] = 0; next0 = TCP_RESET_NEXT_IP_LOOKUP; done: b0->error = error0 != 0 ? node->errors[error0] : 0; b0->flags |= VNET_BUFFER_LOCALLY_ORIGINATED; if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED)) { } vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next, n_left_to_next, bi0, next0); } vlib_put_next_frame (vm, node, next_index, n_left_to_next); } return from_frame->n_vectors; } static uword tcp4_send_reset (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return tcp46_send_reset_inline (vm, node, from_frame, 1); } static uword tcp6_send_reset (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * from_frame) { return tcp46_send_reset_inline (vm, node, from_frame, 0); } /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp4_reset_node) = { .function = tcp4_send_reset, .name = "tcp4-reset", .vector_size = sizeof (u32), .n_errors = TCP_N_ERROR, .error_strings = tcp_error_strings, .n_next_nodes = TCP_RESET_N_NEXT, .next_nodes = { #define _(s,n) [TCP_RESET_NEXT_##s] = n, foreach_tcp4_reset_next #undef _ }, }; /* *INDENT-ON* */ /* *INDENT-OFF* */ VLIB_REGISTER_NODE (tcp6_reset_node) = { .function = tcp6_send_reset, .name = "tcp6-reset", .vector_size = sizeof (u32), .n_errors = TCP_N_ERROR, .error_strings = tcp_error_strings, .n_next_nodes = TCP_RESET_N_NEXT, .next_nodes = { #define _(s,n) [TCP_RESET_NEXT_##s] = n, foreach_tcp6_reset_next #undef _ }, }; /* *INDENT-ON* */ /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */