github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/router/router_desktop.go (about) 1 //go:build !android 2 3 /* 4 * Copyright (C) 2021 The "MysteriumNetwork/node" Authors. 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package router 21 22 import ( 23 "fmt" 24 "net" 25 "sync" 26 "time" 27 28 "github.com/rs/zerolog/log" 29 30 "github.com/mysteriumnetwork/node/config" 31 "github.com/mysteriumnetwork/node/router/network" 32 ) 33 34 type manager struct { 35 mu sync.Mutex 36 once sync.Once 37 38 rules []rule 39 currentGW net.IP 40 41 routingTable router 42 43 gwCheckInterval time.Duration 44 45 onceStop sync.Once 46 stop chan struct{} 47 } 48 49 type router interface { 50 DiscoverGateway() (net.IP, error) 51 ExcludeRule(ip, gw net.IP) error 52 DeleteRule(ip, gw net.IP) error 53 } 54 55 type rule struct { 56 ip net.IP 57 usage int 58 } 59 60 // NewManager creates a new instance of service that maintain routing table to match current state. 61 func NewManager() *manager { 62 var r router = &network.RoutingTable{} 63 64 if config.GetBool(config.FlagUserMode) || config.GetBool(config.FlagUserspace) { 65 r = &network.RoutingTableRemote{} 66 } 67 68 return &manager{ 69 stop: make(chan struct{}), 70 71 gwCheckInterval: 5 * time.Second, 72 routingTable: r, 73 } 74 } 75 76 func (m *manager) ExcludeIP(ip net.IP) error { 77 m.ensureStarted() 78 m.mu.Lock() 79 defer m.mu.Unlock() 80 81 new := true 82 83 for i, rule := range m.rules { 84 if !rule.ip.Equal(ip) { 85 continue 86 } 87 88 new = false 89 m.rules[i].usage++ 90 91 break 92 } 93 94 if !new { 95 return nil 96 } 97 98 if err := m.routingTable.ExcludeRule(ip, m.currentGW); err != nil { 99 return fmt.Errorf("failed to exclude rule: %w", err) 100 } 101 102 m.rules = append(m.rules, rule{ 103 ip: ip, 104 usage: 1, 105 }) 106 107 return nil 108 } 109 110 func (m *manager) RemoveExcludedIP(ip net.IP) error { 111 m.mu.Lock() 112 defer m.mu.Unlock() 113 114 for i, rule := range m.rules { 115 if !rule.ip.Equal(ip) { 116 continue 117 } 118 119 m.rules[i].usage-- 120 121 if m.rules[i].usage == 0 { 122 m.rules = append(m.rules[:i], m.rules[i+1:]...) 123 124 if err := m.routingTable.DeleteRule(ip, m.currentGW); err != nil { 125 return fmt.Errorf("failed to remove excluded rule: %w", err) 126 } 127 } 128 129 break 130 } 131 132 return nil 133 } 134 135 func (m *manager) ensureStarted() { 136 m.once.Do(func() { 137 m.forceCheckGW() 138 139 go m.start() 140 }) 141 } 142 143 func (m *manager) start() { 144 for { 145 select { 146 case <-time.After(m.gwCheckInterval): 147 m.checkGW() 148 case <-m.stop: 149 return 150 } 151 } 152 } 153 154 func (m *manager) Stop() { 155 if err := m.Clean(); err != nil { 156 log.Error().Err(err).Msg("Failed to clean routing rules") 157 } 158 159 m.onceStop.Do(func() { 160 close(m.stop) 161 }) 162 } 163 164 func (m *manager) Clean() (lastErr error) { 165 m.mu.Lock() 166 defer m.mu.Unlock() 167 168 if err := m.clean(); err != nil { 169 return fmt.Errorf("failed to clean routes: %w", err) 170 } 171 172 m.rules = nil 173 174 return nil 175 } 176 177 func (m *manager) clean() (lastErr error) { 178 for _, rule := range m.rules { 179 err := m.routingTable.DeleteRule(rule.ip, m.currentGW) 180 if err != nil { 181 lastErr = err 182 log.Error().Err(err).Msgf("Failed to delete route: %+v", rule) 183 } 184 } 185 186 return lastErr 187 } 188 189 func (m *manager) apply(gw net.IP) (lastErr error) { 190 for _, rule := range m.rules { 191 err := m.routingTable.ExcludeRule(rule.ip, gw) 192 if err != nil { 193 lastErr = err 194 log.Error().Err(err).Msgf("Failed to delete route: %+v", rule) 195 } 196 } 197 198 m.currentGW = gw 199 200 return lastErr 201 } 202 203 func (m *manager) forceCheckGW() { 204 var currentGW net.IP 205 206 for currentGW == nil { 207 m.checkGW() 208 209 m.mu.Lock() 210 currentGW = m.currentGW 211 m.mu.Unlock() 212 } 213 } 214 215 func (m *manager) checkGW() { 216 gw, err := m.routingTable.DiscoverGateway() 217 if err != nil { 218 log.Error().Err(err).Msg("Failed to detect system default gateway, keeping old value") 219 return 220 } 221 222 if !m.currentGW.Equal(gw) && !gw.Equal(net.IPv4zero) { 223 m.mu.Lock() 224 defer m.mu.Unlock() 225 226 log.Info().Msgf("Default gateway changed to %s, reconfiguring routes.", gw) 227 228 if err := m.clean(); err != nil { 229 log.Error().Err(err).Msg("Failed to clean routing rules") 230 } 231 232 if err := m.apply(gw); err != nil { 233 log.Error().Err(err).Msg("Failed to apply new routing rules") 234 } 235 } 236 }