github.com/decred/dcrlnd@v0.7.6/netann/host_ann.go (about) 1 package netann 2 3 import ( 4 "net" 5 "sync" 6 7 "github.com/decred/dcrlnd/lnwire" 8 "github.com/decred/dcrlnd/ticker" 9 ) 10 11 // HostAnnouncerConfig is the main config for the HostAnnouncer. 12 type HostAnnouncerConfig struct { 13 // Hosts is the set of hosts we should watch for IP changes. 14 Hosts []string 15 16 // RefreshTicker ticks each time we should check for any address 17 // changes. 18 RefreshTicker ticker.Ticker 19 20 // LookupHost performs DNS resolution on a given host and returns its 21 // addresses. 22 LookupHost func(string) (net.Addr, error) 23 24 // AdvertisedIPs is the set of IPs that we've already announced with 25 // our current NodeAnnouncement. This set will be constructed to avoid 26 // unnecessary node NodeAnnouncement updates. 27 AdvertisedIPs map[string]struct{} 28 29 // AnnounceNewIPs announces a new set of IP addresses for the backing 30 // Lightning node. The first set of addresses is the new set of 31 // addresses that we should advertise, while the other set are the 32 // stale addresses that we should no longer advertise. 33 AnnounceNewIPs func([]net.Addr, map[string]struct{}) error 34 } 35 36 // HostAnnouncer is a sub-system that allows a user to specify a set of hosts 37 // for lnd that will be continually resolved to notice any IP address changes. 38 // If the target IP address for a host changes, then we'll generate a new 39 // NodeAnnouncement that includes these new IPs. 40 type HostAnnouncer struct { 41 cfg HostAnnouncerConfig 42 43 quit chan struct{} 44 wg sync.WaitGroup 45 46 startOnce sync.Once 47 stopOnce sync.Once 48 } 49 50 // NewHostAnnouncer returns a new instance of the HostAnnouncer. 51 func NewHostAnnouncer(cfg HostAnnouncerConfig) *HostAnnouncer { 52 return &HostAnnouncer{ 53 cfg: cfg, 54 quit: make(chan struct{}), 55 } 56 } 57 58 // Start starts the HostAnnouncer. 59 func (h *HostAnnouncer) Start() error { 60 h.startOnce.Do(func() { 61 h.wg.Add(1) 62 go h.hostWatcher() 63 }) 64 65 return nil 66 } 67 68 // Stop signals the HostAnnouncer for a graceful stop. 69 func (h *HostAnnouncer) Stop() error { 70 h.stopOnce.Do(func() { 71 log.Info("HostAnnouncer shutting down") 72 close(h.quit) 73 h.wg.Wait() 74 }) 75 76 return nil 77 } 78 79 // hostWatcher periodically attempts to resolve the IP for each host, updating 80 // them if they change within the interval. 81 func (h *HostAnnouncer) hostWatcher() { 82 defer h.wg.Done() 83 84 ipMapping := make(map[string]net.Addr) 85 refreshHosts := func() { 86 87 // We'll now run through each of our hosts to check if they had 88 // their backing IPs changed. If so, we'll want to re-announce 89 // them. 90 var addrsToUpdate []net.Addr 91 addrsToRemove := make(map[string]struct{}) 92 for _, host := range h.cfg.Hosts { 93 newAddr, err := h.cfg.LookupHost(host) 94 if err != nil { 95 log.Warnf("unable to resolve IP for "+ 96 "host %v: %v", host, err) 97 continue 98 } 99 100 // If nothing has changed since the last time we 101 // checked, then we don't need to do any updates. 102 oldAddr, oldAddrFound := ipMapping[host] 103 if oldAddrFound && oldAddr.String() == newAddr.String() { 104 continue 105 } 106 107 // Update the IP mapping now, as if this is the first 108 // time then we don't need to send a new announcement. 109 ipMapping[host] = newAddr 110 111 // If this IP has already been announced, then we'll 112 // skip it to avoid triggering an unnecessary node 113 // announcement update. 114 _, ipAnnounced := h.cfg.AdvertisedIPs[newAddr.String()] 115 if ipAnnounced { 116 continue 117 } 118 119 // If we've reached this point, then the old address 120 // was found, and the new address we just looked up 121 // differs from the old one. 122 log.Debugf("IP change detected! %v: %v -> %v", host, 123 oldAddr, newAddr) 124 125 // If we had already advertised an addr for this host, 126 // then we'll need to remove that old stale address. 127 if oldAddr != nil { 128 addrsToRemove[oldAddr.String()] = struct{}{} 129 } 130 131 addrsToUpdate = append(addrsToUpdate, newAddr) 132 } 133 134 // If we don't have any addresses to update, then we can skip 135 // things around until the next round. 136 if len(addrsToUpdate) == 0 { 137 log.Debugf("No IP changes detected for hosts: %v", 138 h.cfg.Hosts) 139 return 140 } 141 142 // Now that we know the set of IPs we need to update, we'll do 143 // them all in a single batch. 144 err := h.cfg.AnnounceNewIPs(addrsToUpdate, addrsToRemove) 145 if err != nil { 146 log.Warnf("unable to announce new IPs: %v", err) 147 } 148 } 149 150 refreshHosts() 151 152 h.cfg.RefreshTicker.Resume() 153 154 for { 155 select { 156 case <-h.cfg.RefreshTicker.Ticks(): 157 log.Debugf("HostAnnouncer checking for any IP " + 158 "changes...") 159 160 refreshHosts() 161 162 case <-h.quit: 163 return 164 } 165 } 166 } 167 168 // NodeAnnUpdater describes a function that's able to update our current node 169 // announcement on disk. It returns the updated node announcement given a set 170 // of updates to be applied to the current node announcement. 171 type NodeAnnUpdater func(refresh bool, modifier ...NodeAnnModifier, 172 ) (lnwire.NodeAnnouncement, error) 173 174 // IPAnnouncer is a factory function that generates a new function that uses 175 // the passed annUpdater function to to announce new IP changes for a given 176 // host. 177 func IPAnnouncer(annUpdater NodeAnnUpdater) func([]net.Addr, map[string]struct{}) error { 178 return func(newAddrs []net.Addr, oldAddrs map[string]struct{}) error { 179 _, err := annUpdater(true, func(currentNodeAnn *lnwire.NodeAnnouncement) { 180 // To ensure we don't duplicate any addresses, we'll 181 // filter out the same of addresses we should no longer 182 // advertise. 183 filteredAddrs := make( 184 []net.Addr, 0, len(currentNodeAnn.Addresses), 185 ) 186 for _, addr := range currentNodeAnn.Addresses { 187 if _, ok := oldAddrs[addr.String()]; ok { 188 continue 189 } 190 191 filteredAddrs = append(filteredAddrs, addr) 192 } 193 194 filteredAddrs = append(filteredAddrs, newAddrs...) 195 currentNodeAnn.Addresses = filteredAddrs 196 }) 197 return err 198 } 199 }