Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WLAN Integration #32530

Open
wants to merge 48 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5cf9df3
rssi collection working
NouemanKHAL Oct 12, 2024
cee3dc7
attempt to request location permission
NouemanKHAL Oct 15, 2024
ab922dd
wip
NouemanKHAL Dec 2, 2024
5489368
cleanup tests, add mocks for unit tests
NouemanKHAL Dec 2, 2024
793c18e
split definitions into header file, and move WifiInfo struct out of d…
NouemanKHAL Dec 3, 2024
a1c7883
fix header file MACRO name
NouemanKHAL Dec 5, 2024
ad2ecb0
send wlan.noise metric
NouemanKHAL Dec 5, 2024
53e0b27
send wlan.transmit_rate metric
NouemanKHAL Dec 5, 2024
5464143
add security_type tag
NouemanKHAL Dec 5, 2024
b3f5bcd
ssid/bssid should be marked unknown when empty
NouemanKHAL Dec 5, 2024
14eb513
send wlan.channel_swap_events
NouemanKHAL Dec 5, 2024
d51b070
send zero values for wlan.channel_swap_events
NouemanKHAL Dec 5, 2024
2c1255f
cleanup tests to assert metrics and stop mocking methods
NouemanKHAL Dec 5, 2024
0c80439
add mac_address tag
NouemanKHAL Dec 11, 2024
2349626
remove security_type tag
NouemanKHAL Dec 19, 2024
716fa50
Merge remote-tracking branch 'origin/main' into noueman/poc
NouemanKHAL Dec 27, 2024
c3d520c
register wlan as a corecheck
NouemanKHAL Dec 27, 2024
cb4dd20
Merge branch 'main' into noueman/poc
NouemanKHAL Dec 27, 2024
e710c95
change location permission to only when in use
NouemanKHAL Dec 27, 2024
fad7499
update info.pList file for the macos app to use location services
NouemanKHAL Dec 27, 2024
d9a29d5
Merge branch 'main' into noueman/poc
NouemanKHAL Dec 28, 2024
8c437d2
use availability directive to support earlier macos versions than 10.15
NouemanKHAL Dec 28, 2024
4b34493
fix location access request
NouemanKHAL Dec 28, 2024
7d5a8ed
add more propreties for location usage inth the info.plist
NouemanKHAL Dec 30, 2024
c368719
stop collecting security type
NouemanKHAL Dec 30, 2024
3f3887c
use requestAlwaysAuthorization
NouemanKHAL Dec 30, 2024
c07f239
fix info.plist to only add NSLocationUsageDescription key
NouemanKHAL Dec 30, 2024
b1e9719
fix location service request logic
NouemanKHAL Dec 30, 2024
b10fd02
switch to requestWhenInUseAuthorization + remove unused code
NouemanKHAL Dec 31, 2024
330ddfd
add available directive to ensure macOs 10.15+, fix typo in requestWh…
NouemanKHAL Dec 31, 2024
b04671f
Merge branch 'main' into noueman/poc
NouemanKHAL Dec 31, 2024
6501a7b
bump macos version check to 11.0 for the authorizationStatus support
NouemanKHAL Dec 31, 2024
d316b8f
cleanup tests
NouemanKHAL Dec 31, 2024
0d71bbe
collect roaming events
NouemanKHAL Dec 31, 2024
49273ed
cleanup
NouemanKHAL Dec 31, 2024
90132cd
refactor code: move setupLocationAccess to darwin implementation only…
NouemanKHAL Dec 31, 2024
a906f6a
reno
NouemanKHAL Dec 31, 2024
b6546af
delete unused code
NouemanKHAL Dec 31, 2024
cf71066
remove build tag from main check file
NouemanKHAL Dec 31, 2024
008a33d
change windows file to nodarwin
NouemanKHAL Dec 31, 2024
8713675
refactor: remove globals in favor of instance attributes
NouemanKHAL Jan 3, 2025
9466007
add test to ensure channel 0 is considered a valid channel number
NouemanKHAL Jan 3, 2025
496af02
Add test for when Wifi Interface is inactive
NouemanKHAL Jan 3, 2025
ca5d1e8
do not report any metrics when the wifi interface is inactive
NouemanKHAL Jan 3, 2025
427309c
Merge branch 'main' into noueman/poc
NouemanKHAL Jan 3, 2025
2bcecb1
go mod tidy
NouemanKHAL Jan 3, 2025
c6b6b31
rename boolean warmedUp to isWarmedUp
NouemanKHAL Jan 3, 2025
16a8f3f
fix typo in Factory method using optional instead of option
NouemanKHAL Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
<string>Copyright (c) 2009-<%= year %>, Datadog Inc.</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSLocationUsageDescription</key>
<string>Datadog needs access to location services to retrieve Wi-Fi network information.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Datadog needs access to local network services to function"</string>
</dict>
Expand Down
121 changes: 121 additions & 0 deletions pkg/collector/corechecks/net/wlan/wlan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//nolint:revive // TODO(PLINT) Fix revive linter
package wlan

