diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | DESIGN | 99 | ||||
| -rw-r--r-- | README | 12 | ||||
| -rw-r--r-- | man4/opentracker.conf.4 | 11 | ||||
| -rw-r--r-- | opentracker.xcodeproj/project.pbxproj | 320 | ||||
| -rw-r--r-- | ot_socket.c | 3 | ||||
| -rw-r--r-- | tests/accept_filter_probe.py | 110 | ||||
| -rw-r--r-- | tests/integration/test_expiry.py | 7 |
8 files changed, 231 insertions, 334 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | *.o | ||
| 2 | *.dSYM | 1 | *.dSYM |
| 2 | *.o | ||
| 3 | opentracker | 3 | opentracker |
| 4 | opentracker.debug | 4 | opentracker.debug |
| 5 | opentracker.integration | 5 | opentracker.integration |
| @@ -7,4 +7,5 @@ opentracker.tsan | |||
| 7 | proxy | 7 | proxy |
| 8 | proxy.debug | 8 | proxy.debug |
| 9 | tests/runner | 9 | tests/runner |
| 10 | tests/runner.poll | ||
| 10 | tests/runner.sanitize | 11 | tests/runner.sanitize |
| @@ -0,0 +1,99 @@ | |||
| 1 | opentracker design notes | ||
| 2 | ======================== | ||
| 3 | |||
| 4 | Decisions that cost measurement to reach, and the alternatives that were | ||
| 5 | tried and rejected. | ||
| 6 | |||
| 7 | What the tracker is optimised for | ||
| 8 | --------------------------------- | ||
| 9 | |||
| 10 | UDP carries most of the traffic. An HTTP request costs about ten times a UDP | ||
| 11 | announce, 26 microseconds of tracker CPU against 2.5. Nearly all of that is | ||
| 12 | the kernel setting up and tearing down a connection rather than tracker | ||
| 13 | code. | ||
| 14 | |||
| 15 | A connection carries one request and closes. Keep-alive works and is rarely | ||
| 16 | asked for. | ||
| 17 | |||
| 18 | Statistics are approximate on purpose. Counters are per thread and summed | ||
| 19 | when read, so a /stats read can under-report once. Nothing drifts. | ||
| 20 | |||
| 21 | |||
| 22 | Decisions | ||
| 23 | --------- | ||
| 24 | |||
| 25 | One UDP socket per worker, where the kernel spreads datagrams over a group | ||
| 26 | bound to one address. Cost per datagram then stays flat as workers are added, | ||
| 27 | 0.71 to 0.72 microseconds from one worker to eight. Sharing one socket grows | ||
| 28 | it eightfold over the same range, 0.71 to 6.07. | ||
| 29 | |||
| 30 | listen.udp.workers defaults to 1. One worker beats none by 19 to 37 per cent | ||
| 31 | everywhere, because a blocking read costs one system call where the event | ||
| 32 | loop costs two. Beyond one the gain depends on the kernel balancing, so | ||
| 33 | raising it is the operator's decision. | ||
| 34 | |||
| 35 | A thread parked in a blocking read is woken by a signal, not by closing or | ||
| 36 | shutting down its socket: | ||
| 37 | |||
| 38 | mechanism Linux FreeBSD macOS | ||
| 39 | close() blocked blocked 0 ms | ||
| 40 | shutdown(SHUT_RD) 1 ms 1 ms blocked | ||
| 41 | SO_RCVTIMEO works works works | ||
| 42 | pthread_kill 1 ms 1 ms 1 ms | ||
| 43 | |||
| 44 | Only the signal works everywhere. A receive timeout also works, but wakes | ||
| 45 | every thread on every tick for the life of the process to catch something | ||
| 46 | that happens once: at eight workers it doubled idle context switches, 255 to | ||
| 47 | 495 over thirty seconds. | ||
| 48 | |||
| 49 | Each binary is built in one compiler invocation. Without object files a build | ||
| 50 | cannot link objects compiled with different FEATURES, which was a real | ||
| 51 | failure mode. It costs incremental rebuilds, 0.075 seconds becoming 1.56. | ||
| 52 | |||
| 53 | A socket is read or written, never both. http_interest_set replaces an | ||
| 54 | add-and-drop pair so that the invariant cannot be broken by accident, and the | ||
| 55 | event layer hears about a change once instead of twice. | ||
| 56 | |||
| 57 | Accept waits until the client's request has arrived: TCP_DEFER_ACCEPT on | ||
| 58 | Linux, the accf_data filter on FreeBSD. Measured against a flood of silent | ||
| 59 | connections, the filter serves more requests than no filter, not fewer, so it | ||
| 60 | is not the denial of service vector that FreeBSD-SA-02:26 once described. | ||
| 61 | |||
| 62 | |||
| 63 | Paths not taken | ||
| 64 | --------------- | ||
| 65 | |||
| 66 | accf_http, the HTTP-aware accept filter. It duplicates a parser the tracker | ||
| 67 | has anyway, puts more kernel code in the path, and holds a connection that | ||
| 68 | sends "GET /" and stops, which accf_data hands straight over. | ||
| 69 | |||
| 70 | recvmmsg and sendmmsg. No measurable gain on either FreeBSD machine, between | ||
| 71 | -4 and +10 per cent, and they do not exist on Darwin. | ||
| 72 | |||
| 73 | Link time optimisation. It cuts .text by 13 per cent, 93,497 bytes to 81,630, | ||
| 74 | and changes throughput not at all. A tracker waits on memory and system | ||
| 75 | calls, not on instruction fetch: the announce path is 6.6 KB of code against | ||
| 76 | a 32 KB instruction cache at worst. | ||
| 77 | |||
| 78 | A directory for object files. bmake has no pattern rules and the Makefile | ||
| 79 | builds under FreeBSD's make today. Building each binary in one invocation | ||
| 80 | clears the objects out of the tree without giving that up. | ||
| 81 | |||
| 82 | A src/ directory. 52 files sharing an ot_ prefix are a module list rather | ||
| 83 | than clutter, and build output was most of what made the tree look crowded. | ||
| 84 | |||
| 85 | |||
| 86 | Platform differences that cost us code | ||
| 87 | -------------------------------------- | ||
| 88 | |||
| 89 | SO_REUSEPORT means three different things. Linux spreads datagrams over the | ||
| 90 | group; FreeBSD permits the bind and needs SO_REUSEPORT_LB to spread. Darwin | ||
| 91 | hands every datagram to whichever socket bound last, so a group there starves | ||
| 92 | all but one reader. | ||
| 93 | |||
| 94 | Waking a blocked reader differs per kernel. see the table above. | ||
| 95 | |||
| 96 | Deferring accept is TCP_DEFER_ACCEPT, or accf_data, or nothing. | ||
| 97 | |||
| 98 | make is bmake on FreeBSD and GNU make elsewhere, and the Makefile has to | ||
| 99 | build under both. | ||
| @@ -1,14 +1,8 @@ | |||
| 1 | This is opentracker. An open bittorrent tracker. | 1 | This is opentracker. An open bittorrent tracker. |
| 2 | 2 | ||
| 3 | You need libowfat (http://www.fefe.de/libowfat/). | ||
| 4 | |||
| 5 | Steps to go: | 3 | Steps to go: |
| 6 | 4 | ||
| 7 | cvs -d :pserver:cvs@cvs.fefe.de:/cvs -z9 co libowfat | 5 | git clone git://erdgeist.org/opentracker |
| 8 | cd libowfat | ||
| 9 | make | ||
| 10 | cd .. | ||
| 11 | cvs -d:pserver:anoncvs@cvs.erdgeist.org:/home/cvsroot co opentracker | ||
| 12 | cd opentracker | 6 | cd opentracker |
| 13 | make | 7 | make |
| 14 | ./opentracker | 8 | ./opentracker |
| @@ -24,7 +18,3 @@ sysctl kern.ipc.somaxconn=1024 | |||
| 24 | sysctl kern.ipc.nmbclusters=32768 | 18 | sysctl kern.ipc.nmbclusters=32768 |
| 25 | sysctl net.inet.tcp.msl=10000 | 19 | sysctl net.inet.tcp.msl=10000 |
| 26 | sysctl kern.maxfiles=10240 | 20 | sysctl kern.maxfiles=10240 |
| 27 | |||
| 28 | License information: | ||
| 29 | |||
| 30 | Although the libowfat library is under GPL, Felix von Leitner agreed that the compiled binary may be distributed under the same beer ware license as the source code for opentracker. However, we like to hear from happy customers. | ||
diff --git a/man4/opentracker.conf.4 b/man4/opentracker.conf.4 index b28879f..aa82e1d 100644 --- a/man4/opentracker.conf.4 +++ b/man4/opentracker.conf.4 | |||
| @@ -100,6 +100,17 @@ group only where the answer is yes. | |||
| 100 | .Pa tests/udp_scaling.py | 100 | .Pa tests/udp_scaling.py |
| 101 | in the source distribution sweeps the setting against a running tracker. | 101 | in the source distribution sweeps the setting against a running tracker. |
| 102 | Measure on the machine that will carry the traffic. | 102 | Measure on the machine that will carry the traffic. |
| 103 | .Pp | ||
| 104 | On FreeBSD the | ||
| 105 | .Xr accf_data 9 | ||
| 106 | filter has the kernel hold a new connection until data arrives on it, so the | ||
| 107 | tracker is woken once with the request already there. opentracker uses it | ||
| 108 | wherever it is available; enable it with | ||
| 109 | .Cm options ACCEPT_FILTER_DATA | ||
| 110 | in the kernel or | ||
| 111 | .Va kld_list Ns = Ns Qq accf_data | ||
| 112 | in | ||
| 113 | .Xr rc.conf 5 . | ||
| 103 | .Sh EXAMPLES | 114 | .Sh EXAMPLES |
| 104 | To specify the address opentracker will listen on for both TCP and UDP connections: | 115 | To specify the address opentracker will listen on for both TCP and UDP connections: |
| 105 | .Dl listen.tcp_udp 0.0.0.0:6969 | 116 | .Dl listen.tcp_udp 0.0.0.0:6969 |
diff --git a/opentracker.xcodeproj/project.pbxproj b/opentracker.xcodeproj/project.pbxproj deleted file mode 100644 index 3706a3f..0000000 --- a/opentracker.xcodeproj/project.pbxproj +++ /dev/null | |||
| @@ -1,320 +0,0 @@ | |||
| 1 | // !$*UTF8*$! | ||
| 2 | { | ||
| 3 | archiveVersion = 1; | ||
| 4 | classes = { | ||
| 5 | }; | ||
| 6 | objectVersion = 44; | ||
| 7 | objects = { | ||
| 8 | |||
| 9 | /* Begin PBXBuildFile section */ | ||
| 10 | 6520B7530D036AAF00A43B1F /* libowfat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6520B7520D036AAF00A43B1F /* libowfat.a */; }; | ||
| 11 | 653A320C0CE7F475007F0D03 /* ot_accesslist.c in Sources */ = {isa = PBXBuildFile; fileRef = 653A320B0CE7F475007F0D03 /* ot_accesslist.c */; }; | ||
| 12 | 653A56B50CE28EC5000CF140 /* ot_iovec.c in Sources */ = {isa = PBXBuildFile; fileRef = 653A56B40CE28EC5000CF140 /* ot_iovec.c */; }; | ||
| 13 | 654A80890CD832FD009035DE /* opentracker.c in Sources */ = {isa = PBXBuildFile; fileRef = 654A80840CD832FC009035DE /* opentracker.c */; }; | ||
| 14 | 654A808A0CD832FD009035DE /* scan_urlencoded_query.c in Sources */ = {isa = PBXBuildFile; fileRef = 654A80850CD832FC009035DE /* scan_urlencoded_query.c */; }; | ||
| 15 | 654A808B0CD832FD009035DE /* trackerlogic.c in Sources */ = {isa = PBXBuildFile; fileRef = 654A80870CD832FC009035DE /* trackerlogic.c */; }; | ||
| 16 | 65542D8B0CE078E800469330 /* ot_vector.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542D8A0CE078E800469330 /* ot_vector.c */; }; | ||
| 17 | 65542D930CE07CED00469330 /* ot_mutex.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542D8F0CE07CED00469330 /* ot_mutex.c */; }; | ||
| 18 | 65542D940CE07CED00469330 /* ot_stats.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542D910CE07CED00469330 /* ot_stats.c */; }; | ||
| 19 | 65542E750CE08B9100469330 /* ot_clean.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542E740CE08B9100469330 /* ot_clean.c */; }; | ||
| 20 | 65542EE80CE0CA6B00469330 /* ot_udp.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542EE70CE0CA6B00469330 /* ot_udp.c */; }; | ||
| 21 | 65542F920CE17CA900469330 /* ot_fullscrape.c in Sources */ = {isa = PBXBuildFile; fileRef = 65542F910CE17CA900469330 /* ot_fullscrape.c */; }; | ||
| 22 | 65B8DF3C0D0310D20017149E /* ot_http.c in Sources */ = {isa = PBXBuildFile; fileRef = 65B8DF3B0D0310D20017149E /* ot_http.c */; }; | ||
| 23 | 65FA33990E7EF09200F7D5A5 /* ot_livesync.c in Sources */ = {isa = PBXBuildFile; fileRef = 65FA33980E7EF09200F7D5A5 /* ot_livesync.c */; }; | ||
| 24 | 8DD76FB00486AB0100D96B5E /* opentracker.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6A0FF2C0290799A04C91782 /* opentracker.1 */; }; | ||
| 25 | /* End PBXBuildFile section */ | ||
| 26 | |||
| 27 | /* Begin PBXCopyFilesBuildPhase section */ | ||
| 28 | 8DD76FAF0486AB0100D96B5E /* CopyFiles */ = { | ||
| 29 | isa = PBXCopyFilesBuildPhase; | ||
| 30 | buildActionMask = 8; | ||
| 31 | dstPath = /usr/share/man/man1/; | ||
| 32 | dstSubfolderSpec = 0; | ||
| 33 | files = ( | ||
| 34 | 8DD76FB00486AB0100D96B5E /* opentracker.1 in CopyFiles */, | ||
| 35 | ); | ||
| 36 | runOnlyForDeploymentPostprocessing = 1; | ||
| 37 | }; | ||
| 38 | /* End PBXCopyFilesBuildPhase section */ | ||
| 39 | |||
| 40 | /* Begin PBXFileReference section */ | ||
| 41 | 6520B7520D036AAF00A43B1F /* libowfat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libowfat.a; path = ../libowfat/libowfat.a; sourceTree = SOURCE_ROOT; }; | ||
| 42 | 653A320A0CE7F475007F0D03 /* ot_accesslist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_accesslist.h; sourceTree = "<group>"; }; | ||
| 43 | 653A320B0CE7F475007F0D03 /* ot_accesslist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_accesslist.c; sourceTree = "<group>"; }; | ||
| 44 | 653A56AC0CE201FF000CF140 /* opentracker */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = opentracker; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 45 | 653A56B30CE28EC5000CF140 /* ot_iovec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_iovec.h; sourceTree = "<group>"; }; | ||
| 46 | 653A56B40CE28EC5000CF140 /* ot_iovec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_iovec.c; sourceTree = "<group>"; }; | ||
| 47 | 654A80840CD832FC009035DE /* opentracker.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = opentracker.c; sourceTree = "<group>"; }; | ||
| 48 | 654A80850CD832FC009035DE /* scan_urlencoded_query.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scan_urlencoded_query.c; sourceTree = "<group>"; }; | ||
| 49 | 654A80860CD832FC009035DE /* scan_urlencoded_query.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scan_urlencoded_query.h; sourceTree = "<group>"; }; | ||
| 50 | 654A80870CD832FC009035DE /* trackerlogic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trackerlogic.c; sourceTree = "<group>"; }; | ||
| 51 | 654A80880CD832FC009035DE /* trackerlogic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trackerlogic.h; sourceTree = "<group>"; }; | ||
| 52 | 65542D890CE078E800469330 /* ot_vector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_vector.h; sourceTree = "<group>"; }; | ||
| 53 | 65542D8A0CE078E800469330 /* ot_vector.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_vector.c; sourceTree = "<group>"; }; | ||
| 54 | 65542D8F0CE07CED00469330 /* ot_mutex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_mutex.c; sourceTree = "<group>"; }; | ||
| 55 | 65542D900CE07CED00469330 /* ot_mutex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_mutex.h; sourceTree = "<group>"; }; | ||
| 56 | 65542D910CE07CED00469330 /* ot_stats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_stats.c; sourceTree = "<group>"; }; | ||
| 57 | 65542D920CE07CED00469330 /* ot_stats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_stats.h; sourceTree = "<group>"; }; | ||
| 58 | 65542E730CE08B9100469330 /* ot_clean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_clean.h; sourceTree = "<group>"; }; | ||
| 59 | 65542E740CE08B9100469330 /* ot_clean.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_clean.c; sourceTree = "<group>"; }; | ||
| 60 | 65542EE60CE0CA6B00469330 /* ot_udp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_udp.h; sourceTree = "<group>"; }; | ||
| 61 | 65542EE70CE0CA6B00469330 /* ot_udp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_udp.c; sourceTree = "<group>"; }; | ||
| 62 | 65542F900CE17CA900469330 /* ot_fullscrape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_fullscrape.h; sourceTree = "<group>"; }; | ||
| 63 | 65542F910CE17CA900469330 /* ot_fullscrape.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_fullscrape.c; sourceTree = "<group>"; }; | ||
| 64 | 65B8DF3A0D0310D20017149E /* ot_http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_http.h; sourceTree = "<group>"; }; | ||
| 65 | 65B8DF3B0D0310D20017149E /* ot_http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_http.c; sourceTree = "<group>"; }; | ||
| 66 | 65FA33970E7EF09200F7D5A5 /* ot_livesync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ot_livesync.h; sourceTree = "<group>"; }; | ||
| 67 | 65FA33980E7EF09200F7D5A5 /* ot_livesync.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ot_livesync.c; sourceTree = "<group>"; }; | ||
| 68 | C6A0FF2C0290799A04C91782 /* opentracker.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = opentracker.1; sourceTree = "<group>"; }; | ||
| 69 | /* End PBXFileReference section */ | ||
| 70 | |||
| 71 | /* Begin PBXFrameworksBuildPhase section */ | ||
| 72 | 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { | ||
| 73 | isa = PBXFrameworksBuildPhase; | ||
| 74 | buildActionMask = 2147483647; | ||
| 75 | files = ( | ||
| 76 | 6520B7530D036AAF00A43B1F /* libowfat.a in Frameworks */, | ||
| 77 | ); | ||
| 78 | runOnlyForDeploymentPostprocessing = 0; | ||
| 79 | }; | ||
| 80 | /* End PBXFrameworksBuildPhase section */ | ||
| 81 | |||
| 82 | /* Begin PBXGroup section */ | ||
| 83 | 08FB7794FE84155DC02AAC07 /* opentracker */ = { | ||
| 84 | isa = PBXGroup; | ||
| 85 | children = ( | ||
| 86 | 65542D810CE0786F00469330 /* Headers */, | ||
| 87 | 08FB7795FE84155DC02AAC07 /* Source */, | ||
| 88 | 92762AC9104EDED700FDCB60 /* Libraries */, | ||
| 89 | C6A0FF2B0290797F04C91782 /* Documentation */, | ||
| 90 | 653A56AD0CE201FF000CF140 /* Products */, | ||
| 91 | ); | ||
| 92 | name = opentracker; | ||
| 93 | sourceTree = "<group>"; | ||
| 94 | }; | ||
| 95 | 08FB7795FE84155DC02AAC07 /* Source */ = { | ||
| 96 | isa = PBXGroup; | ||
| 97 | children = ( | ||
| 98 | 654A80840CD832FC009035DE /* opentracker.c */, | ||
| 99 | 653A320B0CE7F475007F0D03 /* ot_accesslist.c */, | ||
| 100 | 65542E740CE08B9100469330 /* ot_clean.c */, | ||
| 101 | 65542F910CE17CA900469330 /* ot_fullscrape.c */, | ||
| 102 | 65B8DF3B0D0310D20017149E /* ot_http.c */, | ||
| 103 | 65FA33980E7EF09200F7D5A5 /* ot_livesync.c */, | ||
| 104 | 653A56B40CE28EC5000CF140 /* ot_iovec.c */, | ||
| 105 | 65542D8F0CE07CED00469330 /* ot_mutex.c */, | ||
| 106 | 65542D910CE07CED00469330 /* ot_stats.c */, | ||
| 107 | 65542EE70CE0CA6B00469330 /* ot_udp.c */, | ||
| 108 | 65542D8A0CE078E800469330 /* ot_vector.c */, | ||
| 109 | 654A80850CD832FC009035DE /* scan_urlencoded_query.c */, | ||
| 110 | 654A80870CD832FC009035DE /* trackerlogic.c */, | ||
| 111 | ); | ||
| 112 | name = Source; | ||
| 113 | sourceTree = "<group>"; | ||
| 114 | }; | ||
| 115 | 653A56AD0CE201FF000CF140 /* Products */ = { | ||
| 116 | isa = PBXGroup; | ||
| 117 | children = ( | ||
| 118 | 653A56AC0CE201FF000CF140 /* opentracker */, | ||
| 119 | ); | ||
| 120 | name = Products; | ||
| 121 | sourceTree = "<group>"; | ||
| 122 | }; | ||
| 123 | 65542D810CE0786F00469330 /* Headers */ = { | ||
| 124 | isa = PBXGroup; | ||
| 125 | children = ( | ||
| 126 | 653A320A0CE7F475007F0D03 /* ot_accesslist.h */, | ||
| 127 | 65542E730CE08B9100469330 /* ot_clean.h */, | ||
| 128 | 65542F900CE17CA900469330 /* ot_fullscrape.h */, | ||
| 129 | 65B8DF3A0D0310D20017149E /* ot_http.h */, | ||
| 130 | 653A56B30CE28EC5000CF140 /* ot_iovec.h */, | ||
| 131 | 65FA33970E7EF09200F7D5A5 /* ot_livesync.h */, | ||
| 132 | 65542D900CE07CED00469330 /* ot_mutex.h */, | ||
| 133 | 65542D920CE07CED00469330 /* ot_stats.h */, | ||
| 134 | 65542EE60CE0CA6B00469330 /* ot_udp.h */, | ||
| 135 | 65542D890CE078E800469330 /* ot_vector.h */, | ||
| 136 | 654A80860CD832FC009035DE /* scan_urlencoded_query.h */, | ||
| 137 | 654A80880CD832FC009035DE /* trackerlogic.h */, | ||
| 138 | ); | ||
| 139 | name = Headers; | ||
| 140 | sourceTree = "<group>"; | ||
| 141 | }; | ||
| 142 | 92762AC9104EDED700FDCB60 /* Libraries */ = { | ||
| 143 | isa = PBXGroup; | ||
| 144 | children = ( | ||
| 145 | 6520B7520D036AAF00A43B1F /* libowfat.a */, | ||
| 146 | ); | ||
| 147 | name = Libraries; | ||
| 148 | sourceTree = "<group>"; | ||
| 149 | }; | ||
| 150 | C6A0FF2B0290797F04C91782 /* Documentation */ = { | ||
| 151 | isa = PBXGroup; | ||
| 152 | children = ( | ||
| 153 | C6A0FF2C0290799A04C91782 /* opentracker.1 */, | ||
| 154 | ); | ||
| 155 | name = Documentation; | ||
| 156 | sourceTree = "<group>"; | ||
| 157 | }; | ||
| 158 | /* End PBXGroup section */ | ||
| 159 | |||
| 160 | /* Begin PBXNativeTarget section */ | ||
| 161 | 8DD76FA90486AB0100D96B5E /* opentracker */ = { | ||
| 162 | isa = PBXNativeTarget; | ||
| 163 | buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "opentracker" */; | ||
| 164 | buildPhases = ( | ||
| 165 | 8DD76FAB0486AB0100D96B5E /* Sources */, | ||
| 166 | 8DD76FAD0486AB0100D96B5E /* Frameworks */, | ||
| 167 | 8DD76FAF0486AB0100D96B5E /* CopyFiles */, | ||
| 168 | ); | ||
| 169 | buildRules = ( | ||
| 170 | ); | ||
| 171 | dependencies = ( | ||
| 172 | ); | ||
| 173 | name = opentracker; | ||
| 174 | productInstallPath = "$(HOME)/bin"; | ||
| 175 | productName = opentracker; | ||
| 176 | productReference = 653A56AC0CE201FF000CF140 /* opentracker */; | ||
| 177 | productType = "com.apple.product-type.tool"; | ||
| 178 | }; | ||
| 179 | /* End PBXNativeTarget section */ | ||
| 180 | |||
| 181 | /* Begin PBXProject section */ | ||
| 182 | 08FB7793FE84155DC02AAC07 /* Project object */ = { | ||
| 183 | isa = PBXProject; | ||
| 184 | buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "opentracker" */; | ||
| 185 | compatibilityVersion = "Xcode 3.0"; | ||
| 186 | hasScannedForEncodings = 1; | ||
| 187 | mainGroup = 08FB7794FE84155DC02AAC07 /* opentracker */; | ||
| 188 | productRefGroup = 653A56AD0CE201FF000CF140 /* Products */; | ||
| 189 | projectDirPath = ""; | ||
| 190 | projectRoot = ""; | ||
| 191 | targets = ( | ||
| 192 | 8DD76FA90486AB0100D96B5E /* opentracker */, | ||
| 193 | ); | ||
| 194 | }; | ||
| 195 | /* End PBXProject section */ | ||
| 196 | |||
| 197 | /* Begin PBXSourcesBuildPhase section */ | ||
| 198 | 8DD76FAB0486AB0100D96B5E /* Sources */ = { | ||
| 199 | isa = PBXSourcesBuildPhase; | ||
| 200 | buildActionMask = 2147483647; | ||
| 201 | files = ( | ||
| 202 | 654A80890CD832FD009035DE /* opentracker.c in Sources */, | ||
| 203 | 654A808A0CD832FD009035DE /* scan_urlencoded_query.c in Sources */, | ||
| 204 | 654A808B0CD832FD009035DE /* trackerlogic.c in Sources */, | ||
| 205 | 65542D8B0CE078E800469330 /* ot_vector.c in Sources */, | ||
| 206 | 65542D930CE07CED00469330 /* ot_mutex.c in Sources */, | ||
| 207 | 65542D940CE07CED00469330 /* ot_stats.c in Sources */, | ||
| 208 | 65542E750CE08B9100469330 /* ot_clean.c in Sources */, | ||
| 209 | 65542EE80CE0CA6B00469330 /* ot_udp.c in Sources */, | ||
| 210 | 65542F920CE17CA900469330 /* ot_fullscrape.c in Sources */, | ||
| 211 | 653A56B50CE28EC5000CF140 /* ot_iovec.c in Sources */, | ||
| 212 | 653A320C0CE7F475007F0D03 /* ot_accesslist.c in Sources */, | ||
| 213 | 65B8DF3C0D0310D20017149E /* ot_http.c in Sources */, | ||
| 214 | 65FA33990E7EF09200F7D5A5 /* ot_livesync.c in Sources */, | ||
| 215 | ); | ||
| 216 | runOnlyForDeploymentPostprocessing = 0; | ||
| 217 | }; | ||
| 218 | /* End PBXSourcesBuildPhase section */ | ||
| 219 | |||
| 220 | /* Begin XCBuildConfiguration section */ | ||
| 221 | 1DEB928608733DD80010E9CD /* Debug */ = { | ||
| 222 | isa = XCBuildConfiguration; | ||
| 223 | buildSettings = { | ||
| 224 | COPY_PHASE_STRIP = NO; | ||
| 225 | GCC_DYNAMIC_NO_PIC = NO; | ||
| 226 | GCC_ENABLE_FIX_AND_CONTINUE = YES; | ||
| 227 | GCC_MODEL_TUNING = G5; | ||
| 228 | GCC_OPTIMIZATION_LEVEL = 0; | ||
| 229 | INSTALL_PATH = /usr/local/bin; | ||
| 230 | LIBRARY_SEARCH_PATHS = ( | ||
| 231 | "$(inherited)", | ||
| 232 | "\"$(SRCROOT)/../libowfat\"", | ||
| 233 | ); | ||
| 234 | PRODUCT_NAME = opentracker; | ||
| 235 | ZERO_LINK = YES; | ||
| 236 | }; | ||
| 237 | name = Debug; | ||
| 238 | }; | ||
| 239 | 1DEB928708733DD80010E9CD /* Release */ = { | ||
| 240 | isa = XCBuildConfiguration; | ||
| 241 | buildSettings = { | ||
| 242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||
| 243 | GCC_ENABLE_FIX_AND_CONTINUE = YES; | ||
| 244 | GCC_MODEL_TUNING = G5; | ||
| 245 | INSTALL_PATH = /usr/local/bin; | ||
| 246 | LIBRARY_SEARCH_PATHS = ( | ||
| 247 | "$(inherited)", | ||
| 248 | "\"$(SRCROOT)/../libowfat\"", | ||
| 249 | ); | ||
| 250 | PRODUCT_NAME = opentracker; | ||
| 251 | }; | ||
| 252 | name = Release; | ||
| 253 | }; | ||
| 254 | 1DEB928A08733DD80010E9CD /* Debug */ = { | ||
| 255 | isa = XCBuildConfiguration; | ||
| 256 | buildSettings = { | ||
| 257 | ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; | ||
| 258 | GCC_PREPROCESSOR_DEFINITIONS = ( | ||
| 259 | WANT_ACCESSLIST_WHITE, | ||
| 260 | WANT_IP_FROM_QUERY_STRING, | ||
| 261 | WANT_FULLSCRAPE, | ||
| 262 | ); | ||
| 263 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; | ||
| 264 | GCC_WARN_ABOUT_RETURN_TYPE = YES; | ||
| 265 | GCC_WARN_UNUSED_VARIABLE = YES; | ||
| 266 | HEADER_SEARCH_PATHS = ../libowfat/; | ||
| 267 | LIBRARY_SEARCH_PATHS = ../libowfat/; | ||
| 268 | ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH_ACTUAL)"; | ||
| 269 | PREBINDING = NO; | ||
| 270 | SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.6.sdk"; | ||
| 271 | }; | ||
| 272 | name = Debug; | ||
| 273 | }; | ||
| 274 | 1DEB928B08733DD80010E9CD /* Release */ = { | ||
| 275 | isa = XCBuildConfiguration; | ||
| 276 | buildSettings = { | ||
| 277 | ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; | ||
| 278 | DEAD_CODE_STRIPPING = NO; | ||
| 279 | GCC_PREPROCESSOR_DEFINITIONS = ( | ||
| 280 | WANT_ACCESSLIST_WHITE, | ||
| 281 | WANT_IP_FROM_QUERY_STRING, | ||
| 282 | WANT_FULLSCRAPE, | ||
| 283 | ); | ||
| 284 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; | ||
| 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES; | ||
| 286 | GCC_WARN_UNUSED_VARIABLE = YES; | ||
| 287 | HEADER_SEARCH_PATHS = ../libowfat/; | ||
| 288 | LIBRARY_SEARCH_PATHS = ../libowfat/; | ||
| 289 | ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH_ACTUAL)"; | ||
| 290 | PREBINDING = NO; | ||
| 291 | SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.6.sdk"; | ||
| 292 | ZERO_LINK = NO; | ||
| 293 | }; | ||
| 294 | name = Release; | ||
| 295 | }; | ||
| 296 | /* End XCBuildConfiguration section */ | ||
| 297 | |||
| 298 | /* Begin XCConfigurationList section */ | ||
| 299 | 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "opentracker" */ = { | ||
| 300 | isa = XCConfigurationList; | ||
| 301 | buildConfigurations = ( | ||
| 302 | 1DEB928608733DD80010E9CD /* Debug */, | ||
| 303 | 1DEB928708733DD80010E9CD /* Release */, | ||
| 304 | ); | ||
| 305 | defaultConfigurationIsVisible = 0; | ||
| 306 | defaultConfigurationName = Release; | ||
| 307 | }; | ||
| 308 | 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "opentracker" */ = { | ||
| 309 | isa = XCConfigurationList; | ||
| 310 | buildConfigurations = ( | ||
| 311 | 1DEB928A08733DD80010E9CD /* Debug */, | ||
| 312 | 1DEB928B08733DD80010E9CD /* Release */, | ||
| 313 | ); | ||
| 314 | defaultConfigurationIsVisible = 0; | ||
| 315 | defaultConfigurationName = Release; | ||
| 316 | }; | ||
| 317 | /* End XCConfigurationList section */ | ||
| 318 | }; | ||
| 319 | rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; | ||
| 320 | } | ||
diff --git a/ot_socket.c b/ot_socket.c index 78a16d2..48ffc27 100644 --- a/ot_socket.c +++ b/ot_socket.c | |||
| @@ -130,9 +130,10 @@ int socket_defer_accept(int s) { | |||
| 130 | return setsockopt(s, IPPROTO_TCP, TCP_DEFER_ACCEPT, &patience, sizeof(patience)); | 130 | return setsockopt(s, IPPROTO_TCP, TCP_DEFER_ACCEPT, &patience, sizeof(patience)); |
| 131 | #elif defined(SO_ACCEPTFILTER) | 131 | #elif defined(SO_ACCEPTFILTER) |
| 132 | struct accept_filter_arg filter; | 132 | struct accept_filter_arg filter; |
| 133 | const char name[sizeof(filter.af_name)] = "dataready"; | ||
| 133 | 134 | ||
| 134 | memset(&filter, 0, sizeof(filter)); | 135 | memset(&filter, 0, sizeof(filter)); |
| 135 | strcpy(filter.af_name, "httpready"); | 136 | memcpy(filter.af_name, name, sizeof(name)); |
| 136 | return setsockopt(s, SOL_SOCKET, SO_ACCEPTFILTER, &filter, sizeof(filter)); | 137 | return setsockopt(s, SOL_SOCKET, SO_ACCEPTFILTER, &filter, sizeof(filter)); |
| 137 | #else | 138 | #else |
| 138 | (void)s; | 139 | (void)s; |
diff --git a/tests/accept_filter_probe.py b/tests/accept_filter_probe.py new file mode 100644 index 0000000..22f585a --- /dev/null +++ b/tests/accept_filter_probe.py | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | """What a deferred accept does to connections that never send anything. | ||
| 2 | |||
| 3 | Those connections never reach opentracker, so its own client timeout cannot | ||
| 4 | clean them up: whatever bound exists is the kernel's. This measures it, and | ||
| 5 | then asks the question that follows from it -- whether enough of them keep | ||
| 6 | the tracker from answering anybody else. | ||
| 7 | |||
| 8 | make opentracker | ||
| 9 | python3 tests/accept_filter_probe.py | ||
| 10 | |||
| 11 | On FreeBSD the filter does nothing unless accf_data is loaded, so run it both | ||
| 12 | ways to see whether the numbers differ: | ||
| 13 | |||
| 14 | kldload accf_data | ||
| 15 | """ | ||
| 16 | |||
| 17 | import os | ||
| 18 | import socket | ||
| 19 | import signal | ||
| 20 | import subprocess | ||
| 21 | import sys | ||
| 22 | import tempfile | ||
| 23 | import time | ||
| 24 | import urllib.error | ||
| 25 | import urllib.request | ||
| 26 | |||
| 27 | TRACKER = os.environ.get("OPENTRACKER", "./opentracker") | ||
| 28 | IDLE = int(os.environ.get("IDLE", "300")) | ||
| 29 | PATIENCE = float(os.environ.get("PATIENCE", "90")) | ||
| 30 | HASH = "".join("%%%02x" % b for b in range(1, 21)) | ||
| 31 | |||
| 32 | |||
| 33 | def start(port): | ||
| 34 | directory = tempfile.mkdtemp(prefix="ot-accf-") | ||
| 35 | with open(os.path.join(directory, "conf"), "w") as handle: | ||
| 36 | handle.write("listen.tcp 127.0.0.1:%d\nlisten.udp 127.0.0.1:%d\n" % (port, port)) | ||
| 37 | log = open(os.path.join(directory, "log"), "w+b") | ||
| 38 | tracker = subprocess.Popen([TRACKER, "-f", os.path.join(directory, "conf")], stdout=log, stderr=log) | ||
| 39 | for _ in range(300): | ||
| 40 | try: | ||
| 41 | socket.create_connection(("127.0.0.1", port), timeout=0.2).close() | ||
| 42 | return tracker, log | ||
| 43 | except OSError: | ||
| 44 | time.sleep(0.02) | ||
| 45 | tracker.kill() | ||
| 46 | raise SystemExit("tracker never listened on %d" % port) | ||
| 47 | |||
| 48 | |||
| 49 | def announce(port, timeout=5): | ||
| 50 | """One ordinary request, so we can see whether the tracker still serves.""" | ||
| 51 | started = time.time() | ||
| 52 | try: | ||
| 53 | urllib.request.urlopen( | ||
| 54 | "http://127.0.0.1:%d/announce?info_hash=%s&peer_id=-OT0001-probe0000001&port=6881&left=0" % (port, HASH), | ||
| 55 | timeout=timeout, | ||
| 56 | ).read() | ||
| 57 | return time.time() - started, None | ||
| 58 | except Exception as problem: # noqa: BLE001 - reported, not swallowed | ||
| 59 | return time.time() - started, problem | ||
| 60 | |||
| 61 | |||
| 62 | def main(): | ||
| 63 | port = int(os.environ.get("PORT", "28000")) | ||
| 64 | tracker, log = start(port) | ||
| 65 | try: | ||
| 66 | print(" how long a connection that says nothing is held") | ||
| 67 | quiet = socket.create_connection(("127.0.0.1", port), timeout=5) | ||
| 68 | quiet.settimeout(PATIENCE) | ||
| 69 | started = time.time() | ||
| 70 | try: | ||
| 71 | held = "dropped after %.1f s" % (time.time() - started) if quiet.recv(1) == b"" else "sent us something" | ||
| 72 | except socket.timeout: | ||
| 73 | held = "still open after %.0f s" % PATIENCE | ||
| 74 | except OSError as problem: | ||
| 75 | held = "ended after %.1f s: %s" % (time.time() - started, problem) | ||
| 76 | quiet.close() | ||
| 77 | print(" %s" % held) | ||
| 78 | |||
| 79 | print(" and whether %d of them keep anybody else out" % IDLE) | ||
| 80 | before, problem = announce(port) | ||
| 81 | print(" a request before: %.0f ms%s" % (before * 1000, "" if problem is None else " (%s)" % problem)) | ||
| 82 | |||
| 83 | idlers = [] | ||
| 84 | for _ in range(IDLE): | ||
| 85 | try: | ||
| 86 | idlers.append(socket.create_connection(("127.0.0.1", port), timeout=2)) | ||
| 87 | except OSError as problem: | ||
| 88 | print(" could only open %d of them: %s" % (len(idlers), problem)) | ||
| 89 | break | ||
| 90 | print(" holding %d connections that have sent nothing" % len(idlers)) | ||
| 91 | |||
| 92 | during, problem = announce(port, timeout=10) | ||
| 93 | print(" a request during: %.0f ms%s" % (during * 1000, "" if problem is None else " REFUSED: %s" % problem)) | ||
| 94 | |||
| 95 | for idler in idlers: | ||
| 96 | idler.close() | ||
| 97 | after, problem = announce(port) | ||
| 98 | print(" a request after: %.0f ms%s" % (after * 1000, "" if problem is None else " (%s)" % problem)) | ||
| 99 | finally: | ||
| 100 | tracker.send_signal(signal.SIGINT) | ||
| 101 | try: | ||
| 102 | tracker.wait(timeout=10) | ||
| 103 | except subprocess.TimeoutExpired: | ||
| 104 | tracker.kill() | ||
| 105 | tracker.wait() | ||
| 106 | log.close() | ||
| 107 | |||
| 108 | |||
| 109 | if __name__ == "__main__": | ||
| 110 | main() | ||
diff --git a/tests/integration/test_expiry.py b/tests/integration/test_expiry.py index 5a21e57..5e6dc0a 100644 --- a/tests/integration/test_expiry.py +++ b/tests/integration/test_expiry.py | |||
| @@ -27,6 +27,11 @@ class ExpirySweep(TrackerTestCase): | |||
| 27 | def test_the_sweep_takes_the_idle_and_leaves_the_answered(self): | 27 | def test_the_sweep_takes_the_idle_and_leaves_the_answered(self): |
| 28 | idle = socket.create_connection(("127.0.0.1", self.tracker.port), timeout=5) | 28 | idle = socket.create_connection(("127.0.0.1", self.tracker.port), timeout=5) |
| 29 | try: | 29 | try: |
| 30 | # Enough for a kernel that holds a connection back until data | ||
| 31 | # arrives to hand it over, and far too little to be answered, so | ||
| 32 | # the tracker has accepted it and is waiting for the rest | ||
| 33 | idle.sendall(b"GET /ann") | ||
| 34 | |||
| 30 | self.assertEqual(self.tracker.announce(self.hash_for("expiry-sweep"))[b"complete"], 1) | 35 | self.assertEqual(self.tracker.announce(self.hash_for("expiry-sweep"))[b"complete"], 1) |
| 31 | 36 | ||
| 32 | time.sleep(CLIENT_TIMEOUT + 3 * CHECK_INTERVAL) | 37 | time.sleep(CLIENT_TIMEOUT + 3 * CHECK_INTERVAL) |
| @@ -34,6 +39,6 @@ class ExpirySweep(TrackerTestCase): | |||
| 34 | self.assertEqual(self.tracker.torrent_count(), 1, "the tracker stopped serving while sweeping") | 39 | self.assertEqual(self.tracker.torrent_count(), 1, "the tracker stopped serving while sweeping") |
| 35 | 40 | ||
| 36 | idle.settimeout(5) | 41 | idle.settimeout(5) |
| 37 | self.assertEqual(idle.recv(64), b"", "a connection that never sent a request was not swept") | 42 | self.assertEqual(idle.recv(64), b"", "a connection that went quiet mid-request was not swept") |
| 38 | finally: | 43 | finally: |
| 39 | idle.close() | 44 | idle.close() |
