Skip to content

Commit

Permalink
Major refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelangel-nubla committed Dec 30, 2024
1 parent 3fcc8ce commit de3dbfb
Show file tree
Hide file tree
Showing 11 changed files with 459 additions and 342 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/ipv6disc
cmd/ipv6disc/ipv6disc
cmd/ipv6disc/ipv6disc.exe
dist/
13 changes: 9 additions & 4 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
version: 2
before:
hooks:
- go mod tidy
- go generate ./...
- go mod tidy
builds:
- env:
- main: cmd/ipv6disc/ipv6disc.go
env:
- CGO_ENABLED=0
goos:
# - aix
Expand Down Expand Up @@ -85,6 +87,8 @@ dockers:
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.url=https://github.com/miguelangel-nubla/{{ .ProjectName }}"
- "--label=org.opencontainers.image.source=https://github.com/miguelangel-nubla/{{ .ProjectName }}"
extra_files:
- LICENSE.txt
-
image_templates:
- "ghcr.io/miguelangel-nubla/{{.ProjectName}}:{{ .Tag }}-armv7"
Expand All @@ -99,6 +103,8 @@ dockers:
- "--label=org.opencontainers.image.version={{.Version}}"
- "--label=org.opencontainers.image.url=https://github.com/miguelangel-nubla/{{ .ProjectName }}"
- "--label=org.opencontainers.image.source=https://github.com/miguelangel-nubla/{{ .ProjectName }}"
extra_files:
- LICENSE.txt
-
image_templates:
- "ghcr.io/miguelangel-nubla/{{.ProjectName}}:{{ .Tag }}-arm64v8"
Expand Down Expand Up @@ -151,7 +157,6 @@ docker_manifests:

