]> Cypherpunks.ru repositories - nncp.git/blob - src/mcd.go
Note about buildability with 1.22
[nncp.git] / src / mcd.go
1 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
2 // Copyright (C) 2016-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package nncp
17
18 import (
19         "bytes"
20         "encoding/hex"
21         "fmt"
22         "net"
23         "sync"
24         "time"
25
26         xdr "github.com/davecgh/go-xdr/xdr2"
27 )
28
29 const MCDPort = 5400
30
31 type MCD struct {
32         Magic  [8]byte
33         Sender *NodeId
34 }
35
36 type MCDAddr struct {
37         Addr     net.UDPAddr
38         lastSeen time.Time
39 }
40
41 var (
42         mcdIP           = net.ParseIP("ff02::4e4e:4350")
43         mcdAddrLifetime = 2 * time.Minute
44
45         mcdPktSize int
46         MCDAddrs   map[NodeId][]*MCDAddr
47         MCDAddrsM  sync.RWMutex
48 )
49
50 func init() {
51         nodeId := new(NodeId)
52         var buf bytes.Buffer
53         mcd := MCD{Sender: nodeId}
54         if _, err := xdr.Marshal(&buf, mcd); err != nil {
55                 panic(err)
56         }
57         mcdPktSize = buf.Len()
58
59         MCDAddrs = make(map[NodeId][]*MCDAddr)
60         go func() {
61                 for {
62                         time.Sleep(time.Minute)
63                         MCDAddrsM.Lock()
64                         now := time.Now()
65                         for nodeId, addrs := range MCDAddrs {
66                                 addrsAlive := make([]*MCDAddr, 0, len(addrs))
67                                 for _, addr := range addrs {
68                                         if !addr.lastSeen.Add(mcdAddrLifetime).Before(now) {
69                                                 addrsAlive = append(addrsAlive, addr)
70                                         }
71                                 }
72                                 MCDAddrs[nodeId] = addrsAlive
73                         }
74                         MCDAddrsM.Unlock()
75                 }
76         }()
77 }
78
79 func (ctx *Ctx) MCDRx(ifiName string) error {
80         ifi, err := net.InterfaceByName(ifiName)
81         if err != nil {
82                 return err
83         }
84         addr := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
85         conn, err := net.ListenMulticastUDP("udp", ifi, addr)
86         if err != nil {
87                 return err
88         }
89         go func() {
90                 buf := make([]byte, mcdPktSize)
91                 var n int
92                 var mcd MCD
93         ListenCycle:
94                 for {
95                         les := LEs{{"If", ifiName}}
96                         n, addr, err = conn.ReadFromUDP(buf)
97                         if err != nil {
98                                 ctx.LogE("mcd", les, err, func(les LEs) string {
99                                         return fmt.Sprintf("MCD Rx %s/%d", ifiName, MCDPort)
100                                 })
101                                 continue
102                         }
103                         if n != mcdPktSize {
104                                 ctx.LogD("mcd", les, func(les LEs) string {
105                                         return fmt.Sprintf(
106                                                 "MCD Rx %s/%d: got packet with invalid size",
107                                                 ifiName, MCDPort,
108                                         )
109                                 })
110                                 continue
111                         }
112                         _, err = xdr.Unmarshal(bytes.NewReader(buf[:n]), &mcd)
113                         if err != nil {
114                                 ctx.LogD("mcd", les, func(les LEs) string {
115                                         return fmt.Sprintf(
116                                                 "MCD Rx %s/%d: can not unmarshal: %s",
117                                                 ifiName, MCDPort, err,
118                                         )
119                                 })
120                                 continue
121                         }
122                         if mcd.Magic != MagicNNCPDv1.B {
123                                 ctx.LogD("mcd", les, func(les LEs) string {
124                                         return fmt.Sprintf(
125                                                 "MCD Rx %s/%d: unexpected magic: %s",
126                                                 ifiName, MCDPort, hex.EncodeToString(mcd.Magic[:]),
127                                         )
128                                 })
129                                 continue
130                         }
131                         node, known := ctx.Neigh[*mcd.Sender]
132                         if known {
133                                 les = append(les, LE{"Node", node.Id})
134                                 ctx.LogD("mcd", les, func(les LEs) string {
135                                         return fmt.Sprintf(
136                                                 "MCD Rx %s/%d: %s: node %s",
137                                                 ifiName, MCDPort, addr, node.Name,
138                                         )
139                                 })
140                         } else {
141                                 ctx.LogD("mcd", les, func(les LEs) string {
142                                         return fmt.Sprintf(
143                                                 "MCD Rx %s/%d: %s: unknown node %s",
144                                                 ifiName, MCDPort, addr, node.Id.String(),
145                                         )
146                                 })
147                                 continue
148                         }
149                         MCDAddrsM.RLock()
150                         for _, mcdAddr := range MCDAddrs[*mcd.Sender] {
151                                 if mcdAddr.Addr.IP.Equal(addr.IP) &&
152                                         mcdAddr.Addr.Port == addr.Port &&
153                                         mcdAddr.Addr.Zone == addr.Zone {
154                                         mcdAddr.lastSeen = time.Now()
155                                         MCDAddrsM.RUnlock()
156                                         continue ListenCycle
157                                 }
158                         }
159                         MCDAddrsM.RUnlock()
160                         MCDAddrsM.Lock()
161                         MCDAddrs[*mcd.Sender] = append(
162                                 MCDAddrs[*mcd.Sender],
163                                 &MCDAddr{Addr: *addr, lastSeen: time.Now()},
164                         )
165                         MCDAddrsM.Unlock()
166                         ctx.LogI("mcd-add", les, func(les LEs) string {
167                                 return fmt.Sprintf("MCD discovered %s's address: %s", node.Name, addr)
168                         })
169                 }
170         }()
171         return nil
172 }
173
174 func (ctx *Ctx) MCDTx(ifiName string, port int, interval time.Duration) error {
175         ifi, err := net.InterfaceByName(ifiName)
176         if err != nil {
177                 return err
178         }
179         addr := &net.UDPAddr{IP: mcdIP, Port: port, Zone: ifiName}
180         conn, err := net.ListenMulticastUDP("udp", ifi, addr)
181         if err != nil {
182                 return err
183         }
184
185         dst := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
186         var buf bytes.Buffer
187         mcd := MCD{Magic: MagicNNCPDv1.B, Sender: ctx.Self.Id}
188         if _, err := xdr.Marshal(&buf, mcd); err != nil {
189                 panic(err)
190         }
191         if interval == 0 {
192                 _, err = conn.WriteTo(buf.Bytes(), dst)
193                 return err
194         }
195         go func() {
196                 les := LEs{{"If", ifiName}}
197                 for {
198                         ctx.LogD("mcd", les, func(les LEs) string {
199                                 return fmt.Sprintf(
200                                         "MCD Tx %s/%d/%d",
201                                         ifiName, MCDPort, port,
202                                 )
203                         })
204                         _, err = conn.WriteTo(buf.Bytes(), dst)
205                         if err != nil {
206                                 ctx.LogE("mcd", les, err, func(les LEs) string {
207                                         return fmt.Sprintf("MCD on %s/%d/%d", ifiName, MCDPort, port)
208                                 })
209                         }
210                         time.Sleep(interval)
211                 }
212         }()
213         return nil
214 }