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

23 memory leak issue in the network package when handling a large range of ipv6 hosts #24

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 10 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ The documentation for the Subping library can be found in the [docs](docs/) dire

The library consists of the following packages:

- **github.com/fadhilyori/subping**: The main package that provides the Subping struct and related functionalities.
- **github.com/fadhilyori/subping/pkg/network**: A subpackage that offers network-related utilities for working with IP addresses and subnet ranges.
- **[github.com/fadhilyori/subping](docs/)**: The main package that provides the Subping struct and related functionalities.
- **[github.com/fadhilyori/subping/pkg/network](docs/network)**: A subpackage that offers network-related utilities for working with IP addresses and subnet ranges.

Please refer to the documentation for the respective packages to understand how to use them in your applications.

Expand Down Expand Up @@ -55,24 +55,19 @@ To use the Subping library, follow these steps:
```go
import (
"github.com/fadhilyori/subping"
"github.com/fadhilyori/subping/pkg/network"
)
```

2. Create an instance of Subping by calling `NewSubping` with the desired options:

```go
subnetString := "172.17.0.0/24"
targets, err := network.GenerateIPListFromCIDRString(subnetString)
if err != nil {
log.Fatal(err.Error())
}

opts := &subping.Options{
Targets: targets,
LogLevel: "debug",
Subnet: "172.17.0.0/24",
Count: 3,
Timeout: 300 * time.Millisecond,
NumJobs: 8,
Interval: 1 * time.Second
Timeout: 3 * time.Second,
MaxWorkers: 8,
}

sp, err := subping.NewSubping(opts)
Expand All @@ -92,13 +87,13 @@ Note: Ensure that you have imported the necessary packages, such as `"time"` and

This will initiate the ICMP ping operations on the specified IP addresses.

4. Retrieve the results using the `GetResults` method:
4. Retrieve the results:

```go
results := sp.GetResults()
results := sp.Results
```

The `results` variable will contain a map where the keys are the IP addresses, and the values are `*ping.Statistics`
The `results` variable will contain a map where the keys are the IP addresses, and the values are `*subping.Result`
representing the ping statistics for each IP address.

5. Optionally, you can use the `GetOnlineHosts` method to filter the results and obtain only the IP addresses that
Expand Down
Binary file modified assets/images/usage-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 28 additions & 36 deletions cmd/subping/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ import (

"github.com/common-nighthawk/go-figure"
"github.com/fadhilyori/subping"
"github.com/fadhilyori/subping/pkg/network"
"github.com/spf13/cobra"
)

var (
pingCount int
pingTimeoutStr string
pingIntervalStr string
pingNumJobs int
pingMaxWorkers int
subpingVersion = "latest"
showOfflineHostList bool
)
Expand All @@ -33,6 +32,7 @@ func main() {
Run: runSubping,
PreRun: func(cmd *cobra.Command, args []string) {
figure.NewFigure("subping", "larry3d", true).Print()
fmt.Println(cmd.Version)
fmt.Print("\n\n")
},
}
Expand All @@ -42,7 +42,7 @@ func main() {
flags.IntVarP(&pingCount, "count", "c", 1,
"Specifies the number of ping attempts for each IP address.",
)
flags.IntVarP(&pingNumJobs,
flags.IntVarP(&pingMaxWorkers,
"job", "n", 128,
"Specifies the number of maximum concurrent jobs spawned to perform ping operations.",
)
Expand All @@ -61,7 +61,7 @@ func main() {
}
}

func runSubping(cmd *cobra.Command, args []string) {
func runSubping(_ *cobra.Command, args []string) {
subnetString := args[0]

startTime := time.Now()
Expand All @@ -76,41 +76,33 @@ func runSubping(cmd *cobra.Command, args []string) {
log.Fatal(err.Error())
}

ips, err := network.GenerateIPListFromCIDRString(subnetString)
if err != nil {
log.Fatal(err.Error())
}

totalHost := len(ips)

s, err := subping.NewSubping(&subping.Options{
Targets: ips,
Count: pingCount,
Interval: pingInterval,
Timeout: pingTimeout * time.Duration(pingCount),
NumJobs: pingNumJobs,
Subnet: subnetString,
Count: pingCount,
Interval: pingInterval,
Timeout: pingTimeout * time.Duration(pingCount),
MaxWorkers: pingMaxWorkers,
LogLevel: "error",
})
if err != nil {
log.Fatal(err.Error())
}

_, cidr, err := net.ParseCIDR(subnetString)
if err != nil {
log.Fatal(err.Error())
}

fmt.Printf("Network : %s\n", cidr.String())
fmt.Printf("IP Ranges : %s - %s\n", ips[0].String(), ips[len(ips)-1].String())
fmt.Printf("Total hosts : %d\n", totalHost)
fmt.Printf("Num of workers : %d\n", len(s.PartitionedTargets))
fmt.Println(`---------------------------------------`)
fmt.Println("| IP Address | Avg Latency |")
fmt.Println(`---------------------------------------`)
fmt.Printf("Network : %s\n", s.TargetsIterator.IPNet.String())
fmt.Printf("IP Ranges : %s - %s\n", s.TargetsIterator.FirstIP.String(), s.TargetsIterator.LastIP.String())
fmt.Printf("Total hosts : %d\n", s.TargetsIterator.TotalHosts)
fmt.Printf("Total workers : %d\n", s.MaxWorkers)
fmt.Printf("Count : %d\n", s.Count)
fmt.Printf("Interval : %s\n", s.Interval.String())
fmt.Printf("Timeout : %s\n", s.Timeout.String())
fmt.Println(`--------------------------------------------------------`)
fmt.Println("| IP Address | Avg Latency | Packet Loss |")
fmt.Println(`--------------------------------------------------------`)
fmt.Printf("Pinging...")

s.Run()

results := s.GetOnlineHosts()
results, totalHostOnline := s.GetOnlineHosts()

// Extract keys into a slice
keys := make([]net.IP, 0, len(results))
Expand All @@ -120,7 +112,7 @@ func runSubping(cmd *cobra.Command, args []string) {

// Sort the keys Based on byte comparison
sort.Slice(keys, func(i, j int) bool {
return bytes.Compare(keys[i].To4(), keys[j].To4()) < 0
return bytes.Compare(keys[i].To16(), keys[j].To16()) < 0
})

fmt.Print("\r")
Expand All @@ -129,24 +121,24 @@ func runSubping(cmd *cobra.Command, args []string) {
// convert bytes to string in each line of IP
ipString := ip.String()
stats := results[ipString]
packetLossPercentageStr := fmt.Sprintf("%.2f %%", stats.PacketLoss)

fmt.Printf("| %-16s | %-16s |\n", ipString, stats.AvgRtt.String())
fmt.Printf("| %-16s | %-16s | %-14s |\n", ipString, stats.AvgRtt.String(), packetLossPercentageStr)
}

fmt.Println(`---------------------------------------`)
fmt.Println(`--------------------------------------------------------`)

if showOfflineHostList {
fmt.Println("Offline hosts :")
fmt.Println("\nOffline hosts :")
for ip, stats := range s.Results {
if stats.PacketsRecv == 0 {
fmt.Printf(" - %s\n", ip)
fmt.Printf(" - %s\t(Loss: %s, Latency: %s)\n", ip, fmt.Sprintf("%.2f %%", stats.PacketLoss), stats.AvgRtt.String())
}
}
}

elapsed := time.Since(startTime)
totalHostOnline := len(results)
totalHostOffline := totalHost - totalHostOnline
totalHostOffline := s.TargetsIterator.TotalHosts - totalHostOnline

fmt.Printf("\nTotal Hosts Online : %d\n", totalHostOnline)
fmt.Printf("Total Hosts Offline : %d\n", totalHostOffline)
Expand Down
116 changes: 90 additions & 26 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,46 @@
import "github.com/fadhilyori/subping"
```