archives:
-
rlcp: true
files:
- LICENSE*
- README*
Expand All @@ -171,7 +176,7 @@ archives:
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
version_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Download the [latest release](https://github.com/miguelangel-nubla/ipv6disc/rele

Ensure you have Go installed on your system. If not, follow the instructions on the official [Go website](https://golang.org/doc/install) to install it. Then:
```
go install github.com/miguelangel-nubla/ipv6disc
go install github.com/miguelangel-nubla/ipv6disc/cmd/ipv6disc
```

### Or use the docker image
Expand All @@ -39,7 +39,7 @@ If you need to pause and select/copy data use `screen`, launch `ipv6disc -live [
## Flags

- `-log_level`: Set the logging level (default: "info"). Available options: "debug", "info", "warn", "error", "fatal", "panic".
- `-ttl`: Set the time-to-live (TTL) for a discovered host entry in the table after it has been last seen (default: 4 hours).
- `-lifetime`: Set the lifetime for a discovered host entry after it has been last seen (default: 4 hours).
- `-live`: Show the current state live on the terminal (default: false).

## License
Expand Down
106 changes: 106 additions & 0 deletions addr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package ipv6disc

import (
"net"
"net/netip"
"sync"
"time"
)

type Addr struct {
netip.Addr
Hw net.HardwareAddr

lifetime time.Duration
onExpiration func(*Addr, AddrExpirationRemainingEvents)

expiration3 *time.Timer
expiration2 *time.Timer
expiration1 *time.Timer
expiration *time.Timer
expirationTime time.Time

unwatch chan bool

mutex sync.RWMutex
}

type AddrExpirationRemainingEvents int

func (a *Addr) resetTimers(resetExpirationTime bool) {
a.mutex.Lock()
defer a.mutex.Unlock()

a.expiration3.Reset(a.lifetime / 3 * 2)
a.expiration2.Reset(a.lifetime / 4 * 3)
a.expiration1.Reset(a.lifetime / 5 * 4)
a.expiration.Reset(a.lifetime)

if resetExpirationTime {
a.expirationTime = time.Now().Add(a.lifetime)
}
}

func (a *Addr) IsStillValid() bool {
return a.expirationTime.After(time.Now())
}

func (a *Addr) GetExpiration() time.Time {
a.mutex.RLock()
defer a.mutex.RUnlock()

return a.expirationTime
}

func (a *Addr) Seen() {
a.resetTimers(true)
}

func (a *Addr) Watch() {
go func() {
for {
select {
case <-a.expiration3.C:
a.onExpiration(a, 3)
case <-a.expiration2.C:
a.onExpiration(a, 2)
case <-a.expiration1.C:
a.onExpiration(a, 1)
case <-a.expiration.C:
a.onExpiration(a, 0)
return
case <-a.unwatch:
return
}
}
}()

a.resetTimers(false)
}

func (a *Addr) Unwatch() {
a.mutex.Lock()
defer a.mutex.Unlock()

a.expiration3.Stop()
a.expiration2.Stop()
a.expiration1.Stop()
a.expiration.Stop()

close(a.unwatch)
}

func NewAddr(hw net.HardwareAddr, addr netip.Addr, lifetime time.Duration, onExpiration func(*Addr, AddrExpirationRemainingEvents)) *Addr {
return &Addr{
Addr: addr,
Hw: hw,
lifetime: lifetime,
onExpiration: onExpiration,
expiration3: time.NewTimer(lifetime / 3 * 2),
expiration2: time.NewTimer(lifetime / 4 * 3),
expiration1: time.NewTimer(lifetime / 5 * 4),
expiration: time.NewTimer(lifetime),
expirationTime: time.Now().Add(lifetime),
unwatch: make(chan bool),
}
}
182 changes: 182 additions & 0 deletions addr_collection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package ipv6disc

import (
"fmt"
"maps"
"net/netip"
"slices"
"sort"
"strings"
"sync"
"time"
)

type AddrCollection struct {
// string in key avoids looping over Addr.String() in the map
addresses map[string]*Addr
addressesMutex sync.RWMutex
}

func (c *AddrCollection) Enlist(addr *Addr) (*Addr, bool) {
c.addressesMutex.Lock()
defer c.addressesMutex.Unlock()

addString := addr.String()

existing := false
if c.Contains(addr) {
existing = true
} else {
c.addresses[addString] = addr
}

c.addresses[addString].Seen()

return c.addresses[addString], existing
}

func (c *AddrCollection) Remove(addr *Addr) {
c.addressesMutex.Lock()
defer c.addressesMutex.Unlock()

if c.Contains(addr) {
c.addresses[addr.String()].Unwatch()
delete(c.addresses, addr.String())
}
}

func (c *AddrCollection) Join(addr *AddrCollection) {
c.addressesMutex.Lock()
defer c.addressesMutex.Unlock()

for addr, info := range addr.addresses {
c.addresses[addr] = info
}
}

func (c *AddrCollection) Contains(addr *Addr) bool {
_, ok := c.addresses[addr.String()]
return ok
}

func (c *AddrCollection) Equal(other *AddrCollection) bool {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()
other.addressesMutex.RLock()
defer other.addressesMutex.RUnlock()

if len(c.addresses) != len(other.addresses) {
return false
}

for addrKey, _ := range c.addresses {
if _, ok := other.addresses[addrKey]; !ok {
return false
}
}

return true
}

func (c *AddrCollection) FilterPrefix(prefix netip.Prefix) *AddrCollection {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

result := NewAddrCollection()
for _, addr := range c.addresses {
if prefix.Contains(addr.Addr.WithZone("")) {
result.Enlist(addr)
}
}

return result
}

func (c *AddrCollection) Filter6() *AddrCollection {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

result := NewAddrCollection()
for _, addr := range c.addresses {
if addr.Addr.Is6() {
result.Enlist(addr)
}
}
return result
}

func (c *AddrCollection) Filter4() *AddrCollection {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

result := NewAddrCollection()
for _, addr := range c.addresses {
if addr.Addr.Is4() {
result.Enlist(addr)
}
}
return result
}

func (c *AddrCollection) Get() []*Addr {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

keys := make([]string, 0, len(c.addresses))
for key := range c.addresses {
keys = append(keys, key)
}
sort.Strings(keys)

addresses := make([]*Addr, 0, len(c.addresses))
for _, key := range keys {
addresses = append(addresses, c.addresses[key])
}

return addresses
}

func (c *AddrCollection) Strings() []string {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

addressesMap := make(map[string]bool)
for _, addr := range c.addresses {
ip := addr.WithZone("").String()
addressesMap[ip] = true
}

return slices.Collect(maps.Keys(addressesMap))
}

func (c *AddrCollection) PrettyPrint(prefix string) string {
c.addressesMutex.RLock()
defer c.addressesMutex.RUnlock()

var result strings.Builder
c.addressesMutex.RLock()

// Get the keys from the map
keys := make([]netip.Addr, 0, len(c.addresses))
for _, addr := range c.addresses {
keys = append(keys, addr.Addr)
}

// Sort the keys
sort.Slice(keys, func(i, j int) bool {
return keys[i].Less(keys[j])
})

// Iterate ordered
for _, key := range keys {
ipAddressInfo := c.addresses[key.String()]
fmt.Fprintf(&result, prefix+"%s %s\n", ipAddressInfo.Addr.String(), time.Until(ipAddressInfo.GetExpiration()).Round(time.Second))
}
c.addressesMutex.RUnlock()

return result.String()
}

func NewAddrCollection() *AddrCollection {
return &AddrCollection{addresses: make(map[string]*Addr)}
}
Loading

0 comments on commit de3dbfb

Please sign in to comment.