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