diff --git a/firewall/interception.go b/firewall/interception.go index 575179be..165af81a 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -196,6 +196,17 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) { return true } + // Submit to ICMP listener. + submitted := netenv.SubmitPacketToICMPListener(pkt) + if submitted { + // If the packet was submitted to the listener, we must not do a + // permanent accept, because then we won't see any future packets of that + // connection and thus cannot continue to submit them. + log.Debugf("filter: fast-track tracing ICMP/v6: %s", pkt) + _ = pkt.Accept() + return true + } + // Handle echo request and replies regularly. // Other ICMP packets are considered system business. icmpLayers := pkt.Layers().LayerClass(layers.LayerClassIPControl) @@ -214,20 +225,8 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) { } } - // Premit all ICMP/v6 packets that are not echo requests or replies. + // Permit all ICMP/v6 packets that are not echo requests or replies. log.Debugf("filter: fast-track accepting ICMP/v6: %s", pkt) - - // Submit to ICMP listener. - submitted := netenv.SubmitPacketToICMPListener(pkt) - - // If the packet was submitted to the listener, we must not do a - // permanent accept, because then we won't see any future packets of that - // connection and thus cannot continue to submit them. - if submitted { - _ = pkt.Accept() - } else { - _ = pkt.PermanentAccept() - } return true case packet.UDP, packet.TCP: diff --git a/go.mod b/go.mod index 22c66d34..840aee7d 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/google/gopacket v1.1.19 github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-version v1.3.0 + github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/miekg/dns v1.1.40 github.com/oschwald/maxminddb-golang v1.8.0 github.com/safing/jess v0.2.3 // indirect diff --git a/go.sum b/go.sum index b10b7dea..297e58f5 100644 --- a/go.sum +++ b/go.sum @@ -37,12 +37,15 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= +github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -200,6 +203,7 @@ github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd github.com/klauspost/cpuid/v2 v2.0.2/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4 h1:g0I61F2K2DjRHz1cnxlkNSBIaePVoJIjjnHui8QHbiw= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54= github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo= github.com/klauspost/reedsolomon v1.9.11 h1:n2kipJFo+CPqg7fH988XJXjqEyj14RJ8BYj7UayxPNg= github.com/klauspost/reedsolomon v1.9.11/go.mod h1:nLvuzNvy1ZDNQW30IuMc2ZWCbiqrJgdLoUS2X8HAUVg= @@ -227,6 +231,7 @@ github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLt github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= @@ -240,6 +245,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -247,6 +254,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -292,10 +301,13 @@ github.com/safing/portbase v0.11.2/go.mod h1:Xb0htNFFEb6BlkloWyVbQoUguUMGrTVmEDw github.com/safing/portbase v0.12.0 h1:S5gHcr+10qauddByR8ZtlxmI5Bu7MqgOX9xDfFhoKls= github.com/safing/portbase v0.12.0/go.mod h1:Xb0htNFFEb6BlkloWyVbQoUguUMGrTVmEDwwpAAMS5g= github.com/safing/portmaster v0.6.13/go.mod h1:7Ux+qkINlWF3a1YKFWudA6uUrA+i4ik16JwYMmqTzGs= +github.com/safing/portmaster v0.7.0/go.mod h1:5FLo/07RhqJp0HxTcUuy2iX3TYJAil62ZY+uogI0Ul4= github.com/safing/spn v0.2.4 h1:V2XPMTQkHo7xwH89Kfx+65cFWgXAz/jCeCXcweEJuLQ= github.com/safing/spn v0.2.4/go.mod h1:ZA6za4rEP46GiVKJGZsQyqoqMvPhtF913mg5JtJl3Sc= github.com/safing/spn v0.2.5 h1:npp+8c6fWvn+ykCp9+K8FFZuofxGPXT7yDu86nEo61M= github.com/safing/spn v0.2.5/go.mod h1:/WNZAVoJB8C1Ut771dYyxyYCdPge2NXoU++dp4cAMDg= +github.com/safing/spn v0.3.0 h1:qcnFLvtCfNT3VsTt/3xgKMFWyUYxMy8HbC7/hKGXHUw= +github.com/safing/spn v0.3.0/go.mod h1:bZsO2kHMvymxLxIh6Ij99W/S0wT4HFcNQy4kEa5O52Q= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= @@ -362,6 +374,7 @@ github.com/tidwall/sjson v1.1.5 h1:wsUceI/XDyZk3J1FUvuuYlK62zJv2HO2Pzb8A5EWdUE= github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE= github.com/tidwall/sjson v1.1.6 h1:8fDdlahON04OZBlTQCIatW8FstSFJz8oxidj5h0rmSQ= github.com/tidwall/sjson v1.1.6/go.mod h1:KN3FZ7odvXIHPbJdhNorK/M9lWweVUbXsXXhrJ/kGOA= +github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.0 h1:8nbaiZG+iVdh+fXVw0DZoZZa7a4TGm3Qab+xdrdzj8s= github.com/tjfoc/gmsm v1.4.0/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= diff --git a/intel/geoip/location.go b/intel/geoip/location.go index ebe9928e..6d94a563 100644 --- a/intel/geoip/location.go +++ b/intel/geoip/location.go @@ -84,9 +84,8 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) { case l.Coordinates.Latitude == 0 && l.Coordinates.Longitude == 0: fallthrough case to.Coordinates.Latitude == 0 && to.Coordinates.Longitude == 0: - // If we don't have any on any side coordinates, set accuracy to worst - // effective value. - accuracy = 1000 + // If we don't have any coordinates, return. + return proximity case to.Coordinates.AccuracyRadius > accuracy: // If the destination accuracy is worse, use that one. accuracy = to.Coordinates.AccuracyRadius @@ -98,11 +97,11 @@ func (l *Location) EstimateNetworkProximity(to *Location) (proximity int) { distanceInPercent := (earthCircumferenceInKm - km) * 100 / earthCircumferenceInKm // apply penalty for locations with low accuracy (targeting accuracy radius >100) - accuracyModifier := 1 - float64(accuracy)/1000 + accuracyModifier := 1 - float64(accuracy)/2000 proximity += int(distanceInPercent * 0.10 * accuracyModifier) } - return //nolint:nakedret + return proximity } // PrimitiveNetworkProximity calculates the numerical distance between two IP addresses. Returns a proximity value between 0 (far away) and 100 (nearby). diff --git a/netenv/api.go b/netenv/api.go index 776eda73..237dee57 100644 --- a/netenv/api.go +++ b/netenv/api.go @@ -50,5 +50,18 @@ func registerAPIEndpoints() error { return err } + if err := api.RegisterEndpoint(api.Endpoint{ + Path: "network/location/traceroute", + Read: api.PermitUser, + BelongsTo: module, + StructFunc: func(ar *api.Request) (i interface{}, err error) { + return getLocationFromTraceroute() + }, + Name: "Get Approximate Internet Location via Traceroute", + Description: "Returns an approximation of where the device is on the Internet using a the traceroute technique.", + }); err != nil { + return err + } + return nil } diff --git a/netenv/icmp_listener.go b/netenv/icmp_listener.go index 229802b7..7248469c 100644 --- a/netenv/icmp_listener.go +++ b/netenv/icmp_listener.go @@ -1,6 +1,7 @@ package netenv import ( + "net" "sync" "github.com/tevino/abool" @@ -31,21 +32,23 @@ var ( listenICMPEnabled = abool.New() // listenICMPInput is created for every use of the ICMP listenting system. - listenICMPInput chan packet.Packet - listenICMPInputLock sync.Mutex + listenICMPInput chan packet.Packet + listenICMPInputTargetIP net.IP + listenICMPInputLock sync.Mutex ) // ListenToICMP returns a new channel for listenting to icmp packets. Please // note that any icmp packet will be passed and filtering must be done on // the side of the caller. The caller must call the returned done function when // done with the listener. -func ListenToICMP() (packets chan packet.Packet, done func()) { +func ListenToICMP(targetIP net.IP) (packets chan packet.Packet, done func()) { // Lock for single use. listenICMPLock.Lock() // Create new input channel. listenICMPInputLock.Lock() listenICMPInput = make(chan packet.Packet, 100) + listenICMPInputTargetIP = targetIP listenICMPEnabled.Set() listenICMPInputLock.Unlock() @@ -56,6 +59,7 @@ func ListenToICMP() (packets chan packet.Packet, done func()) { // Close input channel. listenICMPInputLock.Lock() listenICMPEnabled.UnSet() + listenICMPInputTargetIP = nil close(listenICMPInput) listenICMPInputLock.Unlock() } @@ -71,15 +75,14 @@ func SubmitPacketToICMPListener(pkt packet.Packet) (submitted bool) { } // Slow path. - submitPacketToICMPListenerSlow(pkt) - return true + return submitPacketToICMPListenerSlow(pkt) } -func submitPacketToICMPListenerSlow(pkt packet.Packet) { +func submitPacketToICMPListenerSlow(pkt packet.Packet) (submitted bool) { // Make sure the payload is available. if err := pkt.LoadPacketData(); err != nil { log.Warningf("netenv: failed to get payload for ICMP listener: %s", err) - return + return false } // Send to input channel. @@ -88,7 +91,14 @@ func submitPacketToICMPListenerSlow(pkt packet.Packet) { // Check if still enabled. if !listenICMPEnabled.IsSet() { - return + return false + } + + // Only listen for outbound packets to the target IP. + if pkt.IsOutbound() && + listenICMPInputTargetIP != nil && + !pkt.Info().Dst.Equal(listenICMPInputTargetIP) { + return false } // Send to channel, if possible. @@ -97,4 +107,5 @@ func submitPacketToICMPListenerSlow(pkt packet.Packet) { default: log.Warning("netenv: failed to send packet payload to ICMP listener: channel full") } + return true } diff --git a/netenv/location.go b/netenv/location.go index f2e895b4..e45a14fd 100644 --- a/netenv/location.go +++ b/netenv/location.go @@ -21,6 +21,12 @@ import ( ) var ( + // locationTestingIPv4 holds the IP address of the server that should be + // tracerouted to find the location of the device. The ping will never reach + // the destination in most cases. + // The selection of this IP requires sensitivity, as the IP address must be + // far enough away to produce good results. + // At the same time, the IP address should be common and not raise attention. locationTestingIPv4 = "1.1.1.1" locationTestingIPv4Addr *net.IPAddr @@ -136,20 +142,23 @@ const ( SourcePeer DeviceLocationSource = "peer" SourceUPNP DeviceLocationSource = "upnp" SourceTraceroute DeviceLocationSource = "traceroute" + SourceTimezone DeviceLocationSource = "timezone" SourceOther DeviceLocationSource = "other" ) func (dls DeviceLocationSource) Accuracy() int { switch dls { case SourceInterface: - return 5 + return 6 case SourcePeer: - return 4 + return 5 case SourceUPNP: - return 3 + return 4 case SourceTraceroute: - return 2 + return 3 case SourceOther: + return 2 + case SourceTimezone: return 1 default: return 0 @@ -162,10 +171,10 @@ func (a sortLocationsByAccuracy) Len() int { return len(a) } func (a sortLocationsByAccuracy) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a sortLocationsByAccuracy) Less(i, j int) bool { return a[j].IsMoreAccurateThan(a[i]) } -func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) { +func SetInternetLocation(ip net.IP, source DeviceLocationSource) (dl *DeviceLocation, ok bool) { // Check if IP is global. if netutils.GetIPScope(ip) != netutils.Global { - return false + return nil, false } // Create new location. @@ -188,29 +197,32 @@ func SetInternetLocation(ip net.IP, source DeviceLocationSource) (ok bool) { loc.Location = geoLoc } + addLocation(loc) + return loc, true +} + +func addLocation(dl *DeviceLocation) { locationsLock.Lock() defer locationsLock.Unlock() // Add to locations, if better. var exists bool for i, existing := range locations.All { - if ip.Equal(existing.IP) { + if (dl.IP == nil && existing.IP == nil) || dl.IP.Equal(existing.IP) { exists = true - if loc.IsMoreAccurateThan(existing) { + if dl.IsMoreAccurateThan(existing) { // Replace - locations.All[i] = loc + locations.All[i] = dl break } } } if !exists { - locations.All = append(locations.All, loc) + locations.All = append(locations.All, dl) } // Sort locations. sort.Sort(sortLocationsByAccuracy(locations.All)) - - return true } // DEPRECATED: Please use GetInternetLocation instead. @@ -243,8 +255,18 @@ func GetInternetLocation() (deviceLocations *DeviceLocations, ok bool) { v4ok, v6ok := getLocationFromInterfaces() // Try other methods for missing locations. - if len(v4s) > 0 && !v4ok { - v4ok = getLocationFromTraceroute() + if len(v4s) > 0 { + if !v4ok { + _, err = getLocationFromTraceroute() + if err != nil { + log.Warningf("netenv: failed to get IPv4 from traceroute: %s", err) + } else { + v4ok = true + } + } + if !v4ok { + v4ok = getLocationFromTimezone(packet.IPv4) + } } if len(v6s) > 0 && !v6ok { // TODO @@ -269,12 +291,12 @@ func getLocationFromInterfaces() (v4ok, v6ok bool) { } for _, ip := range globalIPv4 { - if SetInternetLocation(ip, SourceInterface) { + if _, ok := SetInternetLocation(ip, SourceInterface); ok { v4ok = true } } for _, ip := range globalIPv6 { - if SetInternetLocation(ip, SourceInterface) { + if _, ok := SetInternetLocation(ip, SourceInterface); ok { v6ok = true } } @@ -292,20 +314,18 @@ func getLocationFromUPnP() (ok bool) { } */ -func getLocationFromTraceroute() (v4ok bool) { +func getLocationFromTraceroute() (dl *DeviceLocation, err error) { // Create connection. conn, err := net.ListenPacket("ip4:icmp", "") if err != nil { - log.Warningf("netenv: location: failed to open icmp conn: %s", err) - return false + return nil, fmt.Errorf("failed to open icmp conn: %s", err) } v4Conn := ipv4.NewPacketConn(conn) // Generate a random ID for the ICMP packets. generatedID, err := rng.Number(0xFFFF) // uint16 if err != nil { - log.Warningf("netenv: location: failed to generate icmp msg ID: %s", err) - return false + return nil, fmt.Errorf("failed to generate icmp msg ID: %s", err) } msgID := int(generatedID) var msgSeq int @@ -323,7 +343,7 @@ func getLocationFromTraceroute() (v4ok bool) { maxHops := 4 // add one for every reply that is not global // Get additional listener for ICMP messages via the firewall. - icmpPacketsViaFirewall, doneWithListeningToICMP := ListenToICMP() + icmpPacketsViaFirewall, doneWithListeningToICMP := ListenToICMP(locationTestingIPv4Addr.IP) defer doneWithListeningToICMP() nextHop: @@ -339,15 +359,13 @@ nextHop: // Make packet data. pingPacket, err := pingMessage.Marshal(nil) if err != nil { - log.Warningf("netenv: location: failed to build icmp packet: %s", err) - return false + return nil, fmt.Errorf("failed to build icmp packet: %s", err) } // Set TTL on IP packet. err = v4Conn.SetTTL(i) if err != nil { - log.Warningf("netenv: location: failed to set icmp packet TTL: %s", err) - return false + return nil, fmt.Errorf("failed to set icmp packet TTL: %s", err) } // Send ICMP packet. @@ -357,8 +375,7 @@ nextHop: continue } } - log.Warningf("netenv: location: failed to send icmp packet: %s", err) - return false + return nil, fmt.Errorf("failed to send icmp packet: %s", err) } // Listen for replies of the ICMP packet. @@ -381,7 +398,7 @@ nextHop: } // We received a reply, so we did not trigger a time exceeded response on the way. // This means we were not able to find the nearest router to us. - return false + return nil, errors.New("received final echo reply without time exceeded messages") case layers.ICMPv4TypeDestinationUnreachable, layers.ICMPv4TypeTimeExceeded: // Continue processing. @@ -413,13 +430,17 @@ nextHop: switch icmpPacket.TypeCode.Type() { case layers.ICMPv4TypeDestinationUnreachable: // We have received a valid destination unreachable response, abort. - return false + return nil, errors.New("destination unreachable") case layers.ICMPv4TypeTimeExceeded: // We have received a valid time exceeded error. // If message came from a global unicast, us it! if netutils.GetIPScope(remoteIP) == netutils.Global { - return SetInternetLocation(remoteIP, SourceTraceroute) + dl, ok := SetInternetLocation(remoteIP, SourceTraceroute) + if !ok { + return nil, errors.New("invalid IP address") + } + return dl, nil } // Otherwise, continue. @@ -430,7 +451,7 @@ nextHop: } // We did not receive anything actionable. - return false + return nil, errors.New("did not receive any actionable ICMP reply") } func recvICMP(currentHop int, icmpPacketsViaFirewall chan packet.Packet) ( @@ -455,8 +476,27 @@ func recvICMP(currentHop int, icmpPacketsViaFirewall chan packet.Packet) ( } return pkt.Info().RemoteIP(), icmp4, true - case <-time.After(time.Duration(currentHop*10+50) * time.Millisecond): + case <-time.After(time.Duration(currentHop*20+100) * time.Millisecond): return nil, nil, false } } } + +func getLocationFromTimezone(ipVersion packet.IPVersion) (ok bool) { + // Create base struct. + tzLoc := &DeviceLocation{ + IPVersion: ipVersion, + Location: &geoip.Location{}, + Source: SourceTimezone, + SourceAccuracy: SourceTimezone.Accuracy(), + } + + // Calculate longitude based on current timezone. + _, offsetSeconds := time.Now().Zone() + tzLoc.Location.Coordinates.AccuracyRadius = 1000 + tzLoc.Location.Coordinates.Latitude = 48 + tzLoc.Location.Coordinates.Longitude = float64(offsetSeconds) / 43200 * 180 + + addLocation(tzLoc) + return true +}