]> Cypherpunks.ru repositories - nncp.git/commitdiff
Merge branch 'develop' 3.3
authorSergey Matveev <stargrave@stargrave.org>
Sun, 10 Jun 2018 19:28:45 +0000 (22:28 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 10 Jun 2018 19:28:45 +0000 (22:28 +0300)
38 files changed:
VERSION
doc/.gitignore
doc/Makefile
doc/call.texi
doc/cfg.texi
doc/cmds.texi
doc/download.texi
doc/news.ru.texi
doc/news.texi
doc/niceness.texi
doc/sp.texi
doc/sp.txt [new file with mode: 0644]
doc/spool.texi
ports/nncp/Makefile
src/cypherpunks.ru/nncp/call.go
src/cypherpunks.ru/nncp/cfg.go
src/cypherpunks.ru/nncp/cmd/nncp-bundle/main.go
src/cypherpunks.ru/nncp/cmd/nncp-call/main.go
src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go
src/cypherpunks.ru/nncp/cmd/nncp-daemon/main.go
src/cypherpunks.ru/nncp/cmd/nncp-exec/main.go
src/cypherpunks.ru/nncp/cmd/nncp-file/main.go
src/cypherpunks.ru/nncp/cmd/nncp-freq/main.go
src/cypherpunks.ru/nncp/cmd/nncp-pkt/main.go
src/cypherpunks.ru/nncp/cmd/nncp-stat/main.go
src/cypherpunks.ru/nncp/cmd/nncp-toss/main.go
src/cypherpunks.ru/nncp/cmd/nncp-xfer/main.go
src/cypherpunks.ru/nncp/nice.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/nice_test.go [new file with mode: 0644]
src/cypherpunks.ru/nncp/node.go
src/cypherpunks.ru/nncp/pkt.go
src/cypherpunks.ru/nncp/pkt_test.go
src/cypherpunks.ru/nncp/sp.go
src/cypherpunks.ru/nncp/toss.go
src/cypherpunks.ru/nncp/tx.go
src/golang.org/x/crypto
src/golang.org/x/net
src/golang.org/x/sys

diff --git a/VERSION b/VERSION
index a3ec5a4bd3d7209b4a687a77cad49b945339994b..eb39e5382f4f035e4d71c7f67712cdbfa6c0c335 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.2
+3.3
index 8f28565abd0615615140c771a2ac083e18eb3887..a53111bb2957cffb7aa68c6bd4eb9d0add8c384b 100644 (file)
@@ -1,2 +1,3 @@
 nncp.info
 nncp.html
+sp.utxt
index 4e65b0fe549402cc16a91d150962c62e5b3de85b..4e92b3e5a75ea41b6e37985c61de63792d603780 100644 (file)
@@ -2,12 +2,15 @@ all: nncp.info nncp.html
 
 MAKEINFO ?= makeinfo
 
-nncp.info: *.texi pedro.txt
+sp.utxt: sp.txt
+       plantuml -tutxt sp.txt
+
+nncp.info: *.texi sp.utxt pedro.txt
        $(MAKEINFO) -o nncp.info index.texi
 
 CSS != cat style.css
 
-nncp.html: *.texi pedro.txt
+nncp.html: *.texi sp.utxt pedro.txt
        rm -f nncp.html/*.html
        $(MAKEINFO) --html \
                --set-customization-variable CSS_LINES='$(CSS)' \
index 31d8f82ab6aab13cc815904e989f97813a5d0096..edf2d707ac51d6697568b343cf70b2643de57774 100644 (file)
@@ -10,12 +10,14 @@ calls:
   -
     cron: "*/1 * * * MON-FRI"
     onlinedeadline: 3600
-    nice: 64
+    nice: PRIORITY+10
   -
     cron: "30 * * * SAT,SUN"
     onlinedeadline: 1800
     maxonlinetime: 1750
-    nice: 64
+    nice: NORMAL
+    rxrate: 10
+    txrate: 20
   -
     cron: "0 * * * SAT,SUN"
     xx: rx
@@ -167,6 +169,10 @@ Optional. Call only that address, instead of trying all from
 @ref{CfgAddrs, @emph{addrs}} configuration option. It can be either key
 from @emph{addrs} dictionary, or an ordinary @option{addr:port}.
 
+@item rxrate/txrate
+Optional. Override @ref{CfgXxRate, @emph{rxrate/txrate}} configuration
+option when calling.
+
 @item onlinedeadline
 Optional. Override @ref{CfgOnlineDeadline, @emph{onlinedeadline}}
 configuration option when calling.
index 5be0382bd69628ff5980d37b1abe971d548a36db..410bee884e2ef7305ccbff340a3846ab09f43471 100644 (file)
@@ -57,6 +57,8 @@ neigh:
     freqchunked: 1024
     freqminsize: 2048
     via: [alice]
+    rxrate: 10
+    txrate: 20
 @end verbatim
 
 @strong{spool} field contains an absolute path to @ref{Spool, spool}
@@ -143,6 +145,13 @@ pairs pointing to @ref{nncp-daemon}'s listening instance. May be omitted
 if either no direct connection exists, or @ref{nncp-call} is used with
 forced address specifying.
 
+@anchor{CfgXxRate}
+@item rxrate/txrate
+If greater than zero, then at most *rate packets per second will be
+sent/received after the handshake. It could be used as crude bandwidth
+traffic shaper: each packet has at most 64 KiB payload size. Could be
+omitted at all -- no rate limits.
+
 @anchor{CfgOnlineDeadline}
 @item onlinedeadline
 Online connection deadline of node inactivity in seconds. It is the time
index 33ad949dbbd86a49a7997bfe741d65568551a6b2..c5f4607b826e726800a1895633c223ddca634cad 100644 (file)
@@ -17,11 +17,9 @@ Nearly all commands have the following common options:
     will be 4 KiB (containing file itself and some junk).
 @item -nice
     Set desired outgoing packet @ref{Niceness, niceness level}.
-    1-255 values are allowed.
 @item -replynice
     Set desired reply packet @ref{Niceness, niceness level}. Only freq
-    and exec packets look at that niceness level. 1-255 values are
-    allowed.
+    and exec packets look at that niceness level.
 @item -node
     Process only single specified node.
 @item -via
@@ -95,8 +93,13 @@ their integrity.
 @section nncp-call
 
 @verbatim
-% nncp-call [options] [-onlinedeadline INT] [-maxonlinetime INT] [-rx|-tx]
-                      NODE[:ADDR] [FORCEADDR]
+% nncp-call [options]
+    [-onlinedeadline INT]
+    [-maxonlinetime INT]
+    [-rx|-tx]
+    [-rxrate INT]
+    [-txrate INT]
+    NODE[:ADDR] [FORCEADDR]
 @end verbatim
 
 Call (connect to) specified @option{NODE} and run @ref{Sync,
@@ -111,7 +114,8 @@ transmission is performed. If @option{-tx} option is specified, then
 only outbound transmission is performed. @option{-onlinedeadline}
 overrides @ref{CfgOnlineDeadline, @emph{onlinedeadline}}.
 @option{-maxonlinetime} overrides @ref{CfgMaxOnlineTime,
-@emph{maxonlinetime}}.
+@emph{maxonlinetime}}. @option{-rxrate}/@option{-txrate} override
+@ref{CfgXxRate, rxrate/txrate}.
 
 @node nncp-caller
 @section nncp-caller
index fab6f5ab5d85538e77928637c52658a965680955..92b05c94393a7ca133d9b1b8d64e67abb6859a02 100644 (file)
@@ -23,6 +23,10 @@ Tarballs include all necessary required libraries:
 @multitable {XXXXX} {XXXX KiB} {link sign} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
 @headitem Version @tab Size @tab Tarball @tab SHA256 checksum
 
+@item @ref{Release 3.2, 3.2} @tab 1147 KiB
+@tab @url{download/nncp-3.2.tar.xz, link} @url{download/nncp-3.2.tar.xz.sig, sign}
+@tab @code{BE76802F 1E273D1D E91F0648 A7CB23C5 989F5390 A36F2D0C FD873046 51B9141E}
+
 @item @ref{Release 3.1, 3.1} @tab 1145 KiB
 @tab @url{download/nncp-3.1.tar.xz, link} @url{download/nncp-3.1.tar.xz.sig, sign}
 @tab @code{B9344516 4230B58E 8AAADAA2 066F37F2 493CCB71 B025126B BCAD8FAD 6535149F}
index 0cc0e7a37f41cf88de3925e2fd523d4d5ac92dd3..b26a6330418bee0daf85766545f0af783eab9c27 100644 (file)
@@ -1,6 +1,32 @@
 @node Новости
 @section Новости
 
+@node Релиз 3.3
+@subsection Релиз 3.3
+@itemize
+@item
+@command{nncp-daemon}, @command{nncp-call}, @command{nncp-caller}
+проверяют существование @file{.seen} файла и расценивают его как то, что
+файл уже был скачан. Возможно передача данных была осуществлена
+сторонним способом и удалённая сторона должна быть оповещена об этом.
+@item
+Если более высокоприоритетный пакет попадает в спул, то
+@command{nncp-daemon} добавит его в очередь отправки первым, прерывая
+низкоприоритетные передачи.
+@item
+К средствам связанным с online-соединениями (@command{nncp-daemon},
+@command{nncp-call}, @command{nncp-caller}) добавлен простой
+ограничитель скорости.
+@item
+Возможность задания приоритета символьными обозначениями:
+@verb{|NORMAL|}, @verb{|BULK+10|}, @verb{|PRIORITY-5|}, итд.
+@item
+Изменены значения приоритетов по-умолчанию:
+для @command{nncp-exec} с 64 на 96,
+для @command{nncp-freq} с 64 на 160,
+для @command{nncp-file} с 196 на 224.
+@end itemize
+
 @node Релиз 3.2
 @subsection Релиз 3.2
 @itemize
index ae4c1a94abb586bcf46b85a9e85fe470c8747ccb..af44898b4882294350c6df5a0baae99e4c141282 100644 (file)
@@ -3,6 +3,30 @@
 
 See also this page @ref{Новости, on russian}.
 
+@node Release 3.3
+@section Release 3.3
+@itemize
+@item
+@command{nncp-daemon}, @command{nncp-call}, @command{nncp-caller} check
+if @file{.seen} exists and treat it like file was already downloaded.
+Possibly it was transferred out-of-bound and remote side needs to be
+notifier about that.
+@item
+If higher priority packet is spooled, then @command{nncp-daemon} will
+queue its sending first, interrupting lower priority transmissions.
+@item
+Simple packet rate limiter added to online-related tools
+(@command{nncp-daemon}, @command{nncp-call}, @command{nncp-caller}).
+@item
+Ability to specify niceness with symbolic notation:
+@verb{|NORMAL|}, @verb{|BULK+10|}, @verb{|PRIORITY-5|}, итд.
+@item
+Changed default niceness levels:
+for @command{nncp-exec} from 64 to 96,
+for @command{nncp-freq} from 64 to 160,
+for @command{nncp-file} from 196 to 224.
+@end itemize
+
 @node Release 3.2
 @section Release 3.2
 @itemize
index b0f99e9cc206818526090ba07ada52cc5afaedd6..4686c0915436ae1724ea0f71b94ac8e09bfc9cb1 100644 (file)
@@ -6,10 +6,40 @@ command for controlling processes priority. Higher nicer level means
 that packet is "nicer" and allows other to bypass him -- that means
 lower transmission precedence.
 
-Send big files with higher nicer level! That will guarantee you that
+Send big files with higher nice level! That will guarantee you that
 higher priority packets, like mail messages, will pass first, even when
-lower priority packet was already been partly downloaded.
+lower priority packet was already been partially downloaded.
 
 There are default niceness levels built-in for @ref{nncp-exec},
 @ref{nncp-file} and @ref{nncp-freq} commands. But pay attention that it
 can give information about underlying payload to the adversary!
+
+There are 1-255 niceness levels. They could be specified either as
+integer, or using aliases with delta modifiers:
+
+@table @emph
+@item FLASH (F)
+Urgent priority.
+@item PRIORITY (P)
+High priority. Command execution/mail use that priority by default.
+@item NORMAL (N)
+Normal priority. File requests use that priority by default.
+@item BULK (B)
+Bundles shipped on a "least effort" basis. File transmission use that
+priority by default.
+@end table
+
+@verbatim
+ 1: F-31   65: P-31  129: N-31  193: B-31
+ 2: F-30   66: P-30  130: N-30  194: B-30
+    ...        ...        ...        ...
+32: F      96: P     160: N     224: B
+33: F+1    97: P+1   161: N+1   225: B+1
+34: F+2    98: P+2   162: N+2   226: B+2
+    ...        ...        ...        ...
+64: F+32  128: P+32  192: N+32  255: B+31 | MAX
+@end verbatim
+
+Precedence could be specified both with single-letter aliases and with
+whole strings. They are case insensitive. @emph{MAX} is an alias for 255
+niceness level.
index 919cca108c2104bc58a63f06bb70465685a114b1..239b87f1eb6c0d995caa606a88f196a8114e15a6 100644 (file)
@@ -136,36 +136,63 @@ just an unsigned integer telling what body structure follows.
 
 Typical peer's behaviour is following:
 
+@verbatiminclude sp.utxt
+
 @enumerate
-@item Perform Noise-IK handshake.
-@item When remote peer's identity is known (by definition for initiator
-and after receiving first packet for responser (however it is not
-authenticated yet)), then collect all @emph{tx}-related files
-information and prepare payload packets with all that @emph{INFO}s.
-@item Pad the very first payload packet (that is sent with first Noise
-handshake message) with @emph{HALT}s to the maximal size.
-@item Send all queued payload packets.
-@item When @emph{INFO} packet received, check that is has an acceptable
-niceness level (skip if not), check if file's @file{.part} exists and
-queue @emph{FREQ} outgoing packet (with corresponding offset if
-required).
-@item When @emph{FREQ} packet received, append it to current sending
-queue. Sending queue contains files with offsets that are needed to be
+@item Perform @emph{Noise-IK} handshake:
+
+    @table @strong
+    @item Initiator
+    Collects all @emph{tx}-related files information and prepares
+    payload filled with @emph{INFO}s for including in the @strong{first}
+    handshake message.
+    @item Responder
+    After receiving the first handshake message, it gains remote
+    identity knowledge and similarly prepares the payload for including
+    in the @strong{second} handshake message.
+    @end table
+
+    All payloads are padded to maximal message size with @emph{HALT}s.
+
+@item If queued @emph{INFO}s are not sent completely in handshake
+payloads, then send all of remaining in the transport stage.
+
+@item When @emph{INFO} packet received:
+
+    @itemize
+    @item Check that it has an acceptable niceness level.
+    Ignore it if it is too nice.
+    @item If already downloaded file exists, then queue @emph{DONE}
+    sending.
+    @item If @file{.seen} exists, then queue @emph{DONE} sending.
+    @item If @file{.part} exists, then queue @emph{FREQ} sending with
+    corresponding offset.
+    @end itemize
+
+@item When @emph{FREQ} packet received, insert it to current sending
+queue with niceness level sort: higher priority packets will be sent
+first. Sending queue contains files with offsets that are needed to be
 sent.
