github.com/transparency-dev/armored-witness-applet@v0.1.1/trusted_applet/net.go (about) 1 // Copyright 2022 The Armored Witness Applet authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "context" 19 "crypto/sha256" 20 "encoding/binary" 21 "errors" 22 "fmt" 23 "net" 24 "net/http" 25 "time" 26 27 "gvisor.dev/gvisor/pkg/buffer" 28 "gvisor.dev/gvisor/pkg/tcpip" 29 "gvisor.dev/gvisor/pkg/tcpip/header" 30 "gvisor.dev/gvisor/pkg/tcpip/link/channel" 31 "gvisor.dev/gvisor/pkg/tcpip/network/arp" 32 "gvisor.dev/gvisor/pkg/tcpip/network/ipv4" 33 "gvisor.dev/gvisor/pkg/tcpip/stack" 34 "gvisor.dev/gvisor/pkg/tcpip/transport/icmp" 35 "gvisor.dev/gvisor/pkg/tcpip/transport/tcp" 36 "gvisor.dev/gvisor/pkg/tcpip/transport/udp" 37 "k8s.io/klog/v2" 38 39 "github.com/beevik/ntp" 40 "github.com/transparency-dev/armored-witness-applet/third_party/dhcp" 41 "github.com/transparency-dev/armored-witness-os/api" 42 "go.mercari.io/go-dnscache" 43 44 "github.com/usbarmory/GoTEE/applet" 45 "github.com/usbarmory/GoTEE/syscall" 46 enet "github.com/usbarmory/imx-enet" 47 ) 48 49 // default Trusted Applet network settings 50 const ( 51 DHCP = true 52 IP = "10.0.0.1" 53 Netmask = "255.255.255.0" 54 Gateway = "10.0.0.2" 55 DefaultResolver = "8.8.8.8:53" 56 DefaultNTP = "time.google.com" 57 58 nicID = tcpip.NICID(1) 59 60 // Timeout for any http requests. 61 httpTimeout = 30 * time.Second 62 63 // DNS cache settings. 64 dnsUpdateFreq = 1 * time.Minute 65 dnsUpdateTimeout = 5 * time.Second 66 ) 67 68 // Trusted OS syscalls 69 const ( 70 RX = 0x10000000 71 TX = 0x10000001 72 FIQ = 0x10000002 73 FREQ = 0x10000003 74 ) 75 76 var ( 77 iface *enet.Interface 78 ) 79 80 func init() { 81 net.DefaultNS = []string{DefaultResolver} 82 } 83 84 // runDHCP starts the dhcp client. 85 // 86 // When an IP is successfully leased and configured on the interface, f is called with a context 87 // which will become Done when the leased address expires. Callers can use this as a mechanism to 88 // ensure that networking clients/services are only run while a leased IP is held. 89 // 90 // This function blocks until the passed-in ctx is Done. 91 func runDHCP(ctx context.Context, nicID tcpip.NICID, clientID string, hostname string, f func(context.Context) error) { 92 // This context tracks the lifetime of the IP lease we get (if any) from the DHCP server. 93 // We'll only know what that lease is once we acquire the new IP, which happens inside 94 // the aquired func below. 95 var ( 96 childCtx context.Context 97 cancelChild context.CancelFunc 98 ) 99 // fDone is used to ensure that we wait for the passed-in func f to complete before 100 // make changes to the network stack or attempt to rerun f when we've acquired a new lease. 101 fDone := make(chan bool, 1) 102 defer close(fDone) 103 104 // acquired handles our dhcp.Client events - acquiring, releasing, renewing DHCP leases. 105 acquired := func(oldAddr, newAddr tcpip.AddressWithPrefix, cfg dhcp.Config) { 106 klog.Infof("DHCPC: lease update - old: %v, new: %v", oldAddr.String(), newAddr.String()) 107 // Handled renewals first, old and new addresses will be equivalent in this case. 108 // We may still have to reconfigure the networking stack, even though our assigned IP 109 // isn't changing, the DHCP server could have changed routing or DNS info. 110 if oldAddr.Address == newAddr.Address && oldAddr.PrefixLen == newAddr.PrefixLen { 111 klog.Infof("DHCPC: existing lease on %v renewed", newAddr.String()) 112 // reconfigure network stuff in-case DNS or gateway routes have changed. 113 configureNetFromDHCP(newAddr, cfg) 114 // f should already be running, no need to interfere with it. 115 return 116 } 117 118 // If oldAddr is specified, then our lease on that address has experied - remove it 119 // from our stack. 120 if !oldAddr.Address.Unspecified() { 121 // Since we're changing our primary IP address we must tell f to exit, 122 // and wait for it to do so 123 cancelChild() 124 klog.Info("Waiting for child to complete...") 125 <-fDone 126 127 klog.Infof("DHCPC: Releasing %v", oldAddr.String()) 128 if err := iface.Stack.RemoveAddress(nicID, oldAddr.Address); err != nil { 129 klog.Errorf("Failed to remove expired address from stack: %v", err) 130 } 131 } 132 133 // If newAddr is specified, then we've been granted a lease on a new IP address, so 134 // we'll configure our stack to use it, along with whatever routes and DNS info 135 // we've been sent. 136 if !newAddr.Address.Unspecified() { 137 klog.Infof("DHCPC: Acquired %v", newAddr.String()) 138 139 newProtoAddr := tcpip.ProtocolAddress{ 140 Protocol: ipv4.ProtocolNumber, 141 AddressWithPrefix: newAddr, 142 } 143 if err := iface.Stack.AddProtocolAddress(nicID, newProtoAddr, stack.AddressProperties{PEB: stack.FirstPrimaryEndpoint}); err != nil { 144 klog.Errorf("Failed to add newly acquired address to stack: %v", err) 145 } else { 146 configureNetFromDHCP(newAddr, cfg) 147 148 // Set up a context we'll use to control f's execution lifetime. 149 // This will get canceled above if/when our IP lease expires. 150 childCtx, cancelChild = context.WithCancel(ctx) 151 152 // And execute f in its own goroutine so we don't block the dhcp.Client. 153 go func(childCtx context.Context) { 154 // Signal when we exit: 155 defer func() { fDone <- true }() 156 157 klog.Info("DHCP: running f") 158 for { 159 if err := f(childCtx); err != nil { 160 klog.Errorf("runDHCP f: %v", err) 161 if errors.Is(err, context.Canceled) { 162 break 163 } 164 } 165 } 166 }(childCtx) 167 } 168 } else { 169 klog.Infof("DHCPC: no address acquired") 170 } 171 } 172 173 // Start the DHCP client. 174 c := dhcp.NewClient(iface.Stack, nicID, iface.Link.LinkAddress(), clientID, hostname, 30*time.Second, time.Second, time.Second, acquired) 175 klog.Info("Starting DHCPClient...") 176 c.Run(ctx) 177 } 178 179 // configureNetFromDHCP sets up network related configuration, e.g. DNS servers, 180 // gateway routes, etc. based on config received from the DHCP server. 181 // Note that this function does not update the network stack's assigned IP address. 182 func configureNetFromDHCP(newAddr tcpip.AddressWithPrefix, cfg dhcp.Config) { 183 if len(cfg.DNS) > 0 { 184 resolvers := []string{} 185 for _, r := range cfg.DNS { 186 resolver := fmt.Sprintf("%s:53", r.String()) 187 resolvers = append(resolvers, resolver) 188 } 189 klog.Infof("DHCPC: Using DNS server(s) %v", resolvers) 190 net.DefaultNS = resolvers 191 } 192 // Set up routing for new address 193 // Start with the implicit route to local segment 194 table := []tcpip.Route{ 195 {Destination: newAddr.Subnet(), NIC: nicID}, 196 } 197 // add any additional routes from the DHCP server 198 if len(cfg.Router) > 0 { 199 for _, gw := range cfg.Router { 200 table = append(table, tcpip.Route{Destination: header.IPv4EmptySubnet, Gateway: gw, NIC: nicID}) 201 klog.Infof("DHCPC: Using Gateway %v", gw) 202 } 203 } 204 iface.Stack.SetRouteTable(table) 205 } 206 207 // runNTP starts periodically attempting to sync the system time with NTP. 208 // Returns a channel which become closed once we have obtained an initial time. 209 func runNTP(ctx context.Context) chan bool { 210 if cfg.NTPServer == "" { 211 klog.Info("NTP disabled.") 212 return nil 213 } 214 215 r := make(chan bool) 216 217 go func(ctx context.Context) { 218 // i specifies the interval between checking in with the NTP server. 219 // Initially we'll check in more frequently until we have set a time. 220 i := time.Second * 10 221 for { 222 select { 223 case <-ctx.Done(): 224 return 225 case <-time.After(i): 226 } 227 228 ip, err := net.DefaultResolver.LookupIP(ctx, "ip4", cfg.NTPServer) 229 if err != nil { 230 klog.Errorf("Failed to resolve NTP server %q: %v", DefaultNTP, err) 231 continue 232 } 233 ntpR, err := ntp.QueryWithOptions( 234 ip[0].String(), 235 ntp.QueryOptions{}, 236 ) 237 if err != nil { 238 klog.Errorf("Failed to get NTP time: %v", err) 239 continue 240 } 241 if err := ntpR.Validate(); err != nil { 242 klog.Errorf("got invalid time from NTP server: %v", err) 243 continue 244 } 245 applet.ARM.SetTimer(ntpR.Time.UnixNano()) 246 247 // We've got some sort of sensible time set now, so check in with NTP 248 // much less frequently. 249 i = time.Hour 250 if r != nil { 251 // Signal that we've got an initial time. 252 close(r) 253 r = nil 254 } 255 } 256 }(ctx) 257 258 return r 259 } 260 261 func rxFromEth(buf []byte) int { 262 n := syscall.Read(RX, buf, uint(len(buf))) 263 264 if n == 0 || n > int(enet.MTU) { 265 return 0 266 } 267 268 return n 269 } 270 271 func rx(buf []byte) { 272 if len(buf) < 14 { 273 return 274 } 275 276 hdr := buf[0:14] 277 proto := tcpip.NetworkProtocolNumber(binary.BigEndian.Uint16(buf[12:14])) 278 payload := buf[14:] 279 280 pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{ 281 ReserveHeaderBytes: len(hdr), 282 Payload: buffer.MakeWithData(payload), 283 }) 284 285 copy(pkt.LinkHeader().Push(len(hdr)), hdr) 286 287 iface.Link.InjectInbound(proto, pkt) 288 } 289 290 func tx() (buf []byte) { 291 var pkt *stack.PacketBuffer 292 293 if pkt = iface.NIC.Link.Read(); pkt.IsNil() { 294 return 295 } 296 297 proto := make([]byte, 2) 298 binary.BigEndian.PutUint16(proto, uint16(pkt.NetworkProtocolNumber)) 299 300 // Ethernet frame header 301 buf = append(buf, pkt.EgressRoute.RemoteLinkAddress...) 302 buf = append(buf, iface.NIC.MAC...) 303 buf = append(buf, proto...) 304 305 for _, v := range pkt.AsSlices() { 306 buf = append(buf, v...) 307 } 308 309 return 310 } 311 312 type txNotification struct{} 313 314 func (n *txNotification) WriteNotify() { 315 buf := tx() 316 syscall.Write(TX, buf, uint(len(buf))) 317 } 318 319 // mac creates a stable "local administered" MAC address for the network based on the 320 // provided unit serial number. 321 func mac(serial string) string { 322 m := sha256.Sum256([]byte(fmt.Sprintf("MAC:%s", serial))) 323 // The first byte of the MAC address has a couple of flags which must be set correctly: 324 // - Unicast(0)/multicast(1) in the least significant bit of the byte. 325 // This must be set to unicast. 326 // - Universally unique(0)/Local administered(1) in the second least significant bit. 327 // Since we're not using an organisationally unique prefix triplet, this must be set to 328 // Locally administered 329 m[0] &= 0xfe 330 m[0] |= 0x02 331 return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", m[0], m[1], m[2], m[3], m[4], m[5]) 332 } 333 334 func startNetworking() (err error) { 335 // Set the default resolver from the config, if we're using DHCP this may be updated. 336 net.DefaultNS = []string{cfg.Resolver} 337 338 var status api.Status 339 if err := syscall.Call("RPC.Status", nil, &status); err != nil { 340 return fmt.Errorf("failed to fetch Status: %v", err) 341 } 342 343 iface = &enet.Interface{ 344 Stack: stack.New(stack.Options{ 345 NetworkProtocols: []stack.NetworkProtocolFactory{ 346 ipv4.NewProtocol, 347 arp.NewProtocol, 348 }, 349 TransportProtocols: []stack.TransportProtocolFactory{ 350 tcp.NewProtocol, 351 icmp.NewProtocol4, 352 udp.NewProtocol, 353 }, 354 }), 355 } 356 357 if cfg.DHCP { 358 // This is essentially the contents of enet.Init (plus enet.configure) 359 // with anything to do with setting up static IP addresses/routes 360 // stripped out. 361 // 362 // TODO(al): Refactor imx-enet to make this cleaner 363 macAddress := mac(status.Serial) 364 linkAddress, err := net.ParseMAC(macAddress) 365 if err != nil { 366 return fmt.Errorf("invalid MAC: %v", err) 367 } 368 369 if iface.NICID == 0 { 370 iface.NICID = enet.NICID 371 } 372 373 gvHWAddress, err := tcpip.ParseMACAddress(macAddress) 374 if err != nil { 375 return fmt.Errorf("invalid MAC: %v", err) 376 } 377 iface.Link = channel.New(256, enet.MTU, gvHWAddress) 378 iface.Link.LinkEPCapabilities |= stack.CapabilityResolutionRequired 379 380 linkEP := stack.LinkEndpoint(iface.Link) 381 382 if err := iface.Stack.CreateNIC(iface.NICID, linkEP); err != nil { 383 return fmt.Errorf("%v", err) 384 } 385 386 iface.NIC = &enet.NIC{ 387 MAC: linkAddress, 388 Link: iface.Link, 389 Device: nil, 390 } 391 err = iface.NIC.Init() 392 393 } else { 394 if err = iface.Init(nil, cfg.IP, cfg.Netmask, mac(status.Serial), cfg.Gateway); err != nil { 395 return 396 } 397 } 398 399 iface.EnableICMP() 400 iface.Link.AddNotify(&txNotification{}) 401 402 resolver, err := dnscache.New(dnsUpdateFreq, dnsUpdateTimeout) 403 if err != nil { 404 return fmt.Errorf("failed to create DNS cache: %v", err) 405 } 406 // hook interface into Go runtime 407 net.SocketFunc = iface.Socket 408 http.DefaultClient = &http.Client{ 409 Timeout: httpTimeout, 410 Transport: &http.Transport{ 411 DialContext: dnscache.DialFunc(resolver, (&net.Dialer{ 412 Timeout: 30 * time.Second, 413 KeepAlive: 30 * time.Second, 414 }).DialContext), 415 DisableKeepAlives: true, 416 ForceAttemptHTTP2: false, 417 MaxIdleConns: 100, 418 IdleConnTimeout: 90 * time.Second, 419 ResponseHeaderTimeout: 10 * time.Second, 420 TLSHandshakeTimeout: 10 * time.Second, 421 ExpectContinueTimeout: 1 * time.Second, 422 }, 423 } 424 425 return 426 }