import (
"strings"
"time"

"github.com/DataDog/datadog-agent/pkg/collector/check"
core "github.com/DataDog/datadog-agent/pkg/collector/corechecks"
"github.com/DataDog/datadog-agent/pkg/util/log"
"github.com/DataDog/datadog-agent/pkg/util/option"
)

const (
CheckName = "wlan"
defaultMinCollectionInterval = 15
)

var getWiFiInfo = GetWiFiInfo

// WiFiInfo contains information about the WiFi connection as defined in wlan_darwin.h
type WiFiInfo struct {
Rssi int
Ssid string
Bssid string
Channel int
Noise int
TransmitRate float64
HardwareAddress string
// See: https://developer.apple.com/documentation/corewlan/cwphymode?language=objc
ActivePHYMode int
}

// WLANCheck monitors the status of the WLAN interface
type WLANCheck struct {
core.CheckBase
lastChannelID int
lastBSSID string
lastSSID string
isWarmedUp bool
}

func (c *WLANCheck) String() string {
return "wlan"
}

// Run runs the check
func (c *WLANCheck) Run() error {
sender, err := c.GetSender()
if err != nil {
return err
}
wifiInfo, err := getWiFiInfo()
if err != nil {
log.Error(err)
return err
}

if wifiInfo.ActivePHYMode == 0 {
log.Warn("No active Wi-Fi interface detected: ActivePHYMode is none.")
return nil
}

ssid := wifiInfo.Ssid
if ssid == "" {
ssid = "unknown"
}
bssid := wifiInfo.Bssid
if bssid == "" {
bssid = "unknown"
}
hardwareAddress := strings.ToLower(strings.Replace(wifiInfo.HardwareAddress, " ", "_", -1))

tags := []string{}
tags = append(tags, "ssid:"+ssid)
tags = append(tags, "bssid:"+bssid)
tags = append(tags, "mac_address:"+hardwareAddress)

sender.Gauge("wlan.rssi", float64(wifiInfo.Rssi), "", tags)
sender.Gauge("wlan.noise", float64(wifiInfo.Noise), "", tags)
sender.Gauge("wlan.transmit_rate", float64(wifiInfo.TransmitRate), "", tags)

// channel swap events
if c.isWarmedUp && c.lastChannelID != wifiInfo.Channel {
sender.Count("wlan.channel_swap_events", 1.0, "", tags)
} else {
sender.Count("wlan.channel_swap_events", 0.0, "", tags)
}

// roaming events / ssid swap events
if c.isWarmedUp && c.lastBSSID != "" && c.lastSSID != "" && c.lastBSSID == wifiInfo.Bssid && c.lastSSID != wifiInfo.Ssid {
sender.Count("wlan.roaming_events", 1.0, "", tags)
} else {
sender.Count("wlan.roaming_events", 0.0, "", tags)
}

// update last values
c.lastChannelID = wifiInfo.Channel
c.lastBSSID = wifiInfo.Bssid
c.lastSSID = wifiInfo.Ssid
c.isWarmedUp = true

sender.Commit()
return nil
}

// Factory creates a new check factory
func Factory() option.Option[func() check.Check] {
return option.New(newCheck)
}

func newCheck() check.Check {
return &WLANCheck{
CheckBase: core.NewCheckBaseWithInterval(CheckName, time.Duration(defaultMinCollectionInterval)*time.Second),
}
}
36 changes: 36 additions & 0 deletions pkg/collector/corechecks/net/wlan/wlan_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build darwin

//nolint:revive // TODO(PLINT) Fix revive linter
package wlan

/*
#cgo CFLAGS: -I .
#cgo LDFLAGS: -framework CoreWLAN -framework CoreLocation -framework Foundation
#include "wlan_darwin.h"
*/
import "C"
import (
"github.com/DataDog/datadog-agent/pkg/util/log"
)