-@item While sending queue is not empty, send @emph{FILE} packet until
-queue's head is not fully sent. @emph{FREQ} can contain offset equal to
-size -- anyway sent @emph{FILE} packet with an empty payload.
-@item When @emph{FILE} packet received, check if it is not fully
-downloaded (comparing to @emph{INFO}'s packet information). If so, then
-run background integrity checker on it. If check is succeeded, then
+
+@item While sending queue is not empty, send @emph{FILE} packets.
+@emph{FREQ} could contain offset equal to size -- anyway sent
+@emph{FILE} packet with an empty payload. @emph{FILE} sending is
+performed only if no other outgoing packets are queued: @emph{INFO}s
+have higher priority.
+
+@item When @emph{FILE} packet received, check if it is completely
+downloaded (comparing to @emph{INFO}'s packet size information). If so,
+then run background integrity checker on it. If check succeeds, then
 delete @file{.part} suffix from file's name and send @emph{DONE} packet.
+
 @item When @emph{DONE} packet received, delete corresponding file.
 @item When @emph{HALT} packet received, empty file sending queue.
-@item @emph{FILE} sending is performed only if no other outgoing packets
-are queued.
+
 @item Each second, node checks: are there any new @emph{tx} packets
 appeared and queues corresponding @emph{INFO} packets.
+
 @item If no packets are sent and received during @ref{CfgOnlineDeadline,
 onlinedeadline} duration, then close the connection. There is no
 explicit indication that session is over.
+
 @end enumerate
diff --git a/doc/sp.txt b/doc/sp.txt
new file mode 100644 (file)
index 0000000..461c95a
--- /dev/null
@@ -0,0 +1,19 @@
+@startuml
+hide footbox
+participant Initiator
+participant Responder
+
+== preparation ==
+
+Initiator <- Responder : [s]
+
+== interactive ==
+
+Initiator -> Responder : [e, es, s, ss], INFO..., HALT...
+Initiator <- Responder : [e, ee, se], INFO..., HALT...
+Initiator -> Responder : INFO..., FREQ..., DONE...
+Initiator <- Responder : INFO..., FREQ..., DONE...
+Initiator -> Responder : FILE..., INFO..., DONE...
+Initiator <- Responder : FILE..., INFO..., DONE...
+
+@enduml
index 8a4ce6c816b364a9b67e5ba5e4c306bd6a66ea18..c5c516de71ff198098f35e5aeafddac71401ccb0 100644 (file)
@@ -21,16 +21,16 @@ spool/BYRR...CG6Q/tx/ZI5U...5RRQ
 
 Except for @file{tmp}, all other directories are Base32-encoded node
 identifiers (@file{2WHB...OABQ}, @file{BYRR...CG6Q} in our example).
-Each node subdirectory has @file{rx} (received, partly received and
+Each node subdirectory has @file{rx} (received, partially received and
 currently unprocessed packets) and @file{tx} (for outbound packets)
 directories.
 
 Each @file{rx}/@file{tx} directory contains one file per encrypted
 packet. Its filename is Base32 encoded BLAKE2b hash of the contents. So
 it can be integrity checked at any time. @file{5ZIB...UMKW.part} is
-partly received file from @file{2WHB...OABQ} node. @file{tx} directory
-can not contain partly written files -- they are moved atomically from
-@file{tmp}.
+partially received file from @file{2WHB...OABQ} node. @file{tx}
+directory can not contain partially written files -- they are moved
+atomically from @file{tmp}.
 
 When @ref{nncp-toss} utility is called with @option{-seen} option, it
 will create empty @file{XXX.seen} files, telling that some kind of
index 971db0b504cfaf24db7d55aba98677ec3d07dac1..3a494907d793f1fc3605500e8924bb63bf639256 100644 (file)
@@ -1,12 +1,12 @@
-# $FreeBSD: head/net/nncp/Makefile 460314 2018-01-29 16:17:45Z yuri $
+# $FreeBSD: head/net/nncp/Makefile 471003 2018-05-27 20:24:00Z krion $
 
 PORTNAME=      nncp
-DISTVERSION=   3.1
+DISTVERSION=   3.3
 CATEGORIES=    net
 MASTER_SITES=  http://www.nncpgo.org/download/
 
 MAINTAINER=    stargrave@stargrave.org
-COMMENT=       Utilities for secure store-and-forward files, mail and command exchanging
+COMMENT=       Utilities for secure store-and-forward files, mail, command exchanging
 
 LICENSE=       GPLv3+
 LICENSE_FILE=  ${WRKSRC}/COPYING
index ab120ad890c69e824db3a66ce8bfcdc3d258db1b..6a2c38b740870f249c05162732b3b059c0adba20 100644 (file)
@@ -21,9 +21,28 @@ package nncp
 import (
        "net"
        "strconv"
+
+       "github.com/gorhill/cronexpr"
 )
 
-func (ctx *Ctx) CallNode(node *Node, addrs []string, nice uint8, xxOnly TRxTx, onlineDeadline, maxOnlineTime uint) (isGood bool) {
+type Call struct {
+       Cron           *cronexpr.Expression
+       Nice           uint8
+       Xx             TRxTx
+       RxRate         int
+       TxRate         int
+       Addr           *string
+       OnlineDeadline uint
+       MaxOnlineTime  uint
+}
+
+func (ctx *Ctx) CallNode(
+       node *Node,
+       addrs []string,
+       nice uint8,
+       xxOnly TRxTx,
+       rxRate, txRate int,
+       onlineDeadline, maxOnlineTime uint) (isGood bool) {
        for _, addr := range addrs {
                sds := SDS{"node": node.Id, "addr": addr}
                ctx.LogD("call", sds, "dialing")
@@ -38,6 +57,8 @@ func (ctx *Ctx) CallNode(node *Node, addrs []string, nice uint8, xxOnly TRxTx, o
                        node.Id,
                        nice,
                        xxOnly,
+                       rxRate,
+                       txRate,
                        onlineDeadline,
                        maxOnlineTime,
                )
index b75196175e429b89f7408789fd932077bafd8cfd..75d12a54fb33d63837d4037b37bdb7d56e914cfe 100644 (file)
@@ -48,28 +48,32 @@ type NodeYAML struct {
        Id          string
        ExchPub     string
        SignPub     string
-       NoisePub    *string             `noisepub,omitempty`
-       Exec        map[string][]string `exec,omitempty`
-       Incoming    *string             `incoming,omitempty`
-       Freq        *string             `freq,omitempty`
-       FreqChunked *uint64             `freqchunked,omitempty`
-       FreqMinSize *uint64             `freqminsize,omitempty`
-       Via         []string            `via,omitempty`
-       Calls       []CallYAML          `calls,omitempty`
-
-       Addrs map[string]string `addrs,omitempty`
-
-       OnlineDeadline *uint `onlinedeadline,omitempty`
-       MaxOnlineTime  *uint `maxonlinetime,omitempty`
+       NoisePub    *string             `yaml:"noisepub,omitempty"`
+       Exec        map[string][]string `yaml:"exec,omitempty"`
+       Incoming    *string             `yaml:"incoming,omitempty"`
+       Freq        *string             `yaml:"freq,omitempty"`
+       FreqChunked *uint64             `yaml:"freqchunked,omitempty"`
+       FreqMinSize *uint64             `yaml:"freqminsize,omitempty"`
+       Via         []string            `yaml:"via,omitempty"`
+       Calls       []CallYAML          `yaml:"calls,omitempty"`
+
+       Addrs map[string]string `yaml:"addrs,omitempty"`
+
+       RxRate         *int  `yaml:"rxrate,omitempty"`
+       TxRate         *int  `yaml:"txrate,omitempty"`
+       OnlineDeadline *uint `yaml:"onlinedeadline,omitempty"`
+       MaxOnlineTime  *uint `yaml:"maxonlinetime,omitempty"`
 }
 
 type CallYAML struct {
        Cron           string
-       Nice           *int    `nice,omitempty`
-       Xx             string  `xx,omitempty`
-       Addr           *string `addr,omitempty`
-       OnlineDeadline *uint   `onlinedeadline,omitempty`
-       MaxOnlineTime  *uint   `maxonlinetime,omitempty`
+       Nice           *string `yaml:"nice,omitempty"`
+       Xx             string  `yaml:"xx,omitempty"`
+       RxRate         *int    `yaml:"rxrate,omitempty"`
+       TxRate         *int    `yaml:"txrate,omitempty"`
+       Addr           *string `yaml:"addr,omitempty"`
+       OnlineDeadline *uint   `yaml:"onlinedeadline,omitempty"`
+       MaxOnlineTime  *uint   `yaml:"maxonlinetime,omitempty"`
 }
 
 type NodeOurYAML struct {
@@ -88,17 +92,17 @@ type FromToYAML struct {
 }
 
 type NotifyYAML struct {
-       File *FromToYAML `file,omitempty`
-       Freq *FromToYAML `freq,omitempty`
+       File *FromToYAML `yaml:"file,omitempty"`
+       Freq *FromToYAML `yaml:"freq,omitempty"`
 }
 
 type CfgYAML struct {
-       Self  *NodeOurYAML `self,omitempty`
+       Self  *NodeOurYAML `yaml:"self,omitempty"`
        Neigh map[string]NodeYAML
 
        Spool  string
        Log    string
-       Notify *NotifyYAML `notify,omitempty`
+       Notify *NotifyYAML `yaml:"notify,omitempty"`
 }
 
 func NewNode(name string, yml NodeYAML) (*Node, error) {
@@ -163,6 +167,15 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                freqMinSize = int64(*yml.FreqMinSize) * 1024
        }
 
+       defRxRate := 0
+       if yml.RxRate != nil && *yml.RxRate > 0 {
+               defRxRate = *yml.RxRate
+       }
+       defTxRate := 0
+       if yml.TxRate != nil && *yml.TxRate > 0 {
+               defTxRate = *yml.TxRate
+       }
+
        defOnlineDeadline := uint(DefaultDeadline)
        if yml.OnlineDeadline != nil {
                if *yml.OnlineDeadline <= 0 {
@@ -181,13 +194,15 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                if err != nil {
                        return nil, err
                }
+
                nice := uint8(255)
                if callYml.Nice != nil {
-                       if *callYml.Nice < 1 || *callYml.Nice > 255 {
-                               return nil, errors.New("Nice must be between 1 and 255")
+                       nice, err = NicenessParse(*callYml.Nice)
+                       if err != nil {
+                               return nil, err
                        }
-                       nice = uint8(*callYml.Nice)
                }
+
                var xx TRxTx
                switch callYml.Xx {
                case "rx":
@@ -198,6 +213,16 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                default:
                        return nil, errors.New("xx field must be either \"rx\" or \"tx\"")
                }
+
+               rxRate := 0
+               if callYml.RxRate != nil && *callYml.RxRate > 0 {
+                       rxRate = *callYml.RxRate
+               }
+               txRate := 0
+               if callYml.TxRate != nil && *callYml.TxRate > 0 {
+                       txRate = *callYml.TxRate
+               }
+
                var addr *string
                if callYml.Addr != nil {
                        if a, exists := yml.Addrs[*callYml.Addr]; exists {
@@ -206,6 +231,7 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                                addr = callYml.Addr
                        }
                }
+
                onlineDeadline := defOnlineDeadline
                if callYml.OnlineDeadline != nil {
                        if *callYml.OnlineDeadline == 0 {
@@ -213,14 +239,18 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                        }
                        onlineDeadline = *callYml.OnlineDeadline
                }
+
                var maxOnlineTime uint
                if callYml.MaxOnlineTime != nil {
                        maxOnlineTime = *callYml.MaxOnlineTime
                }
+
                calls = append(calls, &Call{
                        Cron:           expr,
                        Nice:           nice,
                        Xx:             xx,
+                       RxRate:         rxRate,
+                       TxRate:         txRate,
                        Addr:           addr,
                        OnlineDeadline: onlineDeadline,
                        MaxOnlineTime:  maxOnlineTime,
@@ -239,6 +269,8 @@ func NewNode(name string, yml NodeYAML) (*Node, error) {
                FreqMinSize:    freqMinSize,
                Calls:          calls,
                Addrs:          yml.Addrs,
+               RxRate:         defRxRate,
+               TxRate:         defTxRate,
                OnlineDeadline: defOnlineDeadline,
                MaxOnlineTime:  defMaxOnlineTime,
        }
index 1e5f8f2b190f9dd1b288597a391fcc7aad6158ea..86dc099843bb578080eb0e2e8be471aef033caa7 100644 (file)
@@ -55,7 +55,7 @@ func usage() {
 func main() {
        var (
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                doRx      = flag.Bool("rx", false, "Receive packets")
                doTx      = flag.Bool("tx", false, "Transfer packets")
                doDelete  = flag.Bool("delete", false, "Delete transferred packets")
@@ -78,10 +78,10 @@ func main() {
                fmt.Println(nncp.VersionGet())
                return
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
        if *doRx && *doTx {
                log.Fatalln("-rx and -tx can not be set simultaneously")
        }
index bbf9bfa2ff3671c63502cc117af56dc67377d0a3..d8adc60001c046c2fb26c9b2a9d2c57497fe78d2 100644 (file)
@@ -40,9 +40,11 @@ func usage() {
 func main() {
        var (
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                rxOnly    = flag.Bool("rx", false, "Only receive packets")
-               txOnly    = flag.Bool("tx", false, "Only transfer packets")
+               txOnly    = flag.Bool("tx", false, "Only transmit packets")
+               rxRate    = flag.Int("rxrate", 0, "Maximal receive rate, pkts/sec")
+               txRate    = flag.Int("txrate", 0, "Maximal transmit rate, pkts/sec")
                spoolPath = flag.String("spool", "", "Override path to spool")
                logPath   = flag.String("log", "", "Override path to logfile")
                quiet     = flag.Bool("quiet", false, "Print only errors")
@@ -67,10 +69,10 @@ func main() {
                usage()
                os.Exit(1)
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
        if *rxOnly && *txOnly {
                log.Fatalln("-rx and -tx can not be set simultaneously")
        }
@@ -121,7 +123,16 @@ func main() {
                }
        }
 
-       if !ctx.CallNode(node, addrs, nice, xxOnly, *onlineDeadline, *maxOnlineTime) {
+       if !ctx.CallNode(
+               node,
+               addrs,
+               nice,
+               xxOnly,
+               *rxRate,
+               *txRate,
+               *onlineDeadline,
+               *maxOnlineTime,
+       ) {
                os.Exit(1)
        }
 }
index a975878cb4066635bd16aed4adb0e673ec61a71f..d9609420b2bb46a330b9c4e90016aa0ba88760e2 100644 (file)
@@ -128,6 +128,8 @@ func main() {
                                                        addrs,
                                                        call.Nice,
                                                        call.Xx,
+                                                       call.RxRate,
+                                                       call.TxRate,
                                                        call.OnlineDeadline,
                                                        call.MaxOnlineTime,
                                                )
index 1ea30de168b8fb3300adb7dd41a0eb52cef917cd..ba2d602c005f0ebf9bc46fb6774f01afa2db37ef 100644 (file)
@@ -41,7 +41,7 @@ func usage() {
 func main() {
        var (
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                bind      = flag.String("bind", "[::]:5400", "Address to bind to")
                maxConn   = flag.Int("maxconn", 128, "Maximal number of simultaneous connections")
                spoolPath = flag.String("spool", "", "Override path to spool")
@@ -61,10 +61,10 @@ func main() {
                fmt.Println(nncp.VersionGet())
                return
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
 
        ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
index 3a5328dc009a1e46c1e277696257771fc461a203..0cf781063ccc3e22b9e422d847528c53a561220c 100644 (file)
@@ -40,8 +40,8 @@ func usage() {
 func main() {
        var (
                cfgPath      = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw      = flag.Int("nice", nncp.DefaultNiceExec, "Outbound packet niceness")
-               replyNiceRaw = flag.Int("replynice", nncp.DefaultNiceFile, "Possible reply packet niceness")
+               niceRaw      = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceExec), "Outbound packet niceness")
+               replyNiceRaw = flag.String("replynice", nncp.NicenessFmt(nncp.DefaultNiceFile), "Possible reply packet niceness")
                minSize      = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB")
                viaOverride  = flag.String("via", "", "Override Via path to destination node")
                spoolPath    = flag.String("spool", "", "Override path to spool")
@@ -65,14 +65,14 @@ func main() {
                usage()
                os.Exit(1)
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
-       if *replyNiceRaw < 1 || *replyNiceRaw > 255 {
-               log.Fatalln("-replynice must be between 1 and 255")
+       replyNice, err := nncp.NicenessParse(*replyNiceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       replyNice := uint8(*replyNiceRaw)
 
        ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
index e19eb9365463a52634e51231e1f734c8c925e71d..6e7eaf5ab8fabbb2a888e2647350b2ac21b54780 100644 (file)
@@ -45,7 +45,7 @@ options by default. You can forcefully turn them off by specifying 0 value.
 func main() {
        var (
                cfgPath      = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw      = flag.Int("nice", nncp.DefaultNiceFile, "Outbound packet niceness")
+               niceRaw      = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceFile), "Outbound packet niceness")
                argMinSize   = flag.Int64("minsize", -1, "Minimal required resulting packet size, in KiB")
                argChunkSize = flag.Int64("chunked", -1, "Split file on specified size chunks, in KiB")
                viaOverride  = flag.String("via", "", "Override Via path to destination node")
@@ -70,10 +70,10 @@ func main() {
                usage()
                os.Exit(1)
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
 
        ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
index 1c8e248cb60ce27a1f2c7e0be57566ad778a3b4d..14f2fea68ed0970d8dd0ca7a20d537d4ec2e5bb3 100644 (file)
@@ -25,6 +25,7 @@ import (
        "log"
        "os"
        "path/filepath"
+       "strconv"
        "strings"
 
        "cypherpunks.ru/nncp"
@@ -40,8 +41,8 @@ func usage() {
 func main() {
        var (
                cfgPath      = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
-               niceRaw      = flag.Int("nice", nncp.DefaultNiceFreq, "Outbound packet niceness")
-               replyNiceRaw = flag.Int("replynice", nncp.DefaultNiceFile, "Reply file packet niceness")
+               niceRaw      = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceFreq), "Outbound packet niceness")
+               replyNiceRaw = flag.String("replynice", strconv.Itoa(nncp.DefaultNiceFile), "Reply file packet niceness")
                minSize      = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB")
                viaOverride  = flag.String("via", "", "Override Via path to destination node")
                spoolPath    = flag.String("spool", "", "Override path to spool")
@@ -65,14 +66,14 @@ func main() {
                usage()
                os.Exit(1)
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
-       if *replyNiceRaw < 1 || *replyNiceRaw > 255 {
-               log.Fatalln("-replynice must be between 1 and 255")
+       replyNice, err := nncp.NicenessParse(*replyNiceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       replyNice := uint8(*replyNiceRaw)
 
        ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
index f4de406a4b17eab7eb6b22cff66849d476cdc9d8..ff77eb45195ac188be4a0872aeaa5ddb3768ca47 100644 (file)
@@ -114,8 +114,8 @@ func main() {
                        path = string(pkt.Path[:pkt.PathLen])
                }
                fmt.Printf(
-                       "Packet type: plain\nPayload type: %s\nNiceness: %d\nPath: %s\n",
-                       payloadType, pkt.Nice, path,
+                       "Packet type: plain\nPayload type: %s\nNiceness: %s (%d)\nPath: %s\n",
+                       payloadType, nncp.NicenessFmt(pkt.Nice), pkt.Nice, path,
                )
                return
        }
@@ -148,8 +148,8 @@ func main() {
                        return
                }
                fmt.Printf(
-                       "Packet type: encrypted\nNiceness: %d\nSender: %s\nRecipient: %s\n",
-                       pktEnc.Nice, pktEnc.Sender, pktEnc.Recipient,
+                       "Packet type: encrypted\nNiceness: %s (%d)\nSender: %s\nRecipient: %s\n",
+                       nncp.NicenessFmt(pktEnc.Nice), pktEnc.Nice, pktEnc.Sender, pktEnc.Recipient,
                )
                return
        }
index 94a0bb1fd2557959393041c7f6bf86ad5c084d11..fcc872f7333d6d253210642384a7b8baf48fe761 100644 (file)
@@ -99,18 +99,19 @@ func main() {
                        txBytes[job.PktEnc.Nice] = txBytes[job.PktEnc.Nice] + job.Size
                }
                fmt.Println(node.Name)
-               for nice := 0; nice < 256; nice++ {
-                       rxNum, rxExists := rxNums[uint8(nice)]
-                       txNum, txExists := txNums[uint8(nice)]
+               var nice uint8
+               for nice = 1; nice > 0; nice++ {
+                       rxNum, rxExists := rxNums[nice]
+                       txNum, txExists := txNums[nice]
                        if !(rxExists || txExists) {
                                continue
                        }
                        fmt.Printf(
-                               "\tnice:% 3d | Rx: % 10s, % 3d pkts | Tx: % 10s, % 3d pkts\n",
-                               nice,
-                               humanize.IBytes(uint64(rxBytes[uint8(nice)])),
+                               "\tnice:% 4s | Rx: % 10s, % 3d pkts | Tx: % 10s, % 3d pkts\n",
+                               nncp.NicenessFmt(nice),
+                               humanize.IBytes(uint64(rxBytes[nice])),
                                rxNum,
-                               humanize.IBytes(uint64(txBytes[uint8(nice)])),
+                               humanize.IBytes(uint64(txBytes[nice])),
                                txNum,
                        )
                }
index cc3ca67fb57e6afd6a215d66d634b833e9aee098..bb4a58684c710e41012185632e5e285bf048a392 100644 (file)
@@ -40,7 +40,7 @@ func main() {
        var (
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
                nodeRaw   = flag.String("node", "", "Process only that node")
-               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                dryRun    = flag.Bool("dryrun", false, "Do not actually write any tossed data")
                doSeen    = flag.Bool("seen", false, "Create .seen files")
                cycle     = flag.Uint("cycle", 0, "Repeat tossing after N seconds in infinite loop")
@@ -65,10 +65,10 @@ func main() {
                fmt.Println(nncp.VersionGet())
                return
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
 
        ctx, err := nncp.CtxFromCmdline(*cfgPath, *spoolPath, *logPath, *quiet, *debug)
        if err != nil {
index 15e4843600c51559455f60947e9a173d11afe81e..01fe150cce0a8dcdd254d1c6bc7c62ce4b856f54 100644 (file)
@@ -45,7 +45,7 @@ func main() {
        var (
                cfgPath   = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file")
                nodeRaw   = flag.String("node", "", "Process only that node")
-               niceRaw   = flag.Int("nice", 255, "Minimal required niceness")
+               niceRaw   = flag.String("nice", nncp.NicenessFmt(255), "Minimal required niceness")
                rxOnly    = flag.Bool("rx", false, "Only receive packets")
                txOnly    = flag.Bool("tx", false, "Only transfer packets")
                mkdir     = flag.Bool("mkdir", false, "Create necessary outbound directories")
@@ -71,10 +71,10 @@ func main() {
                usage()
                os.Exit(1)
        }
-       if *niceRaw < 1 || *niceRaw > 255 {
-               log.Fatalln("-nice must be between 1 and 255")
+       nice, err := nncp.NicenessParse(*niceRaw)
+       if err != nil {
+               log.Fatalln(err)
        }
-       nice := uint8(*niceRaw)
        if *rxOnly && *txOnly {
                log.Fatalln("-rx and -tx can not be set simultaneously")
        }
diff --git a/src/cypherpunks.ru/nncp/nice.go b/src/cypherpunks.ru/nncp/nice.go
new file mode 100644 (file)
index 0000000..eceb6fc
--- /dev/null
@@ -0,0 +1,108 @@
+package nncp
+
+import (
+       "errors"
+       "fmt"
+       "regexp"
+       "strconv"
+       "strings"
+)
+
+const (
+       NiceFlash    = 32
+       NicePriority = 96
+       NiceNormal   = 160
+       NiceBulk     = 224
+
+       DefaultNiceExec = NicePriority
+       DefaultNiceFreq = NiceNormal
+       DefaultNiceFile = NiceBulk
+)
+
+var (
+       niceRe      *regexp.Regexp   = regexp.MustCompile(`^(\w+)([-+])(\d+)$`)
+       niceAliases map[string]uint8 = map[string]uint8{
+               "flash":    NiceFlash,
+               "f":        NiceFlash,
+               "priority": NicePriority,
+               "p":        NicePriority,
+               "normal":   NiceNormal,
+               "n":        NiceNormal,
+               "bulk":     NiceBulk,
+               "b":        NiceBulk,
+               "max":      255,
+       }
+)
+
+func NicenessParse(s string) (uint8, error) {
+       if nice, err := strconv.Atoi(s); err == nil {
+               if nice <= 0 || nice > 255 {
+                       return 0, errors.New("nice out of bounds")
+               }
+               return uint8(nice), nil
+       }
+       s = strings.ToLower(s)
+       var baseNice uint8
+       var found bool
+       if baseNice, found = niceAliases[s]; found {
+               return baseNice, nil
+       }
+       matches := niceRe.FindStringSubmatch(s)
+       if len(matches) != 1+3 {
+               return 0, errors.New("invalid niceness")
+       }
+       baseNice, found = niceAliases[matches[1]]
+       if !found {
+               return 0, errors.New("invalid niceness")
+       }
+       delta, err := strconv.Atoi(matches[3])
+       if err != nil {
+               return 0, err
+       }
+       if matches[2] == "-" {
+               if delta > 31 {
+                       return 0, errors.New("too big niceness delta")
+               }
+               return baseNice - uint8(delta), nil
+       } else {
+               if delta > 32 || (baseNice == NiceBulk && delta > 31) {
+                       return 0, errors.New("too big niceness delta")
+               }
+               return baseNice + uint8(delta), nil
+       }
+}
+
+func NicenessFmt(nice uint8) string {
+       switch {
+       case nice == 255:
+               return "MAX"
+       case NiceFlash-31 < nice && nice < NiceFlash:
+               return fmt.Sprintf("F-%d", NiceFlash-nice)
+       case nice == NiceFlash:
+               return "F"
+       case NiceFlash < nice && nice <= (NiceFlash+32):
+               return fmt.Sprintf("F+%d", nice-NiceFlash)
+
+       case NicePriority-31 < nice && nice < NicePriority:
+               return fmt.Sprintf("P-%d", NicePriority-nice)
+       case nice == NicePriority:
+               return "P"
+       case NicePriority < nice && nice <= (NicePriority+32):
+               return fmt.Sprintf("P+%d", nice-NicePriority)
+
+       case NiceNormal-31 < nice && nice < NiceNormal:
+               return fmt.Sprintf("N-%d", NiceNormal-nice)
+       case nice == NiceNormal:
+               return "N"
+       case NiceNormal < nice && nice <= (NiceNormal+32):
+               return fmt.Sprintf("N+%d", nice-NiceNormal)
+
+       case NiceBulk-31 < nice && nice < NiceBulk:
+               return fmt.Sprintf("B-%d", NiceBulk-nice)
+       case nice == NiceBulk:
+               return "B"
+       case NiceBulk < nice && nice <= (NiceBulk+30):
+               return fmt.Sprintf("B+%d", nice-NiceBulk)
+       }
+       return strconv.Itoa(int(nice))
+}
diff --git a/src/cypherpunks.ru/nncp/nice_test.go b/src/cypherpunks.ru/nncp/nice_test.go
new file mode 100644 (file)
index 0000000..905cec3
--- /dev/null
@@ -0,0 +1,21 @@
+package nncp
+
+import (
+       "strings"
+       "testing"
+)
+
+func TestNiceSymmetric(t *testing.T) {
+       var nice uint8
+       for nice = 1; nice > 0; nice++ {
+               s := NicenessFmt(nice)
+               parsed, err := NicenessParse(s)
+               if err != nil || parsed != nice {
+                       t.Error(err)
+               }
+               parsed, err = NicenessParse(strings.ToLower(s))
+               if err != nil || parsed != nice {
+                       t.Error(err)
+               }
+       }
+}
index f1fb79ff5bdf8df4b7bca8f44bdc1eb08e3b4802..b6cdcc65f9b09f08d55530c71d8e4f0922c61f16 100644 (file)
@@ -24,7 +24,6 @@ import (
        "sync"
 
        "github.com/flynn/noise"
-       "github.com/gorhill/cronexpr"
        "golang.org/x/crypto/blake2b"
        "golang.org/x/crypto/ed25519"
        "golang.org/x/crypto/nacl/box"
@@ -49,6 +48,8 @@ type Node struct {
        FreqMinSize    int64
        Via            []*NodeId
        Addrs          map[string]string
+       RxRate         int
+       TxRate         int
        OnlineDeadline uint
        MaxOnlineTime  uint
        Calls          []*Call
@@ -67,15 +68,6 @@ type NodeOur struct {
        NoisePrv *[32]byte
 }
 
-type Call struct {
-       Cron           *cronexpr.Expression
-       Nice           uint8
-       Xx             TRxTx
-       Addr           *string
-       OnlineDeadline uint
-       MaxOnlineTime  uint
-}
-
 func NewNodeGenerate() (*NodeOur, error) {
        exchPub, exchPrv, err := box.GenerateKey(rand.Reader)
        if err != nil {
index b1c506a590d4ec816a61ac030781aa2645a7a8ea..1d1ff34bf0b082dc3bce06381513b3927372889a 100644 (file)
@@ -47,10 +47,6 @@ const (
 
        MaxPathSize = 1<<8 - 1
 
-       DefaultNiceExec = 64
-       DefaultNiceFreq = 64
-       DefaultNiceFile = 196
-
        NNCPBundlePrefix = "NNCP"
 )
 
@@ -174,7 +170,14 @@ func ae(keyEnc *[32]byte, r io.Reader, w io.Writer) (int, error) {
        return written, nil
 }
 
-func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize int64, data io.Reader, out io.Writer) error {
+func PktEncWrite(
+       our *NodeOur,
+       their *Node,
+       pkt *Pkt,
+       nice uint8,
+       size, padSize int64,
+       data io.Reader,
+       out io.Writer) error {
        pubEph, prvEph, err := box.GenerateKey(rand.Reader)
        if err != nil {
                return err
@@ -253,7 +256,7 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize
        if err != nil {
                return err
        }
-       lr := io.LimitedReader{data, size}
+       lr := io.LimitedReader{R: data, N: size}
        mr := io.MultiReader(&pktBuf, &lr)
        mw := io.MultiWriter(out, mac)
        fullSize := pktBuf.Len() + int(size)
@@ -271,7 +274,7 @@ func PktEncWrite(our *NodeOur, their *Node, pkt *Pkt, nice uint8, size, padSize
                if _, err = io.ReadFull(kdf, keyEnc[:]); err != nil {
                        return err
                }
-               lr = io.LimitedReader{DevZero{}, padSize}
+               lr = io.LimitedReader{R: DevZero{}, N: padSize}
                written, err = ae(keyEnc, &lr, out)
                if err != nil {
                        return err
@@ -298,7 +301,11 @@ func TbsVerify(our *NodeOur, their *Node, pktEnc *PktEnc) (bool, error) {
        return ed25519.Verify(their.SignPub, tbsBuf.Bytes(), pktEnc.Sign[:]), nil
 }
 
-func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Writer) (*Node, int64, error) {
+func PktEncRead(
+       our *NodeOur,
+       nodes map[NodeId]*Node,
+       data io.Reader,
+       out io.Writer) (*Node, int64, error) {
        var pktEnc PktEnc
        _, err := xdr.Unmarshal(data, &pktEnc)
        if err != nil {
@@ -373,7 +380,7 @@ func PktEncRead(our *NodeOur, nodes map[NodeId]*Node, data io.Reader, out io.Wri
        }
 
        fullSize := PktOverhead + size - 8 - 2*blake2b.Size256
-       lr := io.LimitedReader{data, fullSize}
+       lr := io.LimitedReader{R: data, N: fullSize}
        tr := io.TeeReader(&lr, mac)
        written, err := ae(keyEnc, tr, out)
        if err != nil {
index e0765b3a7179c53be091cc421f4d4ff63c58d50a..5775e7327b18eb5fff91c3a276814528c97eae80 100644 (file)
@@ -81,7 +81,12 @@ func TestPktEncRead(t *testing.T) {
        if err != nil {
                panic(err)
        }
-       f := func(path string, pathSize uint8, data [1 << 16]byte, size, padSize uint16, junk []byte) bool {
+       f := func(
+               path string,
+               pathSize uint8,
+               data [1 << 16]byte,
+               size, padSize uint16,
+               junk []byte) bool {
                dataR := bytes.NewReader(data[:])
                var ct bytes.Buffer
                if len(path) > int(pathSize) {
index 5bffe28d306f6d37301ede3ff433926f54293ae2..957dec9793eef5d1c57f65c6a17fd50d855196da 100644 (file)
@@ -99,6 +99,11 @@ type SPRaw struct {
        Payload []byte
 }
 
+type FreqWithNice struct {
+       freq *SPFreq
+       nice uint8
+}
+
 func init() {
        var buf bytes.Buffer
        spHead := SPHead{Type: SPTypeHalt}
@@ -169,8 +174,8 @@ type SPState struct {
        csTheir        *noise.CipherState
        payloads       chan []byte
        infosTheir     map[[32]byte]*SPInfo
-       infosOurSeen   map[[32]byte]struct{}
-       queueTheir     []*SPFreq
+       infosOurSeen   map[[32]byte]uint8
+       queueTheir     []*FreqWithNice
        wg             sync.WaitGroup
        RxBytes        int64
        RxLastSeen     time.Time
@@ -183,6 +188,8 @@ type SPState struct {
        rxLock         *os.File
        txLock         *os.File
        xxOnly         TRxTx
+       rxRate         int
+       txRate         int
        isDead         bool
        sync.RWMutex
 }
@@ -195,7 +202,8 @@ func (state *SPState) NotAlive() bool {
        if state.maxOnlineTime > 0 && state.started.Add(time.Duration(state.maxOnlineTime)*time.Second).Before(now) {
                return true
        }
-       return uint(now.Sub(state.RxLastSeen).Seconds()) >= state.onlineDeadline && uint(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline
+       return uint(now.Sub(state.RxLastSeen).Seconds()) >= state.onlineDeadline &&
+               uint(now.Sub(state.TxLastSeen).Seconds()) >= state.onlineDeadline
 }
 
 func (state *SPState) dirUnlock() {
@@ -226,7 +234,7 @@ func (state *SPState) ReadSP(src io.Reader) ([]byte, error) {
        return sp.Payload, nil
 }
 
-func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]struct{}) [][]byte {
+func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]uint8) [][]byte {
        var infos []*SPInfo
        var totalSize int64
        for job := range ctx.Jobs(nodeId, TTx) {
@@ -243,7 +251,7 @@ func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]struct{}
                        Size: uint64(job.Size),
                        Hash: job.HshValue,
                })
-               (*seen)[*job.HshValue] = struct{}{}
+               (*seen)[*job.HshValue] = job.PktEnc.Nice
        }
        sort.Sort(ByNice(infos))
        var payloads [][]byte
@@ -266,7 +274,13 @@ func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]struct{}
        return payloadsSplit(payloads)
 }
 
-func (ctx *Ctx) StartI(conn net.Conn, nodeId *NodeId, nice uint8, xxOnly TRxTx, onlineDeadline, maxOnlineTime uint) (*SPState, error) {
+func (ctx *Ctx) StartI(
+       conn net.Conn,
+       nodeId *NodeId,
+       nice uint8,
+       xxOnly TRxTx,
+       rxRate, txRate int,
+       onlineDeadline, maxOnlineTime uint) (*SPState, error) {
        err := ctx.ensureRxDir(nodeId)
        if err != nil {
                return nil, err
@@ -310,11 +324,13 @@ func (ctx *Ctx) StartI(conn net.Conn, nodeId *NodeId, nice uint8, xxOnly TRxTx,
                nice:           nice,
                payloads:       make(chan []byte),
                infosTheir:     make(map[[32]byte]*SPInfo),
-               infosOurSeen:   make(map[[32]byte]struct{}),
+               infosOurSeen:   make(map[[32]byte]uint8),
                started:        started,
                rxLock:         rxLock,
                txLock:         txLock,
                xxOnly:         xxOnly,
+               rxRate:         rxRate,
+               txRate:         txRate,
        }
 
        var infosPayloads [][]byte
@@ -388,7 +404,7 @@ func (ctx *Ctx) StartR(conn net.Conn, nice uint8, xxOnly TRxTx) (*SPState, error
                hs:           hs,
                nice:         nice,
                payloads:     make(chan []byte),
-               infosOurSeen: make(map[[32]byte]struct{}),
+               infosOurSeen: make(map[[32]byte]uint8),
                infosTheir:   make(map[[32]byte]*SPInfo),
                started:      started,
                xxOnly:       xxOnly,
@@ -422,6 +438,8 @@ func (ctx *Ctx) StartR(conn net.Conn, nice uint8, xxOnly TRxTx) (*SPState, error
                return nil, errors.New("Unknown peer: " + peerId)
        }
        state.Node = node
+       state.rxRate = node.RxRate
+       state.txRate = node.TxRate
        state.onlineDeadline = node.OnlineDeadline
        state.maxOnlineTime = node.MaxOnlineTime
        sds := SDS{"node": node.Id, "nice": strconv.Itoa(int(nice))}
@@ -480,7 +498,10 @@ func (ctx *Ctx) StartR(conn net.Conn, nice uint8, xxOnly TRxTx) (*SPState, error
        return &state, err
 }
 
-func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payload []byte) error {
+func (state *SPState) StartWorkers(
+       conn net.Conn,
+       infosPayloads [][]byte,
+       payload []byte) error {
        sds := SDS{"node": state.Node.Id, "nice": strconv.Itoa(int(state.nice))}
        if len(infosPayloads) > 1 {
                go func() {
@@ -563,8 +584,13 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa
                                        time.Sleep(100 * time.Millisecond)
                                        continue
                                }
-                               freq := state.queueTheir[0]
+                               freq := state.queueTheir[0].freq
                                state.RUnlock()
+
+                               if state.txRate > 0 {
+                                       time.Sleep(time.Second / time.Duration(state.txRate))
+                               }
+
                                sdsp := SdsAdd(sds, SDS{
                                        "xx":   string(TTx),
                                        "hash": ToBase32(freq.Hash[:]),
@@ -618,7 +644,7 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa
                                sdsp["fullsize"] = strconv.FormatInt(int64(fullSize), 10)
                                state.ctx.LogP("sp-file", sdsp, "")
                                state.Lock()
-                               if len(state.queueTheir) > 0 && *state.queueTheir[0].Hash == *freq.Hash {
+                               if len(state.queueTheir) > 0 && *state.queueTheir[0].freq.Hash == *freq.Hash {
                                        if ourSize == fullSize {
                                                state.ctx.LogD("sp-file", sdsp, "finished")
                                                if len(state.queueTheir) > 1 {
@@ -627,7 +653,7 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa
                                                        state.queueTheir = state.queueTheir[:0]
                                                }
                                        } else {
-                                               state.queueTheir[0].Offset += uint64(len(buf))
+                                               state.queueTheir[0].freq.Offset += uint64(len(buf))
                                        }
                                } else {
                                        state.ctx.LogD("sp-file", sdsp, "queue disappeared")
@@ -639,7 +665,7 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa
                                SdsAdd(sds, SDS{"size": strconv.Itoa(len(payload))}),
                                "sending",
                        )
-                       conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
+                       conn.SetWriteDeadline(time.Now().Add(DefaultDeadline * time.Second))
                        if err := state.WriteSP(conn, state.csOur.Encrypt(nil, nil, payload)); err != nil {
                                state.ctx.LogE("sp-xmit", SdsAdd(sds, SDS{"err": err}), "")
                                break
@@ -702,6 +728,9 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa
                                        state.payloads <- reply
                                }
                        }()
+                       if state.rxRate > 0 {
+                               time.Sleep(time.Second / time.Duration(state.rxRate))
+                       }
                }
        }()
 
@@ -763,22 +792,23 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                        state.infosTheir[*info.Hash] = &info
                        state.Unlock()
                        state.ctx.LogD("sp-process", sdsp, "stating part")
-                       if _, err = os.Stat(filepath.Join(
+                       pktPath := filepath.Join(
                                state.ctx.Spool,
                                state.Node.Id.String(),
                                string(TRx),
                                ToBase32(info.Hash[:]),
-                       )); err == nil {
+                       )
+                       if _, err = os.Stat(pktPath); err == nil {
                                state.ctx.LogD("sp-process", sdsp, "already done")
                                replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
                                continue
                        }
-                       fi, err := os.Stat(filepath.Join(
-                               state.ctx.Spool,
-                               state.Node.Id.String(),
-                               string(TRx),
-                               ToBase32(info.Hash[:])+PartSuffix,
-                       ))
+                       if _, err = os.Stat(pktPath + SeenSuffix); err == nil {
+                               state.ctx.LogD("sp-process", sdsp, "already seen")
+                               replies = append(replies, MarshalSP(SPTypeDone, SPDone{info.Hash}))
+                               continue
+                       }
+                       fi, err := os.Stat(pktPath + PartSuffix)
                        var offset int64
                        if err == nil {
                                offset = fi.Size()
@@ -922,9 +952,26 @@ func (state *SPState) ProcessSP(payload []byte) ([][]byte, error) {
                                "hash":   ToBase32(freq.Hash[:]),
                                "offset": strconv.FormatInt(int64(freq.Offset), 10),
                        }), "queueing")
-                       state.Lock()
-                       state.queueTheir = append(state.queueTheir, &freq)
-                       state.Unlock()
+                       nice, exists := state.infosOurSeen[*freq.Hash]
+                       if exists {
+                               state.Lock()
+                               insertIdx := 0
+                               var freqWithNice *FreqWithNice
+                               for insertIdx, freqWithNice = range state.queueTheir {
+                                       if freqWithNice.nice > nice {
+                                               break
+                                       }
+                               }
+                               state.queueTheir = append(state.queueTheir, nil)
+                               copy(state.queueTheir[insertIdx+1:], state.queueTheir[insertIdx:])
+                               state.queueTheir[insertIdx] = &FreqWithNice{&freq, nice}
+                               state.Unlock()
+                       } else {
+                               state.ctx.LogD("sp-process", SdsAdd(sdsp, SDS{
+                                       "hash":   ToBase32(freq.Hash[:]),
+                                       "offset": strconv.FormatInt(int64(freq.Offset), 10),
+                               }), "unknown")
+                       }
                case SPTypeHalt:
                        sdsp := SdsAdd(sds, SDS{"type": "halt"})
                        state.ctx.LogD("sp-process", sdsp, "")
index 86d154b2aa310a36b454e87c36ab989312c80e44..fa49c169049c9c5882a852e5e99abc6a58834cff 100644 (file)
@@ -52,7 +52,10 @@ func newNotification(fromTo *FromToYAML, subject string) io.Reader {
        ))
 }
 
-func (ctx *Ctx) Toss(nodeId *NodeId, nice uint8, dryRun, doSeen, noFile, noFreq, noExec, noTrns bool) bool {
+func (ctx *Ctx) Toss(
+       nodeId *NodeId,
+       nice uint8,
+       dryRun, doSeen, noFile, noFreq, noExec, noTrns bool) bool {
        isBad := false
        for job := range ctx.Jobs(nodeId, TRx) {
                pktName := filepath.Base(job.Fd.Name())
index 43198138b6440907abf09668e31c0cff7fc4b649..0692ecaa66433128026799f3b80d609fc6a7d111 100644 (file)
@@ -36,7 +36,12 @@ import (
        "golang.org/x/crypto/blake2b"
 )
 
-func (ctx *Ctx) Tx(node *Node, pkt *Pkt, nice uint8, size, minSize int64, src io.Reader) (*Node, error) {
+func (ctx *Ctx) Tx(
+       node *Node,
+       pkt *Pkt,
+       nice uint8,
+       size, minSize int64,
+       src io.Reader) (*Node, error) {
        tmp, err := ctx.NewTmpFileWHash()
        if err != nil {
                return nil, err
@@ -180,7 +185,12 @@ func (ctx *Ctx) TxFile(node *Node, nice uint8, srcPath, dstPath string, minSize
        return err
 }
 
-func (ctx *Ctx) TxFileChunked(node *Node, nice uint8, srcPath, dstPath string, minSize int64, chunkSize int64) error {
+func (ctx *Ctx) TxFileChunked(
+       node *Node,
+       nice uint8,
+       srcPath, dstPath string,
+       minSize int64,
+       chunkSize int64) error {
        if dstPath == "" {
                if srcPath == "-" {
                        return errors.New("Must provide destination filename")
@@ -312,7 +322,11 @@ func (ctx *Ctx) TxFileChunked(node *Node, nice uint8, srcPath, dstPath string, m
        return err
 }
 
-func (ctx *Ctx) TxFreq(node *Node, nice, replyNice uint8, srcPath, dstPath string, minSize int64) error {
+func (ctx *Ctx) TxFreq(
+       node *Node,
+       nice, replyNice uint8,
+       srcPath, dstPath string,
+       minSize int64) error {
        dstPath = filepath.Clean(dstPath)
        if filepath.IsAbs(dstPath) {
                return errors.New("Relative destination path required")
@@ -345,7 +359,13 @@ func (ctx *Ctx) TxFreq(node *Node, nice, replyNice uint8, srcPath, dstPath strin
        return err
 }
 
-func (ctx *Ctx) TxExec(node *Node, nice, replyNice uint8, handle string, args []string, body []byte, minSize int64) error {
+func (ctx *Ctx) TxExec(
+       node *Node,
+       nice, replyNice uint8,
+       handle string,
+       args []string,
+       body []byte,
+       minSize int64) error {
        path := make([][]byte, 0, 1+len(args))
        path = append(path, []byte(handle))
        for _, arg := range args {
index ab813273cd59e1333f7ae7bff5d027d4aadf528c..8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9 160000 (submodule)
@@ -1 +1 @@
-Subproject commit ab813273cd59e1333f7ae7bff5d027d4aadf528c
+Subproject commit 8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9
index dfa909b99c79129e1100513e5cd36307665e5723..1e491301e022f8f977054da4c2d852decd59571f 160000 (submodule)
@@ -1 +1 @@
-Subproject commit dfa909b99c79129e1100513e5cd36307665e5723
+Subproject commit 1e491301e022f8f977054da4c2d852decd59571f
index c11f84a56e43e20a78cee75a7c034031ecf57d1f..9527bec2660bd847c050fda93a0f0c6dee0800bb 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c11f84a56e43e20a78cee75a7c034031ecf57d1f
+Subproject commit 9527bec2660bd847c050fda93a0f0c6dee0800bb