1 // NNCP -- Node to Node copy, utilities for store-and-forward data exchange
2 // Copyright (C) 2016-2024 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
26 xdr "github.com/davecgh/go-xdr/xdr2"
42 mcdIP = net.ParseIP("ff02::4e4e:4350")
43 mcdAddrLifetime = 2 * time.Minute
46 MCDAddrs map[NodeId][]*MCDAddr
47 MCDAddrsM sync.RWMutex
53 mcd := MCD{Sender: nodeId}
54 if _, err := xdr.Marshal(&buf, mcd); err != nil {
57 mcdPktSize = buf.Len()
59 MCDAddrs = make(map[NodeId][]*MCDAddr)
62 time.Sleep(time.Minute)
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)
72 MCDAddrs[nodeId] = addrsAlive
79 func (ctx *Ctx) MCDRx(ifiName string) error {
80 ifi, err := net.InterfaceByName(ifiName)
84 addr := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
85 conn, err := net.ListenMulticastUDP("udp", ifi, addr)
90 buf := make([]byte, mcdPktSize)
95 les := LEs{{"If", ifiName}}
96 n, addr, err = conn.ReadFromUDP(buf)
98 ctx.LogE("mcd", les, err, func(les LEs) string {
99 return fmt.Sprintf("MCD Rx %s/%d", ifiName, MCDPort)
104 ctx.LogD("mcd", les, func(les LEs) string {
106 "MCD Rx %s/%d: got packet with invalid size",
112 _, err = xdr.Unmarshal(bytes.NewReader(buf[:n]), &mcd)
114 ctx.LogD("mcd", les, func(les LEs) string {
116 "MCD Rx %s/%d: can not unmarshal: %s",
117 ifiName, MCDPort, err,
122 if mcd.Magic != MagicNNCPDv1.B {
123 ctx.LogD("mcd", les, func(les LEs) string {
125 "MCD Rx %s/%d: unexpected magic: %s",
126 ifiName, MCDPort, hex.EncodeToString(mcd.Magic[:]),
131 node, known := ctx.Neigh[*mcd.Sender]
133 les = append(les, LE{"Node", node.Id})
134 ctx.LogD("mcd", les, func(les LEs) string {
136 "MCD Rx %s/%d: %s: node %s",
137 ifiName, MCDPort, addr, node.Name,
141 ctx.LogD("mcd", les, func(les LEs) string {
143 "MCD Rx %s/%d: %s: unknown node %s",
144 ifiName, MCDPort, addr, node.Id.String(),
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()
161 MCDAddrs[*mcd.Sender] = append(
162 MCDAddrs[*mcd.Sender],
163 &MCDAddr{Addr: *addr, lastSeen: time.Now()},
166 ctx.LogI("mcd-add", les, func(les LEs) string {
167 return fmt.Sprintf("MCD discovered %s's address: %s", node.Name, addr)
174 func (ctx *Ctx) MCDTx(ifiName string, port int, interval time.Duration) error {
175 ifi, err := net.InterfaceByName(ifiName)
179 addr := &net.UDPAddr{IP: mcdIP, Port: port, Zone: ifiName}
180 conn, err := net.ListenMulticastUDP("udp", ifi, addr)
185 dst := &net.UDPAddr{IP: mcdIP, Port: MCDPort, Zone: ifiName}
187 mcd := MCD{Magic: MagicNNCPDv1.B, Sender: ctx.Self.Id}
188 if _, err := xdr.Marshal(&buf, mcd); err != nil {
192 _, err = conn.WriteTo(buf.Bytes(), dst)
196 les := LEs{{"If", ifiName}}
198 ctx.LogD("mcd", les, func(les LEs) string {
201 ifiName, MCDPort, port,
204 _, err = conn.WriteTo(buf.Bytes(), dst)
206 ctx.LogE("mcd", les, err, func(les LEs) string {
207 return fmt.Sprintf("MCD on %s/%d/%d", ifiName, MCDPort, port)