From 2e22bda93fdf8f2f84e4d19b3f1d46318b497139 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sun, 13 Aug 2023 21:31:21 +0300 Subject: [PATCH] Generate ACKs during tossing --- doc/ack.texi | 72 +++++++++++++++++++++++++++++++++++++ doc/call.texi | 6 ++-- doc/cfg/neigh.texi | 19 ++++++++++ doc/cmd/nncp-ack.texi | 66 +++------------------------------- doc/cmd/nncp-rm.texi | 3 +- doc/cmd/nncp-toss.texi | 11 ++++-- doc/index.texi | 1 + doc/news.ru.texi | 26 +++++++++++--- doc/news.texi | 22 ++++++++++-- doc/spool.texi | 10 ++++++ src/call.go | 1 + src/cfg.go | 26 ++++++++++++++ src/cfgdir.go | 51 ++++++++++++++++++++++++++ src/cmd/nncp-ack/main.go | 17 +++++++-- src/cmd/nncp-call/main.go | 3 ++ src/cmd/nncp-caller/main.go | 3 ++ src/cmd/nncp-daemon/main.go | 4 +++ src/cmd/nncp-rm/main.go | 45 ++++++++++++++++++++++- src/cmd/nncp-toss/main.go | 3 ++ src/nncp.go | 2 +- src/node.go | 2 ++ src/toss.go | 29 +++++++++++++++ 22 files changed, 345 insertions(+), 77 deletions(-) create mode 100644 doc/ack.texi diff --git a/doc/ack.texi b/doc/ack.texi new file mode 100644 index 0000000..085251b --- /dev/null +++ b/doc/ack.texi @@ -0,0 +1,72 @@ +@node ACK +@cindex ACKnowledgements +@cindex packet acknowledgement +@unnumbered ACKnowledgements + +There is possibility to explicitly acknowledge the receipt of the +encrypted packet, by generating the reply of @code{ACK}-type which +contains the packet identifier. + +ACK packets can be generated explicitly by @command{@ref{nncp-ack}} +command, or during the tossing procedure if @option{-gen-ack} option +is specified. + +General workflow with acknowledgement is following, assuming that +Alice has some outbound packets for Bob: + +@itemize + +@item Transfer encrypted packets, without deleting them locally: + +@example +alice$ nncp-xfer -keep -tx -node bob /mnt/shared +@end example + +@item On Bob's side retrieve those packets: + +@example +bob$ nncp-xfer -rx /mnt/shared +@end example + +That will also check if copied packets checksum is not mismatched. + +@item Create ACK packets of received ones, saving the list of encrypted + ACK packets: + +@example +bob$ nncp-ack -node alice 4>acks +@end example + +@item Send those newly created packets back to Alice: + +@example +bob$ nncp-xfer [-keep] -tx /mnt/shared +@end example + +@item Remove them from outbound spool, because we expect no + acknowledgement for them: + +@example +bob$ nncp-rm -node alice -pkt &1 >&2 | nncp-rm [@dots{}] -pkt @end example -Send acknowledgement of successful @option{PKT} (Base32-encoded hash) -packet receipt from @option{NODE} node. If no @option{-pkt} is -specified, then acknowledge all packet in node's @code{rx} spool. -If @option{-all} is specified, then do that for all nodes. +Send @ref{ACK, acknowledgement} of successful @option{PKT} +(Base32-encoded hash) packet receipt from @option{NODE} node. If no +@option{-pkt} is specified, then acknowledge all packet in node's +@code{rx} spool. If @option{-all} is specified, then do that for all +nodes. That commands outputs list of created encrypted ACK packets (@code{NODE/PKT}) to @strong{4}th file descriptor. That output can be passed for example to @command{@ref{nncp-rm}} to remove them after transmission to not wait for acknowledgement and retransmission. - -General workflow with acknowledgement is following, assuming that -Alice has some outbound packets for Bob: - -@itemize - -@item Transfer encrypted packets, without deleting them locally: - -@example -alice$ nncp-xfer -keep -tx -node bob /mnt/shared -@end example - -@item On Bob's side retrieve those packets: - -@example -bob$ nncp-xfer -rx /mnt/shared -@end example - -That will also check if copied packets checksum is not mismatched. - -@item Create ACK packets of received ones, saving the list of encrypted - ACK packets: - -@example -bob$ nncp-ack -node alice 4>acks -@end example - -@item Send those newly created packets back to Alice: - -@example -bob$ nncp-xfer [-keep] -tx /mnt/shared -@end example - -@item Remove them from outbound spool, because we expect no - acknowledgement for them: - -@example -bob$ nncp-rm -node alice -pkt 0 { defRxRate = *cfg.RxRate @@ -317,6 +339,8 @@ func NewNode(name string, cfg NodeJSON) (*Node, error) { call.AutoTossNoExec = callCfg.AutoTossNoExec call.AutoTossNoTrns = callCfg.AutoTossNoTrns call.AutoTossNoArea = callCfg.AutoTossNoArea + call.AutoTossNoACK = callCfg.AutoTossNoACK + call.AutoTossGenACK = callCfg.AutoTossGenACK calls = append(calls, &call) } @@ -332,6 +356,8 @@ func NewNode(name string, cfg NodeJSON) (*Node, error) { FreqChunked: freqChunked, FreqMinSize: freqMinSize, FreqMaxSize: freqMaxSize, + ACKNice: ackNice, + ACKMinSize: ackMinSize, Calls: calls, Addrs: cfg.Addrs, RxRate: defRxRate, diff --git a/src/cfgdir.go b/src/cfgdir.go index f9378c8..fc36636 100644 --- a/src/cfgdir.go +++ b/src/cfgdir.go @@ -260,6 +260,24 @@ func CfgToDir(dst string, cfg *CfgJSON) (err error) { } } + if n.ACK != nil { + if err = cfgDirMkdir(dst, "neigh", name, "ack"); err != nil { + return + } + if err = cfgDirSave( + n.ACK.MinSize, + dst, "neigh", name, "ack", "minsize", + ); err != nil { + return + } + if err = cfgDirSave( + n.ACK.Nice, + dst, "neigh", name, "ack", "nice", + ); err != nil { + return + } + } + if len(n.Via) > 0 { if err = cfgDirSave( strings.Join(n.Via, "\n"), @@ -372,6 +390,16 @@ func CfgToDir(dst string, cfg *CfgJSON) (err error) { return } } + if call.AutoTossNoACK { + if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-noack"); err != nil { + return + } + } + if call.AutoTossGenACK { + if err = cfgDirTouch(dst, "neigh", name, "calls", is, "autotoss-gen-ack"); err != nil { + return + } + } } } @@ -689,6 +717,23 @@ func DirToCfg(src string) (*CfgJSON, error) { } } + if cfgDirExists(src, "neigh", n, "ack") { + node.ACK = &NodeACKJSON{} + i64, err := cfgDirLoadIntOpt(src, "neigh", n, "ack", "minsize") + if err != nil { + return nil, err + } + if i64 != nil { + i := uint64(*i64) + node.ACK.MinSize = &i + } + if node.ACK.Nice, err = cfgDirLoadOpt( + src, "neigh", n, "ack", "nice", + ); err != nil { + return nil, err + } + } + via, err := cfgDirLoadOpt(src, "neigh", n, "via") if err != nil { return nil, err @@ -856,6 +901,12 @@ func DirToCfg(src string) (*CfgJSON, error) { if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noarea") { call.AutoTossNoArea = true } + if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-noack") { + call.AutoTossNoACK = true + } + if cfgDirExists(src, "neigh", n, "calls", is, "autotoss-gen-ack") { + call.AutoTossGenACK = true + } node.Calls = append(node.Calls, call) } cfg.Neigh[n] = node diff --git a/src/cmd/nncp-ack/main.go b/src/cmd/nncp-ack/main.go index 7342c84..18a6f4b 100644 --- a/src/cmd/nncp-ack/main.go +++ b/src/cmd/nncp-ack/main.go @@ -47,7 +47,7 @@ func main() { cfgPath = flag.String("cfg", nncp.DefaultCfgPath, "Path to configuration file") niceRaw = flag.String("nice", nncp.NicenessFmt(nncp.DefaultNiceFreq), "Outbound packet niceness") - minSizeRaw = flag.Uint64("minsize", 0, "Minimal required resulting packet size, in KiB") + minSizeRaw = flag.Int64("minsize", -1, "Minimal required resulting packet size, in KiB") viaOverride = flag.String("via", "", "Override Via path to destination node (ignored with -all)") spoolPath = flag.String("spool", "", "Override path to spool") logPath = flag.String("log", "", "Override path to logfile") @@ -94,7 +94,6 @@ func main() { } ctx.Umask() - minSize := int64(*minSizeRaw) * 1024 var nodes []*nncp.Node if *nodesRaw != "" { @@ -130,6 +129,14 @@ func main() { os.Exit(1) } nncp.ViaOverride(*viaOverride, ctx, nodes[0]) + + var minSize int64 + if *minSizeRaw < 0 { + minSize = nodes[0].ACKMinSize + } else if *minSizeRaw > 0 { + minSize = *minSizeRaw * 1024 + } + pktName, err := ctx.TxACK(nodes[0], nice, *pktRaw, minSize) if err != nil { log.Fatalln(err) @@ -140,6 +147,12 @@ func main() { isBad := false for _, node := range nodes { + var minSize int64 + if *minSizeRaw < 0 { + minSize = node.ACKMinSize + } else if *minSizeRaw > 0 { + minSize = *minSizeRaw * 1024 + } for job := range ctx.Jobs(node.Id, nncp.TRx) { pktName := filepath.Base(job.Path) sender := ctx.Neigh[*job.PktEnc.Sender] diff --git a/src/cmd/nncp-call/main.go b/src/cmd/nncp-call/main.go index a684c98..23b6fb9 100644 --- a/src/cmd/nncp-call/main.go +++ b/src/cmd/nncp-call/main.go @@ -81,6 +81,8 @@ func main() { "Do not process \"area\" packets during tossing") autoTossNoACK = flag.Bool("autotoss-noack", false, "Do not process \"ack\" packets during tossing") + autoTossGenACK = flag.Bool("autotoss-gen-ack", false, + "Generate ACK packets") ) log.SetFlags(log.Lshortfile) flag.Usage = usage @@ -229,6 +231,7 @@ func main() { NoTrns: *autoTossNoTrns, NoArea: *autoTossNoArea, NoACK: *autoTossNoACK, + GenACK: *autoTossGenACK, }, ) } diff --git a/src/cmd/nncp-caller/main.go b/src/cmd/nncp-caller/main.go index 08c4afa..3eff4d2 100644 --- a/src/cmd/nncp-caller/main.go +++ b/src/cmd/nncp-caller/main.go @@ -67,6 +67,8 @@ func main() { "Do not process \"area\" packets during tossing") autoTossNoACK = flag.Bool("autotoss-noack", false, "Do not process \"ack\" packets during tossing") + autoTossGenACK = flag.Bool("autotoss-gen-ack", false, + "Generate ACK packets") ) log.SetFlags(log.Lshortfile) flag.Usage = usage @@ -229,6 +231,7 @@ func main() { NoTrns: call.AutoTossNoTrns || *autoTossNoTrns, NoArea: call.AutoTossNoArea || *autoTossNoArea, NoACK: call.AutoTossNoACK || *autoTossNoACK, + GenACK: call.AutoTossGenACK || *autoTossGenACK, }, ) } diff --git a/src/cmd/nncp-daemon/main.go b/src/cmd/nncp-daemon/main.go index f4bf2c3..33fffa7 100644 --- a/src/cmd/nncp-daemon/main.go +++ b/src/cmd/nncp-daemon/main.go @@ -167,6 +167,8 @@ func main() { "Do not process \"area\" packets during tossing") autoTossNoACK = flag.Bool("autotoss-noack", false, "Do not process \"ack\" packets during tossing") + autoTossGenACK = flag.Bool("autotoss-gen-ack", false, + "Generate ACK packets") ) log.SetFlags(log.Lshortfile) flag.Usage = usage @@ -228,6 +230,7 @@ func main() { NoTrns: *autoTossNoTrns, NoArea: *autoTossNoArea, NoACK: *autoTossNoACK, + GenACK: *autoTossGenACK, }, ) } @@ -317,6 +320,7 @@ func main() { NoTrns: *autoTossNoTrns, NoArea: *autoTossNoArea, NoACK: *autoTossNoACK, + GenACK: *autoTossGenACK, }, ) } diff --git a/src/cmd/nncp-rm/main.go b/src/cmd/nncp-rm/main.go index b1d1524..18931eb 100644 --- a/src/cmd/nncp-rm/main.go +++ b/src/cmd/nncp-rm/main.go @@ -44,7 +44,8 @@ func usage() { fmt.Fprintf(os.Stderr, " %s [options] [-older X] {-all|-node NODE} -nock\n", os.Args[0]) fmt.Fprintf(os.Stderr, " %s [options] [-older X] {-all|-node NODE} -area\n", os.Args[0]) fmt.Fprintf(os.Stderr, " %s [options] [-older X] {-all|-node NODE} {-rx|-tx} [-hdr]\n", os.Args[0]) - fmt.Fprintf(os.Stderr, " %s [options] [-older X] {-all|-node NODE} -pkt < ...\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -pkt < ...\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " %s [options] {-all|-node NODE} -ack\n", os.Args[0]) fmt.Fprintln(os.Stderr, "-older option's time units are: (s)econds, (m)inutes, (h)ours, (d)ays") fmt.Fprintln(os.Stderr, "Options:") flag.PrintDefaults() @@ -67,6 +68,7 @@ func main() { older = flag.String("older", "", "XXX{smhd}: only older than XXX number of time units") dryRun = flag.Bool("dryrun", false, "Do not actually remove files") doPkt = flag.Bool("pkt", false, "Remove only that packets") + doACK = flag.Bool("ack", false, "Remove ACK packets from outbound") spoolPath = flag.String("spool", "", "Override path to spool") quiet = flag.Bool("quiet", false, "Print only errors") debug = flag.Bool("debug", false, "Print debug messages") @@ -393,5 +395,46 @@ func main() { log.Fatalln("Can not remove:", err) } } + if *doACK { + dirPath := filepath.Join( + ctx.Spool, node.Id.String(), string(nncp.TTx), nncp.ACKDir) + dir, err := os.Open(dirPath) + if err != nil { + continue + } + for { + fis, err := dir.ReadDir(1 << 10) + if err != nil { + if err == io.EOF { + break + } + log.Fatalln("Can not read directory:", err) + } + for _, fi := range fis { + for _, pth := range []string{ + filepath.Join( + ctx.Spool, + node.Id.String(), + string(nncp.TTx), + fi.Name(), + ), + filepath.Join(dirPath, fi.Name()), + } { + if err = os.Remove(pth); err == nil { + ctx.LogI( + "rm", + nncp.LEs{{K: "File", V: pth}}, + func(les nncp.LEs) string { + return fmt.Sprintf("File %s: removed", pth) + }, + ) + } else if !errors.Is(err, fs.ErrNotExist) { + log.Fatalln("Can not remove:", pth, ":", err) + } + } + } + } + dir.Close() + } } } diff --git a/src/cmd/nncp-toss/main.go b/src/cmd/nncp-toss/main.go index dad1d3e..24b50e2 100644 --- a/src/cmd/nncp-toss/main.go +++ b/src/cmd/nncp-toss/main.go @@ -49,6 +49,7 @@ func main() { noTrns = flag.Bool("notrns", false, "Do not process \"transitional\" packets") noArea = flag.Bool("noarea", false, "Do not process \"area\" packets") noACK = flag.Bool("noack", false, "Do not process \"ack\" packets") + genACK = flag.Bool("gen-ack", false, "Generate ACK packets") spoolPath = flag.String("spool", "", "Override path to spool") logPath = flag.String("log", "", "Override path to logfile") quiet = flag.Bool("quiet", false, "Print only errors") @@ -118,6 +119,7 @@ func main() { NoTrns: *noTrns, NoArea: *noArea, NoACK: *noACK, + GenACK: *genACK, }, ) || isBad if nodeId == *ctx.SelfId { @@ -174,6 +176,7 @@ func main() { NoTrns: *noTrns, NoArea: *noArea, NoACK: *noACK, + GenACK: *genACK, }, ) if *nodeId == *ctx.SelfId { diff --git a/src/nncp.go b/src/nncp.go index dc64ffc..d521372 100644 --- a/src/nncp.go +++ b/src/nncp.go @@ -41,7 +41,7 @@ along with this program. If not, see .` ) var ( - Version string = "8.8.4" + Version string = "8.9.0" Base32Codec *base32.Encoding = base32.StdEncoding.WithPadding(base32.NoPadding) ) diff --git a/src/node.go b/src/node.go index d71fd29..0587fbc 100644 --- a/src/node.go +++ b/src/node.go @@ -50,6 +50,8 @@ type Node struct { FreqChunked int64 FreqMinSize int64 FreqMaxSize int64 + ACKNice uint8 + ACKMinSize int64 Via []*NodeId Addrs map[string]string RxRate int diff --git a/src/toss.go b/src/toss.go index d365e2e..c471049 100644 --- a/src/toss.go +++ b/src/toss.go @@ -44,6 +44,7 @@ import ( const ( SeenDir = "seen" + ACKDir = "ack" ) type TossOpts struct { @@ -56,6 +57,7 @@ type TossOpts struct { NoTrns bool NoArea bool NoACK bool + GenACK bool } func jobPath2Seen(jobPath string) string { @@ -123,6 +125,33 @@ func jobProcess( humanize.IBytes(pktSize), ) }) + if opts.GenACK && pkt.Type != PktTypeACK { + newPktName, err := ctx.TxACK( + sender, sender.ACKNice, pktName, sender.ACKMinSize, + ) + if err != nil { + ctx.LogE("rx-unmarshal", les, err, func(les LEs) string { + return fmt.Sprintf("Tossing %s/%s: generating ACK", sender.Name, pktName) + }) + return err + } + ackDir := filepath.Join(ctx.Spool, sender.Id.String(), string(TTx), ACKDir) + os.MkdirAll(ackDir, os.FileMode(0777)) + if fd, err := os.Create(filepath.Join(ackDir, newPktName)); err == nil { + fd.Close() + if err = DirSync(ackDir); err != nil { + ctx.LogE("rx-genack", les, err, func(les LEs) string { + return fmt.Sprintf("Tossing %s/%s: genACK", sender.Name, pktName) + }) + return err + } + } else { + ctx.LogE("rx-genack", les, err, func(les LEs) string { + return fmt.Sprintf("Tossing %s/%s: genACK", sender.Name, pktName) + }) + return err + } + } switch pkt.Type { case PktTypeExec, PktTypeExecFat: if opts.NoExec { -- 2.44.0