Filtering & Grouping
The SDK provides powerful filtering and grouping capabilities for analyzing delegation data. Both functional-style functions and a chainable filter API are available.
flowchart TB
subgraph Input["Input Data"]
Entries["DelegatedEntry[]<br/>or DelegatedExtendedEntry[]"]
end
subgraph Functional["Functional API"]
FilterEntries["FilterEntries(entries, country, type)"]
FilterStatus["FilterByStatus(entries, status)"]
FilterDate["FilterByDateRange(entries, start, end)"]
FilterOpaque["FilterExtendedByOpaqueID(entries, id)"]
end
subgraph Chainable["Chainable Filter API"]
NewFilter["NewFilter(entries)"]
ByCountry[".ByCountry(cc)"]
ByType[".ByType(type)"]
ByStatus[".ByStatus(status)"]
ByDate[".ByDateRange(start, end)"]
ByOpaque[".ByOpaqueID(id)"]
Result[".Result()"]
end
subgraph Grouping["Grouping Functions"]
GroupCountry["GroupByCountry(entries)"]
GroupOpaque["GroupExtendedByOpaqueID(entries)"]
end
subgraph Output["Output"]
Filtered["Filtered []Entry"]
Grouped["map[key][]Entry"]
CIDR["CIDR notation"]
end
Input --> Functional --> Filtered
Input --> Chainable --> Result --> Filtered
Input --> Grouping --> Grouped
Entries -->|"entry.CIDR()"| CIDR
Functional Filter Functions
Standard Delegated Entries
| Function | Description |
FilterEntries(entries, country, resType) | Filter by country and resource type |
FilterByStatus(entries, status) | Filter by status (allocated, assigned, reserved, available) |
FilterByDateRange(entries, start, end) | Filter by date range (inclusive) |
Extended Delegated Entries
| Function | Description |
FilterExtendedByOpaqueID(entries, opaqueID) | Filter by organization opaqueId |
FilterExtendedByCountry(entries, country) | Filter by country code |
FilterExtendedByType(entries, resType) | Filter by resource type |
FilterExtendedByStatus(entries, status) | Filter by status |
Grouping Functions
| Function | Description |
GroupByCountry(entries) | Group entries by country code |
GroupExtendedByOpaqueID(entries) | Group extended entries by opaqueId |
GroupExtendedByCountry(entries) | Group extended entries by country |
Chainable Filter API
flowchart LR
subgraph Chain["Chain Filter"]
Start["NewFilter(entries)"]
C1[".ByCountry(\"CN\")"]
C2[".ByType(\"ipv4\")"]
C3[".ByStatus(\"allocated\")"]
C4[".ByDateRange(start, end)"]
End[".Result()"]
end
Start --> C1 --> C2 --> C3 --> C4 --> End
EntryFilter Methods
| Method | Description |
NewFilter(entries) | Create a new filter chain |
.ByCountry(country) | Filter by ISO 3166 country code |
.ByType(resType) | Filter by resource type (ipv4, ipv6, asn) |
.ByStatus(status) | Filter by status |
.ByDateRange(start, end) | Filter by date range |
.ByRegistry(registry) | Filter by registry name |
.Result() | Get filtered entries |
.Count() | Get count of filtered entries |
ExtendedEntryFilter Methods
| Method | Description |
NewExtendedFilter(entries) | Create a new extended filter chain |
.ByCountry(country) | Filter by country code |
.ByType(resType) | Filter by resource type |
.ByStatus(status) | Filter by status |
.ByOpaqueID(opaqueID) | Filter by organization opaqueId |
.ByDateRange(start, end) | Filter by date range |
.Result() | Get filtered entries |
.Count() | Get count of filtered entries |
CIDR Calculation
| Method | Description |
entry.CIDR() | Convert DelegatedEntry to CIDR notation |
extEntry.CIDR() | Convert DelegatedExtendedEntry to CIDR notation |
legacyEntry.CIDR() | Convert LegacyEntry to CIDR notation |
Examples
Basic Filtering
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
// Fetch all delegated entries
entries, err := client.GetDelegatedEntries(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Total entries: %d\n", len(entries))
// Filter by country
cnEntries := apnic.FilterEntries(entries, "CN", "")
fmt.Printf("China entries: %d\n", len(cnEntries))
// Filter by type
ipv4Entries := apnic.FilterEntries(entries, "", "ipv4")
fmt.Printf("IPv4 entries: %d\n", len(ipv4Entries))
// Filter by country and type
cnIPv4 := apnic.FilterEntries(entries, "CN", "ipv4")
fmt.Printf("China IPv4: %d\n", len(cnIPv4))
// Filter by status
allocated := apnic.FilterByStatus(cnIPv4, "allocated")
fmt.Printf("China IPv4 allocated: %d\n", len(allocated))
}
Chainable Filter API
package main
import (
"context"
"fmt"
"log"
"time"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetDelegatedEntries(ctx)
// Chain filters together
start := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
end := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
result := apnic.NewFilter(entries).
ByCountry("CN").
ByType("ipv4").
ByStatus("allocated").
ByDateRange(start, end).
Result()
fmt.Printf("Filtered entries: %d\n", len(result))
// Using Count()
count := apnic.NewFilter(entries).
ByCountry("JP").
ByType("ipv6").
Count()
fmt.Printf("Japan IPv6 entries: %d\n", count)
}
Extended Filter with OpaqueID
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
// Fetch extended entries (with opaqueId)
entries, err := client.GetExtendedEntries(ctx)
if err != nil {
log.Fatal(err)
}
// Filter by opaqueId (organization)
opaqueID := "A92E1062"
filtered := apnic.NewExtendedFilter(entries).
ByOpaqueID(opaqueID).
Result()
fmt.Printf("Entries for organization %s: %d\n", opaqueID, len(filtered))
for _, e := range filtered {
cidr, _ := e.CIDR()
fmt.Printf(" %s (%s)\n", cidr, e.Type)
}
}
Grouping by Country
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetDelegatedEntries(ctx)
// Group by country
byCountry := apnic.GroupByCountry(entries)
fmt.Println("Entries by Country (top 10):")
// Sort and display
type kv struct {
Country string
Count int
}
var sorted []kv
for cc, ents := range byCountry {
sorted = append(sorted, kv{cc, len(ents)})
}
// Simple sort (in production, use sort.Slice)
for i := 0; i < 10 && i < len(sorted); i++ {
fmt.Printf(" %s: %d\n", sorted[i].Country, sorted[i].Count)
}
}
Grouping by Organization
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetExtendedEntries(ctx)
// Group by opaqueId (organization)
byOrg := apnic.GroupExtendedByOpaqueID(entries)
fmt.Printf("Unique organizations: %d\n", len(byOrg))
// Find organizations with most resources
type orgInfo struct {
OpaqueID string
Count int
}
var orgs []orgInfo
for id, ents := range byOrg {
orgs = append(orgs, orgInfo{id, len(ents)})
}
fmt.Println("\nOrganizations with most resources:")
// (In production, sort and show top N)
for i := 0; i < 5 && i < len(orgs); i++ {
fmt.Printf(" %s: %d resources\n", orgs[i].OpaqueID, orgs[i].Count)
}
}
Date Range Filtering
package main
import (
"context"
"fmt"
"time"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetDelegatedEntries(ctx)
// Define date ranges
q1Start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
q1End := time.Date(2024, 4, 1, 0, 0, 0, 0, time.UTC)
q1Entries := apnic.NewFilter(entries).
ByDateRange(q1Start, q1End).
Result()
fmt.Printf("Q1 2024 allocations: %d\n", len(q1Entries))
// Allocations in last 30 days
now := time.Now()
monthAgo := now.AddDate(0, -1, 0)
recent := apnic.NewFilter(entries).
ByDateRange(monthAgo, now).
Result()
fmt.Printf("Last 30 days: %d\n", len(recent))
}
CIDR Conversion
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetDelegatedEntries(ctx)
// Convert entries to CIDR notation
fmt.Println("First 10 IPv4 entries as CIDR:")
count := 0
for _, e := range entries {
if e.Type != "ipv4" {
continue
}
cidr, err := e.CIDR()
if err != nil {
continue
}
fmt.Printf(" %s (Country: %s, Status: %s)\n",
cidr, e.Country, e.Status)
count++
if count >= 10 {
break
}
}
}
Combined Analysis
package main
import (
"context"
"fmt"
"log"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetExtendedEntries(ctx)
// Comprehensive analysis for a country
country := "AU"
auIPv4 := apnic.NewExtendedFilter(entries).
ByCountry(country).
ByType("ipv4").
ByStatus("allocated").
Result()
auIPv6 := apnic.NewExtendedFilter(entries).
ByCountry(country).
ByType("ipv6").
Result()
auASN := apnic.NewExtendedFilter(entries).
ByCountry(country).
ByType("asn").
Result()
fmt.Printf("Resource Summary for %s:\n", country)
fmt.Printf(" IPv4 allocated: %d\n", len(auIPv4))
fmt.Printf(" IPv6 entries: %d\n", len(auIPv6))
fmt.Printf(" ASN entries: %d\n", len(auASN))
// Group by organization
auByOrg := apnic.GroupExtendedByOpaqueID(auIPv4)
fmt.Printf(" Unique IPv4 holders: %d\n", len(auByOrg))
}
Multiple Country Comparison
package main
import (
"context"
"fmt"
apnic "github.com/cyberspacesec/apnic-skills"
)
func main() {
client := apnic.NewClient()
ctx := context.Background()
entries, _ := client.GetDelegatedEntries(ctx)
countries := []string{"CN", "JP", "KR", "AU", "SG"}
fmt.Println("IPv4 Allocations by Country:")
fmt.Println("Country | Allocated | Assigned | Reserved")
fmt.Println("--------|-----------|----------|---------")
for _, cc := range countries {
allocated := len(apnic.NewFilter(entries).
ByCountry(cc).ByType("ipv4").ByStatus("allocated").Result())
assigned := len(apnic.NewFilter(entries).
ByCountry(cc).ByType("ipv4").ByStatus("assigned").Result())
reserved := len(apnic.NewFilter(entries).
ByCountry(cc).ByType("ipv4").ByStatus("reserved").Result())
fmt.Printf("%-7s | %9d | %8d | %8d\n", cc, allocated, assigned, reserved)
}
}
Data Types
DelegatedEntry
type DelegatedEntry struct {
Registry string // "apnic"
Country string // ISO 3166 code
Type string // "ipv4", "ipv6", "asn"
Start string // Starting address/ASN
Value int64 // Count (IPv4) or prefix length (IPv6)
Date time.Time // Allocation date
Status string // "allocated", "assigned", "reserved", "available"
}
DelegatedExtendedEntry
type DelegatedExtendedEntry struct {
Registry string
Country string
Type string
Start string
Value int64
Date time.Time
Status string
OpaqueID string // Organization identifier
}
Status Values
| Status | Description |
allocated | Allocated to LIR |
assigned | Assigned to end user |
reserved | Reserved by registry |
available | Available for allocation |
Resource Types
| Type | Description |
ipv4 | IPv4 address space |
ipv6 | IPv6 address space |
asn | Autonomous System Numbers |
Error Handling
cidr, err := entry.CIDR()
if err != nil {
// Possible errors:
// - ErrInvalidIP: invalid count/prefix value
// - ErrUnsupportedType: not ipv4 or ipv6
log.Printf("CIDR conversion failed: %v", err)
}