github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/endpoint/userspace/device_parser.go (about) 1 /* 2 * Copyright (C) 2020 The "MysteriumNetwork/node" Authors. 3 * 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, either version 3 of the License, or 7 * (at your option) any later version. 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 userspace 19 20 import ( 21 "bufio" 22 "bytes" 23 "encoding/hex" 24 "fmt" 25 "io" 26 "net" 27 "os" 28 "strconv" 29 "time" 30 31 "golang.zx2c4.com/wireguard/wgctrl/wgtypes" 32 ) 33 34 // UserspaceDevice is a WireGuard device. 35 type UserspaceDevice struct { 36 // ListenPort is the device's network listening port. 37 ListenPort int 38 39 // FirewallMark is the device's current firewall mark. 40 // 41 // The firewall mark can be used in conjunction with firewall software to 42 // take action on outgoing WireGuard packets. 43 FirewallMark int 44 45 // Peers is the list of network peers associated with this device. 46 Peers []UserspaceDevicePeer 47 } 48 49 // UserspaceDevicePeer is a WireGuard peer to a Device. 50 type UserspaceDevicePeer struct { 51 // PublicKey is the public key of a peer, computed from its private key. 52 // 53 // PublicKey is always present in a Peer. 54 PublicKey string 55 56 // Endpoint is the most recent source address used for communication by 57 // this Peer. 58 Endpoint *net.UDPAddr 59 60 // PersistentKeepaliveInterval specifies how often an "empty" packet is sent 61 // to a peer to keep a connection alive. 62 // 63 // A value of 0 indicates that persistent keepalives are disabled. 64 PersistentKeepaliveInterval time.Duration 65 66 // LastHandshakeTime indicates the most recent time a handshake was performed 67 // with this peer. 68 // 69 // A zero-value time.Time indicates that no handshake has taken place with 70 // this peer. 71 LastHandshakeTime time.Time 72 73 // ReceiveBytes indicates the number of bytes received from this peer. 74 ReceiveBytes int64 75 76 // TransmitBytes indicates the number of bytes transmitted to this peer. 77 TransmitBytes int64 78 79 // AllowedIPs specifies which IPv4 and IPv6 addresses this peer is allowed 80 // to communicate on. 81 // 82 // 0.0.0.0/0 indicates that all IPv4 addresses are allowed, and ::/0 83 // indicates that all IPv6 addresses are allowed. 84 AllowedIPs []net.IPNet 85 86 // ProtocolVersion specifies which version of the WireGuard protocol is used 87 // for this Peer. 88 // 89 // A value of 0 indicates that the most recent protocol version will be used. 90 ProtocolVersion int 91 } 92 93 // ParseUserspaceDevice parses WireGuard device state buffer. 94 func ParseUserspaceDevice(ipcGetOp func(w io.Writer) error) (*UserspaceDevice, error) { 95 var buf bytes.Buffer 96 writer := bufio.NewWriter(&buf) 97 if err := ipcGetOp(writer); err != nil { 98 return nil, err 99 } 100 if err := writer.Flush(); err != nil { 101 return nil, err 102 } 103 104 var dp deviceParser 105 s := bufio.NewScanner(&buf) 106 for s.Scan() { 107 b := s.Bytes() 108 if len(b) == 0 { 109 // Empty line, done parsing. 110 break 111 } 112 113 // All data is in key=value format. 114 kvs := bytes.Split(b, []byte("=")) 115 if len(kvs) != 2 { 116 return nil, fmt.Errorf("invalid key=value pair: %q", string(b)) 117 } 118 119 dp.Parse(string(kvs[0]), string(kvs[1])) 120 } 121 122 if dp.err != nil { 123 return nil, dp.err 124 } 125 return &dp.d, nil 126 } 127 128 // A deviceParser accumulates information about a Device and its Peers. Adapted from 129 // https://github.com/WireGuard/wgctrl-go/blob/master/internal/wguser/parse.go. 130 type deviceParser struct { 131 d UserspaceDevice 132 err error 133 134 parsePeers bool 135 peers int 136 hsSec, hsNano int 137 } 138 139 // Parse parses a single key/value pair into fields of a Device. 140 func (dp *deviceParser) Parse(key, value string) { 141 switch key { 142 case "errno": 143 // 0 indicates success, anything else returns an error number that matches 144 // definitions from errno.h. 145 if errno := dp.parseInt(value); errno != 0 { 146 dp.err = os.NewSyscallError("read", fmt.Errorf("wguser: errno=%d", errno)) 147 return 148 } 149 case "public_key": 150 // We've either found the first peer or the next peer. Stop parsing 151 // Device fields and start parsing Peer fields, including the public 152 // key indicated here. 153 dp.parsePeers = true 154 dp.peers++ 155 156 dp.d.Peers = append(dp.d.Peers, UserspaceDevicePeer{ 157 PublicKey: dp.parseKey(value), 158 }) 159 return 160 } 161 162 // Are we parsing peer fields? 163 if dp.parsePeers { 164 dp.peerParse(key, value) 165 return 166 } 167 168 // Device field parsing. 169 switch key { 170 case "listen_port": 171 dp.d.ListenPort = dp.parseInt(value) 172 case "fwmark": 173 dp.d.FirewallMark = dp.parseInt(value) 174 } 175 } 176 177 // curPeer returns the current Peer being parsed so its fields can be populated. 178 func (dp *deviceParser) curPeer() *UserspaceDevicePeer { 179 return &dp.d.Peers[dp.peers-1] 180 } 181 182 // peerParse parses a key/value field into the current Peer. 183 func (dp *deviceParser) peerParse(key, value string) { 184 p := dp.curPeer() 185 switch key { 186 case "endpoint": 187 p.Endpoint = dp.parseAddr(value) 188 case "last_handshake_time_sec": 189 dp.hsSec = dp.parseInt(value) 190 case "last_handshake_time_nsec": 191 dp.hsNano = dp.parseInt(value) 192 193 // Assume that we've seen both seconds and nanoseconds and populate this 194 // field now. However, if both fields were set to 0, assume we have never 195 // had a successful handshake with this peer, and return a zero-value 196 // time.Time to our callers. 197 if dp.hsSec > 0 && dp.hsNano > 0 { 198 p.LastHandshakeTime = time.Unix(int64(dp.hsSec), int64(dp.hsNano)) 199 } 200 case "tx_bytes": 201 p.TransmitBytes = dp.parseInt64(value) 202 case "rx_bytes": 203 p.ReceiveBytes = dp.parseInt64(value) 204 case "persistent_keepalive_interval": 205 p.PersistentKeepaliveInterval = time.Duration(dp.parseInt(value)) * time.Second 206 case "allowed_ip": 207 cidr := dp.parseCIDR(value) 208 if cidr != nil { 209 p.AllowedIPs = append(p.AllowedIPs, *cidr) 210 } 211 case "protocol_version": 212 p.ProtocolVersion = dp.parseInt(value) 213 } 214 } 215 216 // parseKey parses a Key from a hex string. 217 func (dp *deviceParser) parseKey(s string) string { 218 if dp.err != nil { 219 return "" 220 } 221 222 b, err := hex.DecodeString(s) 223 if err != nil { 224 dp.err = err 225 return "" 226 } 227 228 key, err := wgtypes.NewKey(b) 229 if err != nil { 230 dp.err = err 231 return "" 232 } 233 234 return key.String() 235 } 236 237 // parseInt parses an integer from a string. 238 func (dp *deviceParser) parseInt(s string) int { 239 if dp.err != nil { 240 return 0 241 } 242 243 v, err := strconv.Atoi(s) 244 if err != nil { 245 dp.err = err 246 return 0 247 } 248 249 return v 250 } 251 252 // parseInt64 parses an int64 from a string. 253 func (dp *deviceParser) parseInt64(s string) int64 { 254 if dp.err != nil { 255 return 0 256 } 257 258 v, err := strconv.ParseInt(s, 10, 64) 259 if err != nil { 260 dp.err = err 261 return 0 262 } 263 264 return v 265 } 266 267 // parseAddr parses a UDP address from a string. 268 func (dp *deviceParser) parseAddr(s string) *net.UDPAddr { 269 if dp.err != nil { 270 return nil 271 } 272 273 addr, err := net.ResolveUDPAddr("udp", s) 274 if err != nil { 275 dp.err = err 276 return nil 277 } 278 279 return addr 280 } 281 282 // parseInt parses an address CIDR from a string. 283 func (dp *deviceParser) parseCIDR(s string) *net.IPNet { 284 if dp.err != nil { 285 return nil 286 } 287 288 _, cidr, err := net.ParseCIDR(s) 289 if err != nil { 290 dp.err = err 291 return nil 292 } 293 294 return cidr 295 }