From 40e961bf30dce2ecd1b4517190c6f2040df08de7 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sun, 10 Jun 2018 18:10:22 +0300 Subject: [PATCH] Simple rate limiter --- doc/call.texi | 6 ++++ doc/cfg.texi | 9 ++++++ doc/cmds.texi | 12 +++++-- doc/news.ru.texi | 4 +++ doc/news.texi | 3 ++ src/cypherpunks.ru/nncp/call.go | 17 +++++++++- src/cypherpunks.ru/nncp/cfg.go | 32 +++++++++++++++++++ src/cypherpunks.ru/nncp/cmd/nncp-call/main.go | 15 +++++++-- .../nncp/cmd/nncp-caller/main.go | 2 ++ src/cypherpunks.ru/nncp/node.go | 12 ++----- src/cypherpunks.ru/nncp/sp.go | 16 +++++++++- 11 files changed, 111 insertions(+), 17 deletions(-) diff --git a/doc/call.texi b/doc/call.texi index 31d8f82..63e4c79 100644 --- a/doc/call.texi +++ b/doc/call.texi @@ -16,6 +16,8 @@ calls: onlinedeadline: 1800 maxonlinetime: 1750 nice: 64 + 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. diff --git a/doc/cfg.texi b/doc/cfg.texi index 5be0382..410bee8 100644 --- a/doc/cfg.texi +++ b/doc/cfg.texi @@ -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 diff --git a/doc/cmds.texi b/doc/cmds.texi index 33ad949..49a4f1d 100644 --- a/doc/cmds.texi +++ b/doc/cmds.texi @@ -95,8 +95,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 +116,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 diff --git a/doc/news.ru.texi b/doc/news.ru.texi index dd20336..4d0451b 100644 --- a/doc/news.ru.texi +++ b/doc/news.ru.texi @@ -13,6 +13,10 @@ Если более высокоприоритетный пакет попадает в спул, то @command{nncp-daemon} добавит его в очередь отправки первым, прерывая низкоприоритетные передачи. +@item +К средствам связанным с online-соединениями (@command{nncp-daemon}, +@command{nncp-call}, @command{nncp-caller}) добавлен простой +ограничитель скорости. @end itemize @node Релиз 3.2 diff --git a/doc/news.texi b/doc/news.texi index d69e02e..974dae9 100644 --- a/doc/news.texi +++ b/doc/news.texi @@ -14,6 +14,9 @@ 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}). @end itemize @node Release 3.2 diff --git a/src/cypherpunks.ru/nncp/call.go b/src/cypherpunks.ru/nncp/call.go index ab120ad..fbdd05b 100644 --- a/src/cypherpunks.ru/nncp/call.go +++ b/src/cypherpunks.ru/nncp/call.go @@ -21,9 +21,22 @@ 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 +51,8 @@ func (ctx *Ctx) CallNode(node *Node, addrs []string, nice uint8, xxOnly TRxTx, o node.Id, nice, xxOnly, + rxRate, + txRate, onlineDeadline, maxOnlineTime, ) diff --git a/src/cypherpunks.ru/nncp/cfg.go b/src/cypherpunks.ru/nncp/cfg.go index 5d5d66f..381f7de 100644 --- a/src/cypherpunks.ru/nncp/cfg.go +++ b/src/cypherpunks.ru/nncp/cfg.go @@ -59,6 +59,8 @@ type NodeYAML struct { 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"` } @@ -67,6 +69,8 @@ type CallYAML struct { Cron string Nice *int `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"` @@ -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,6 +194,7 @@ 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 { @@ -188,6 +202,7 @@ func NewNode(name string, yml NodeYAML) (*Node, error) { } 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, } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go index bbf9bfa..d304aea 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-call/main.go @@ -42,7 +42,9 @@ func main() { cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.Int("nice", 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") @@ -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) } } diff --git a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go index a975878..d960942 100644 --- a/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go +++ b/src/cypherpunks.ru/nncp/cmd/nncp-caller/main.go @@ -128,6 +128,8 @@ func main() { addrs, call.Nice, call.Xx, + call.RxRate, + call.TxRate, call.OnlineDeadline, call.MaxOnlineTime, ) diff --git a/src/cypherpunks.ru/nncp/node.go b/src/cypherpunks.ru/nncp/node.go index f1fb79f..b6cdcc6 100644 --- a/src/cypherpunks.ru/nncp/node.go +++ b/src/cypherpunks.ru/nncp/node.go @@ -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 { diff --git a/src/cypherpunks.ru/nncp/sp.go b/src/cypherpunks.ru/nncp/sp.go index 86cd96c..5904b04 100644 --- a/src/cypherpunks.ru/nncp/sp.go +++ b/src/cypherpunks.ru/nncp/sp.go @@ -188,6 +188,8 @@ type SPState struct { rxLock *os.File txLock *os.File xxOnly TRxTx + rxRate int + txRate int isDead bool sync.RWMutex } @@ -271,7 +273,7 @@ func (ctx *Ctx) infosOur(nodeId *NodeId, nice uint8, seen *map[[32]byte]uint8) [ 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 @@ -320,6 +322,8 @@ func (ctx *Ctx) StartI(conn net.Conn, nodeId *NodeId, nice uint8, xxOnly TRxTx, rxLock: rxLock, txLock: txLock, xxOnly: xxOnly, + rxRate: rxRate, + txRate: txRate, } var infosPayloads [][]byte @@ -427,6 +431,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))} @@ -570,6 +576,11 @@ func (state *SPState) StartWorkers(conn net.Conn, infosPayloads [][]byte, payloa } 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[:]), @@ -707,6 +718,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)) + } } }() -- 2.44.0