]> Cypherpunks.ru repositories - nncp.git/blob - src/mcd.go
Do not use multiline const-s with single entry
[nncp.git] / src / mcd.go
1 /*
2 NNCP -- Node to Node copy, utilities for store-and-forward data exchange
3 Copyright (C) 2016-2023 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 MCDPort = 5400
32
33 type MCD struct {
34         Magic  [8]byte
35         Sender *NodeId
36 }
37
38 type MCDAddr struct {
39         Addr     net.UDPAddr
40         lastSeen time.Time
41 }
42
43 var (
44         mcdIP           = net.ParseIP("ff02::4e4e:4350")
45         mcdAddrLifetime = 2 * time.Minute
46
47         mcdPktSize int
48         MCDAddrs   map[NodeId][]*MCDAddr
49         MCDAddrsM  sync.RWMutex
50 )
51
52 func init() {
53         nodeId := new(NodeId)
54         var buf bytes.Buffer
55         mcd := MCD{Sender: nodeId}
56         if _, err := xdr.Marshal(&buf, mcd); err != nil {
57                 panic(err)
58         }
59         mcdPktSize = buf.Len()
60
61         MCDAddrs = make(map[NodeId][]*MCDAddr)
62         go func() {
63                 for {
64                         time.Sleep(time.Minute)
65                         MCDAddrsM.Lock()
66                         now := time.Now()
67                         for nodeId, addrs := range MCDAddrs {
68                                 addrsAlive := make([]*MCDAddr, 0, len(addrs))
69                                 for _, addr := range addrs {
70                                         if !addr.lastSeen.Add(mcdAddrLifetime).Before(now) {
71                                                 addrsAlive = append(addrsAlive, addr)
72                                         }
73                                 }
74                                 MCDAddrs[nodeId] = addrsAlive
75                         }
76                         MCDAddrsM.Unlock()
77                 }
78         }()
79 }
80
81 func (ctx *Ctx) MCDRx(ifiName string) error {
82         ifi, err := net.InterfaceByName(ifiName)
83         if err != nil {
84                 return err
85         }
86         addr := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
87         conn, err := net.ListenMulticastUDP("udp", ifi, addr)
88         if err != nil {
89                 return err
90         }
91         go func() {
92                 buf := make([]byte, mcdPktSize)
93                 var n int
94                 var mcd MCD
95         ListenCycle:
96                 for {
97                         les := LEs{{"If", ifiName}}
98                         n, addr, err = conn.ReadFromUDP(buf)
99                         if err != nil {
100                                 ctx.LogE("mcd", les, err, func(les LEs) string {
101                                         return fmt.Sprintf("MCD Rx %s/%d", ifiName, MCDPort)
102                                 })
103                                 continue
104                         }
105                         if n != mcdPktSize {
106                                 ctx.LogD("mcd", les, func(les LEs) string {
107                                         return fmt.Sprintf(
108                                                 "MCD Rx %s/%d: got packet with invalid size",
109                                                 ifiName, MCDPort,
110                                         )
111                                 })
112                                 continue
113                         }
114                         _, err = xdr.Unmarshal(bytes.NewReader(buf[:n]), &mcd)
115                         if err != nil {
116                                 ctx.LogD("mcd", les, func(les LEs) string {
117                                         return fmt.Sprintf(
118                                                 "MCD Rx %s/%d: can not unmarshal: %s",
119                                                 ifiName, MCDPort, err,
120                                         )
121                                 })
122                                 continue
123                         }
124                         if mcd.Magic != MagicNNCPDv1.B {
125                                 ctx.LogD("mcd", les, func(les LEs) string {
126                                         return fmt.Sprintf(
127                                                 "MCD Rx %s/%d: unexpected magic: %s",
128                                                 ifiName, MCDPort, hex.EncodeToString(mcd.Magic[:]),
129                                         )
130                                 })
131                                 continue
132                         }
133                         node, known := ctx.Neigh[*mcd.Sender]
134                         if known {
135                                 les = append(les, LE{"Node", node.Id})
136                                 ctx.LogD("mcd", les, func(les LEs) string {
137                                         return fmt.Sprintf(
138                                                 "MCD Rx %s/%d: %s: node %s",
139                                                 ifiName, MCDPort, addr, node.Name,
140                                         )
141                                 })
142                         } else {
143                                 ctx.LogD("mcd", les, func(les LEs) string {
144                                         return fmt.Sprintf(
145                                                 "MCD Rx %s/%d: %s: unknown node %s",
146                                                 ifiName, MCDPort, addr, node.Id.String(),
147                                         )
148                                 })
149                                 continue
150                         }
151                         MCDAddrsM.RLock()
152                         for _, mcdAddr := range MCDAddrs[*mcd.Sender] {
153                                 if mcdAddr.Addr.IP.Equal(addr.IP) &&
154                                         mcdAddr.Addr.Port == addr.Port &&
155                                         mcdAddr.Addr.Zone == addr.Zone {
156                                         mcdAddr.lastSeen = time.Now()
157                                         MCDAddrsM.RUnlock()
158                                         continue ListenCycle
159                                 }
160                         }
161                         MCDAddrsM.RUnlock()
162                         MCDAddrsM.Lock()
163                         MCDAddrs[*mcd.Sender] = append(
164                                 MCDAddrs[*mcd.Sender],
165                                 &MCDAddr{Addr: *addr, lastSeen: time.Now()},
166                         )
167                         MCDAddrsM.Unlock()
168                         ctx.LogI("mcd-add", les, func(les LEs) string {
169                                 return fmt.Sprintf("MCD discovered %s's address: %s", node.Name, addr)
170                         })
171                 }
172         }()
173         return nil
174 }
175
176 func (ctx *Ctx) MCDTx(ifiName string, port int, interval time.Duration) error {
177         ifi, err := net.InterfaceByName(ifiName)
178         if err != nil {
179                 return err
180         }
181         addr := &net.UDPAddr{IP: mcdIP, Port: port, Zone: ifiName}
182         conn, err := net.ListenMulticastUDP("udp", ifi, addr)
183         if err != nil {
184                 return err
185         }
186
187         dst := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
188         var buf bytes.Buffer
189         mcd := MCD{Magic: MagicNNCPDv1.B, Sender: ctx.Self.Id}
190         if _, err := xdr.Marshal(&buf, mcd); err != nil {
191                 panic(err)
192         }
193         if interval == 0 {
194                 _, err = conn.WriteTo(buf.Bytes(), dst)
195                 return err
196         }
197         go func() {
198                 les := LEs{{"If", ifiName}}
199                 for {
200                         ctx.LogD("mcd", les, func(les LEs) string {
201                                 return fmt.Sprintf(
202                                         "MCD Tx %s/%d/%d",
203                                         ifiName, MCDPort, port,
204                                 )
205                         })
206                         _, err = conn.WriteTo(buf.Bytes(), dst)
207                         if err != nil {
208                                 ctx.LogE("mcd", les, err, func(les LEs) string {
209                                         return fmt.Sprintf("MCD on %s/%d/%d", ifiName, MCDPort, port)
210                                 })
211                         }
212                         time.Sleep(interval)
213                 }
214         }()
215         return nil
216 }