func GetWiFiInfo() (WiFiInfo, error) {
C.InitLocationServices()
log.Info("Initialized Location Manager")

info := C.GetWiFiInformation()
return WiFiInfo{
Rssi: int(info.rssi),
Ssid: C.GoString(info.ssid),
Bssid: C.GoString(info.bssid),
Channel: int(info.channel),
Noise: int(info.noise),
TransmitRate: float64(info.transmitRate),
HardwareAddress: C.GoString(info.hardwareAddress),
ActivePHYMode: int(info.activePHYMode),
}, nil
}
18 changes: 18 additions & 0 deletions pkg/collector/corechecks/net/wlan/wlan_darwin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef WLAN_DARWIN_H
#define WLAN_DARWIN_H

typedef struct {
int rssi;
const char *ssid;
const char *bssid;
int channel;
int noise;
double transmitRate;
const char *hardwareAddress;
int activePHYMode;
} WiFiInfo;

WiFiInfo GetWiFiInformation();
void InitLocationServices();

#endif
99 changes: 99 additions & 0 deletions pkg/collector/corechecks/net/wlan/wlan_darwin.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

#include <Foundation/Foundation.h>
#include <CoreWLAN/CoreWLAN.h>
#include <CoreLocation/CoreLocation.h>
#include "wlan_darwin.h"


@interface LocationManager : NSObject <CLLocationManagerDelegate>
@property (nonatomic, strong) CLLocationManager *locationManager;
@end

@implementation LocationManager

- (instancetype)init {
self = [super init];
if (self) {
[self setupLocationServices];
}
return self;
}

- (void)setupLocationServices {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;

if (@available(macOS 11.0, *)) {
if (self.locationManager.authorizationStatus == kCLAuthorizationStatusNotDetermined) {
[self.locationManager requestWhenInUseAuthorization];
}
} else {
NSLog(@"Location services not available on this OS version");
}
}

#pragma mark - CLLocationManagerDelegate

- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager {
if (@available(macOS 11.0, *)) {
CLAuthorizationStatus status = manager.authorizationStatus;

switch (status) {
case kCLAuthorizationStatusAuthorizedAlways:
NSLog(@"Location services authorized!");
[self.locationManager startUpdatingLocation];
break;

case kCLAuthorizationStatusDenied:
NSLog(@"Location services denied by user");
break;

case kCLAuthorizationStatusRestricted:
NSLog(@"Location services restricted");
break;
default:
NSLog(@"Location services status undetermined: %d", status);
break;
}
} else {
NSLog(@"Location services not available on this OS version");
}
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *location = [locations lastObject];
NSLog(@"Location updated: %@", location);
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(@"Location update failed with error: %@", error.localizedDescription);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these messages end up? Would it make sense to use agent logging facilities instead?

}

@end

// Wrapper function to start location updates
void InitLocationServices() {
LocationManager *locationManager = [[LocationManager alloc] init];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gets called every check run, is this allocating a new object every time? Will those be released automatically?

}

// GetWiFiInformation return wifi data
WiFiInfo GetWiFiInformation() {
CWInterface *wifiInterface = [[CWWiFiClient sharedWiFiClient] interface];

WiFiInfo info;

info.rssi = (int)wifiInterface.rssiValue;
info.ssid = [[wifiInterface ssid] UTF8String];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very familiar with objective c, is UTF8String value is going to be valid long enough to read with GoString? When the NSString returned by ssid is freed?

info.bssid = [[wifiInterface bssid] UTF8String];
info.channel = (int)wifiInterface.wlanChannel.channelNumber;
info.noise = (int)wifiInterface.noiseMeasurement;
info.transmitRate = wifiInterface.transmitRate;
info.hardwareAddress = [[wifiInterface hardwareAddress] UTF8String];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hardwareAddress can return nil, would it make sense to check for it?

info.activePHYMode = (int)wifiInterface.activePHYMode;

return info;
}
15 changes: 15 additions & 0 deletions pkg/collector/corechecks/net/wlan/wlan_nodarwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build !darwin

//nolint:revive // TODO(PLINT) Fix revive linter
package wlan

import "fmt"

func GetWiFiInfo() (WiFiInfo, error) {
return WiFiInfo{}, fmt.Errorf("wifi info only supported on macOS")
}
Loading
Loading