Package subping provides a utility for concurrently pinging multiple IP addresses and collecting the results.

The package includes functionality for running ping operations on multiple IP addresses concurrently, calculating ping statistics, and partitioning data for parallel processing.

Example usage:

```
// Create options for Subping
opts := &subping.Options{
LogLevel: "info",
Subnet: "192.168.0.0/24",
Count: 5,
Interval: time.Second,
Timeout: 2 * time.Second,
MaxWorkers: 10,
}

// Create a new Subping instance
sp, err := subping.NewSubping(opts)
if err != nil {
log.Fatalf("Failed to create Subping instance: %v", err)
}

// Run the Subping process
sp.Run()

// Get the online hosts and their statistics
onlineHosts, total := sp.GetOnlineHosts()
fmt.Printf("Online Hosts: %v\n", onlineHosts)
fmt.Printf("Total Online Hosts: %d\n", total)
```

## Index

- [func RunPing\(ipAddress string, count int, interval time.Duration, timeout time.Duration\) ping.Statistics](<#RunPing>)
- [type Options](<#Options>)
- [type Result](<#Result>)
- [type Subping](<#Subping>)
- [func NewSubping\(opts \*Options\) \(Subping, error\)](<#NewSubping>)
- [func \(s \*Subping\) GetOnlineHosts\(\) map\[string\]ping.Statistics](<#Subping.GetOnlineHosts>)
- [func NewSubping\(opts \*Options\) \(\*Subping, error\)](<#NewSubping>)
- [func \(s \*Subping\) GetOnlineHosts\(\) \(map\[string\]Result, int\)](<#Subping.GetOnlineHosts>)
- [func \(s \*Subping\) Run\(\)](<#Subping.Run>)


Expand All @@ -23,7 +56,7 @@ import "github.com/fadhilyori/subping"
func RunPing(ipAddress string, count int, interval time.Duration, timeout time.Duration) ping.Statistics
```

RunPing sends ICMP echo requests to the specified IP address and returns the ping statistics.
RunPing performs a ping operation to the specified IP address. It sends the specified number of ping requests with the given interval and timeout.

<a name="Options"></a>
## type Options
Expand All @@ -32,58 +65,89 @@ Options holds the configuration options for creating a new Subping instance.

```go
type Options struct {
// List of IP addresses to ping
Targets []net.IP
// LogLevel sets the log levels for the Subping instance.
LogLevel string

// Subnet is the subnet to scan for IP addresses to ping.
Subnet string

// Number of ping packets to send
// Count is the number of ping requests to send for each target.
Count int

// Interval for each ping request
// Interval is the time duration between each ping request.
Interval time.Duration

// Timeout specifies a timeout before exits each target
// Timeout specifies the timeout duration before exiting each target.
Timeout time.Duration

// Number of concurrent jobs to execute
NumJobs int
// MaxWorkers specifies the maximum number of concurrent workers to use.
MaxWorkers int
}
```

<a name="Result"></a>
## type Result

Result contains the statistics and metrics for a single ping operation.

```go
type Result struct {
// AvgRtt is the average round-trip time of the ping requests.
AvgRtt time.Duration

// PacketLoss is the percentage of packets lost during the ping operation.
PacketLoss float64

// PacketsSent is the number of packets sent for the ping operation.
PacketsSent int

// PacketsRecv is the number of packets received for the ping operation.
PacketsRecv int

// PacketsRecvDuplicates is the number of duplicate packets received.
PacketsRecvDuplicates int
}
```

<a name="Subping"></a>
## type Subping

Subping is a utility for concurrently pinging multiple IP addresses and collecting the Results.
Subping is a utility for concurrently pinging multiple IP addresses and collecting the results.

```go
type Subping struct {
// List of IP addresses to ping
Targets []net.IP
// TargetsIterator is an iterator for the target IP addresses to ping.
TargetsIterator *network.SubnetHostsIterator

// Number of ping packets to send
// Count is the number of ping requests to send for each target.
Count int

// Interval for each ping request
// Interval is the time duration between each ping request.
Interval time.Duration

// Timeout specifies a timeout before exits each target
// Timeout specifies the timeout duration before exiting each target.
Timeout time.Duration

// Number of concurrent jobs to execute
NumJobs int
// BatchSize is the number of concurrent ping jobs to execute.
BatchSize int64

// Results stores the ping results for each target IP address.
Results map[string]Result

// Results of the ping requests
Results map[string]ping.Statistics
// TotalResults represents the total number of ping results collected.
TotalResults int

// PartitionedTargets List of IP addresses that have already been partitioned for pinging.
PartitionedTargets [][]net.IP
// MaxWorkers specifies the maximum number of concurrent workers to use.
MaxWorkers int
// contains filtered or unexported fields
}
```

<a name="NewSubping"></a>
### func NewSubping

```go
func NewSubping(opts *Options) (Subping, error)
func NewSubping(opts *Options) (*Subping, error)
```

NewSubping creates a new Subping instance with the provided options.
Expand All @@ -92,10 +156,10 @@ NewSubping creates a new Subping instance with the provided options.
### func \(\*Subping\) GetOnlineHosts

```go
func (s *Subping) GetOnlineHosts() map[string]ping.Statistics
func (s *Subping) GetOnlineHosts() (map[string]Result, int)
```

GetOnlineHosts returns the Results of the ping requests for IP addresses that responded successfully.
GetOnlineHosts returns a map of online hosts and their corresponding ping results, as well as the total number of online hosts.

<a name="Subping.Run"></a>
### func \(\*Subping\) Run
Expand All @@ -104,6 +168,6 @@ GetOnlineHosts returns the Results of the ping requests for IP addresses that re
func (s *Subping) Run()
```

Run starts the ping operation on the specified IP addresses using the configured options.
Run starts the Subping process, concurrently pinging the target IP addresses. It spawns worker goroutines, assigns tasks to them, waits for them to finish, and collects the results.

Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
Loading