A Go SDK for interacting with Cisco Catalyst 9800 Wireless Network Controller.
Cisco Catalyst 9800 Wireless Network Controller running on:
This SDK requires Go 1.27 or newer.
go get github.com/umatare5/cisco-ios-xe-wireless-go
You have to enable RESTCONF and HTTPS on the C9800 before using this SDK. Please see:
Encode your controller credentials as Base64.
# username:password → Base64 echo -n "admin:your-password" | base64 # Output: YWRtaW46eW91ci1wYXNzd29yZA==
Use your controller host and token to fetch AP operational data.
package main import ( "context" "fmt" "os" "time" wnc "github.com/umatare5/cisco-ios-xe-wireless-go" ) func main() { // Load environment variables controller := os.Getenv("WNC_CONTROLLER") token := os.Getenv("WNC_ACCESS_TOKEN") // Create client client, err := wnc.NewClient(controller, token, wnc.WithTimeout(30*time.Second), wnc.WithInsecureSkipVerify(true), // remove for production ) if err != nil { fmt.Fprintf(os.Stderr, "Failed to create client: %v\n", err) os.Exit(1) } // Create simple context with timeout ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() // Request AP operational data apData, err := client.AP().GetOperational(ctx) if err != nil { fmt.Fprintf(os.Stderr, "AP oper request failed: %v\n", err) os.Exit(1) } // Print AP operational data fmt.Printf("Successfully connected! Found %d APs\n", len(apData.CiscoIOSXEWirelessAPOperData.CAPWAPData)) }
Caution
The wnc.WithInsecureSkipVerify(true) option disables TLS certificate verification. This should only be used in development environments or when connecting to controllers with self-signed certificates. Never use this option in production environments as it compromises security. Where the controller presents a certificate from a private CA, pass wnc.WithRootCAs(pool) instead: the certificate is then verified rather than unverified.
# Set environment variables export WNC_CONTROLLER="wnc1.example.internal" export WNC_ACCESS_TOKEN="YWRtaW46eW91ci1wYXNzd29yZA==" # Run the application go run main.go # result: Successfully connected! Found 2 APs
This SDK provides a client to interact with the Cisco Catalyst 9800 Wireless Network Controller's RESTCONF.
To create a new client, use the wnc.NewClient function with the controller address and access token.
| Parameter | Type | Description |
|---|---|---|
controller |
string |
The hostname or IP address of the WNC. |
accessToken |
string |
The Base64-encoded Basic Auth token. |
options... |
...Option |
Optional client configuration options. |
There are several options to customize the client behavior. Each argument type is in the package documentation.
| Option | Default | Description |
|---|---|---|
WithTimeout(d) |
60s |
Whole-request timeout |
WithResponseHeaderTimeout(d) |
5s |
Header wait timeout |
WithTLSHandshakeTimeout(d) |
5s |
TLS handshake wait |
WithRootCAs(pool) |
host roots | Trust a private CA |
WithClientCertificate(cert) |
none | Present a client cert |
WithInsecureSkipVerify(skip) |
false |
Skip TLS verify |
WithProxy(fn) |
nil |
Proxy resolver |
WithLogger(l) |
slog.Default() |
Structured logger |
WithUserAgent(ua) |
cisco-ios-xe-wireless-go/<version> |
Custom User-Agent |
Every read method takes optional GetOption values after ctx, which apply to that single request.
| Option | Value on the wire | Description |
|---|---|---|
WithDefaults(wnc.ReportAll) |
with-defaults=report-all |
Adds the leaves in force at their default. |
WithDefaults(wnc.Explicit) |
with-defaults=explicit |
Adds the leaves a client set to the default. |
WithFields(expr) |
fields=<expr> |
Returns only the nodes named. |
WithDepth(n) |
depth=<n> |
Returns the top n levels only. |
entries, err := client.WLAN().ListWlanCfgEntries(ctx, wnc.WithDefaults(wnc.ReportAll))
Note
RFC 6243 3.3 is why wnc.Explicit differs from a plain GET, which omits any leaf equal to its default. Scope wnc.ReportAll to the container you need, because on a whole-container read the added leaves accumulate across every nested one. A pruned leaf decodes to zero, so WithFields and WithDepth must name every node the caller reads.
Every node this SDK types has an accessor.
For one it does not — a container a later IOS-XE release adds, or an RPC with no typed wrapper — the root client carries untyped methods that share the client's credentials, TLS settings, timeouts and *APIError typing.
| Method | RESTCONF resource | Notes |
|---|---|---|
GetData(ctx, path, opts...) |
/restconf/data |
Read with same GetOption |
GetDataInto[T](ctx, client, path, opts...) |
/restconf/data |
Read into a typed envelope |
PostData / PutData / PatchData / DeleteData |
/restconf/data |
Edit via fixed call verb |
PostRPC(ctx, path, payload) |
/restconf/operations |
Invoke RPC |
Request(ctx, method, path, payload) |
either | Fallback; carries the status |
GetDataInto is the one entry above that validates the envelope, so it takes a T whose outermost tag is the module-qualified node the path reads. It is a function rather than a method because a generic method may not be declared in an interface and is invisible to reflect.
body, err := client.PatchData(ctx, "Cisco-IOS-XE-wireless-wlan-cfg:wlan-cfg-data/wlan-cfg-entries/wlan-cfg-entry=1,demo", payload)
Warning
A []byte or json.RawMessage payload is sent as written once checked for well-formed JSON, and anything else is marshaled. Edit a body read with GetData as bytes, because decoding it into a Go value first rounds a 64-bit number.
Please refer to the Go Reference for the complete reference.
The following table summarizes the supported service APIs and their capabilities.
Legend:
| API | GetOperational() |
GetConfig() |
Other Functions | Notes |
|---|---|---|---|---|
AFC() |
✅️ | ⬜️ | ⬜️ | |
AP() |
✅️ | ✅️ | 🟩 | Issue #47 on 17.15+ |
APF() |
⬜️ | ✅️ | ⬜️ | |
AWIPS() |
✅️ | ⬜️ | ⬜️ | Issue #48 on 17.15+ |
BLE() |
✅️ | ⬜️ | ⬜️ | |
Client() |
✅️ | ⬜️ | ⬜️ | |
Controller() |
⬜️ | ⬜️ | 🟩 | |
CTS() |
⬜️ | ✅️ | ⬜️ | |
Dot11() |
⬜️ | ✅️ | ⬜️ | |
Dot15() |
⬜️ | ✅️ | ⬜️ | |
Fabric() |
⬜️ | ✅️ | ⬜️ | |
Flex() |
⬜️ | ✅️ | ⬜️ | |
General() |
✅️ | ✅️ | ⬜️ | |
Geolocation() |
✅️ | ⬜️ | ⬜️ | |
Hyperlocation() |
✅️ | ⬜️ | ⬜️ | |
LISP() |
✅️ | ⬜️ | ⬜️ | |
Location() |
✅️ | ✅️ | ⬜️ | |
Mcast() |
✅️ | ⬜️ | ⬜️ | |
MDNS() |
✅️ | ⬜️ | ⬜️ | |
Mesh() |
✅️ | ✅️ | ⬜️ | |
Mobility() |
✅️ | ⬜️ | ⬜️ | |
NMSP() |
✅️ | ⬜️ | ⬜️ | |
Radio() |
⬜️ | ✅️ | ⬜️ | |
RF() |
⬜️ | ✅️ | ⬜️ | |
RFTag() |
⬜️ | ⬜️ | 🟩 | |
RFID() |
✅️ | ✅️ | ⬜️ | |
Rogue() |
✅️ | ⬜️ | ⬜️ | |
RRM() |
✅️ | ✅️ | ⬜️ | |
Site() |
✅️ | ✅️ | ⬜️ | |
SiteTag() |
⬜️ | ⬜️ | 🟩 | |
Spaces() |
🟨 | ⬜️ | ⬜️ | Requires 17.15+ |
URWB() |
🟨 | 🟨 | ⬜️ | Requires 17.18+ |
WAT() |
⬜️ | 🟨 | ⬜️ | Requires 17.18+ |
WLAN() |
✅️ | ✅️ | ⬜️ | |
PolicyTag() |
⬜️ | ⬜️ | 🟩 |
Tip
wtpMac is the same as radioMac. WTP (Wireless Termination Point), defined in RFC 5415 denotes an AP.
Runnable examples are available:
example/list_aps/main.go lists APs managed by the controller.
❯ go run example/list_aps/main.go Successfully connected! Found 2 APs AP Name | MAC Address | IP Address | Status ------------------|---------------------|------------------|----------------- TEST-AP01 | aa:bb:cc:dd:ee:01 | 192.168.1.11 | registered TEST-AP02 | aa:bb:cc:dd:ee:02 | 192.168.1.12 | registered
example/list_clients/main.go lists clients associating to wireless networks.
❯ go run example/list_clients/main.go Successfully connected! Found 17 clients MAC Address | IP Address ----------------------|---------------- aa:bb:cc:dd:ee:a1 | 192.168.1.101 aa:bb:cc:dd:ee:a2 | 192.168.1.102 aa:bb:cc:dd:ee:a3 | 192.168.1.103 aa:bb:cc:dd:ee:a4 | 192.168.1.104 <snip>
example/list_wlans/main.go lists WLANs and their BSSIDs.
❯ go run example/list_wlans/main.go Successfully connected! Found 7 WLANs across all APs AP Name | AP MAC Address | Slot | WLAN | BSSID | SSID ------------------|-------------------|------|------|-------------------|------------------------- TEST-AP01 | aa:bb:cc:dd:ee:01 | 0 | 1 | aa:bb:cc:dd:ee:b1 | test-wlan TEST-AP01 | aa:bb:cc:dd:ee:01 | 1 | 2 | aa:bb:cc:dd:ee:b2 | test-psk TEST-AP01 | aa:bb:cc:dd:ee:01 | 1 | 4 | aa:bb:cc:dd:ee:b3 | test-tls <snip>
example/list_neighbors/main.go lists neighboring APs detected by the APs.
❯ go run example/list_neighbors/main.go Successfully connected! Found 11 AP neighbors AP Name | Slot | Neighbor BSSID | Neighbor SSID | RSSI | Channel | Last Heard At ------------------|------|-------------------|------------------------|-------|---------|-------------------------- TEST-AP01 | 0 | aa:bb:cc:dd:ee:f1 | test-rogue-01 | -20 | 11 | 2024-01-15 10:40:00 TEST-AP01 | 0 | aa:bb:cc:dd:ee:f2 | test-rogue-02 | -62 | 4 | 2024-01-15 10:41:00 TEST-AP01 | 1 | aa:bb:cc:dd:ee:f3 | test-rogue-03 | -64 | 36 | 2024-01-15 10:42:00 <snip>
example/reset_ap/main.go resets a specified AP by its MAC address.
❯ go run example/reset_ap/main.go === Access Point Reset Tool === WARNING: This tool will restart access points causing service interruption! Use only in controlled environments with proper authorization. Target Controller: wnc1.example.internal Enter AP MAC address (format: xx:xx:xx:xx:xx:xx or xx-xx-xx-xx-xx-xx): aa:bb:cc:dd:ee:01 Target AP MAC: aa:bb:cc:dd:ee:01 This will restart the specified Access Point(s). Type 'YES' to confirm: YES ✓ WNC client created successfully Executing AP reset for MAC aa:bb:cc:dd:ee:01 WARNING: AP will become unavailable and disconnect all clients during restart... ✓ AP reset command sent successfully for MAC: aa:bb:cc:dd:ee:01 Note: AP is now restarting and will be temporarily unavailable Clients will need to reconnect after AP restart completes
example/reload_controller/main.go reloads the entire wireless controller.
❯ go run ./example/reload_controller/main.go === WNC Controller Reload Tool === WARNING: This tool will restart the wireless controller! Use only in controlled environments with proper authorization. Target Controller: wnc1.example.internal This will restart the WNC controller. Type 'YES' to confirm: YES ✓ WNC client created successfully Executing controller reload with reason: Manual reload via CLI tool at 2024-01-15T10:30:00+09:00 WARNING: Controller will become unavailable during restart... ✓ Controller reload command sent successfully Note: Controller is now restarting and will be temporarily unavailable Wait for controller to complete restart before attempting reconnection
example/save_config/main.go copies the running configuration to the startup configuration.
❯ go run example/save_config/main.go === WNC Configuration Save Tool === WARNING: This tool overwrites the startup configuration and cannot be undone! Use only in controlled environments with proper authorization. Target Controller: wnc1.example.internal This will overwrite the startup configuration. Type 'YES' to confirm: YES ✓ WNC client created successfully Executing configuration save... ✓ Save running-config successful
Please read the Contribution Guide before submitting PRs and issues and also see the following documents:
I launched this project with the help of GitHub Copilot Coding Agent, and I am grateful to the global developer community for their contributions to open source projects and public repositories.
Owner
Contributors
Categories
NetworkingProducts
Catalyst Access PointsProgramming Languages
GoLicense
Code Exchange Community
Get help, share code, and collaborate with other developers in the Code Exchange community.View Community