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