ipv6disc

package module
v0.8.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2026 License: Apache-2.0 Imports: 15 Imported by: 4

README

IPv6 Host Discovery

This program scans the local network and discovers IPv6 hosts addresses.

Used by ipv6ddns

Installation

Download the latest release for your architecture.

Or use the docker image

Minimal run (active discovery only):

docker run -it --rm --network host gcr.io/miguelangel-nubla/ipv6disc -live

Run with plugins (recommended):

docker run -it --rm --network host gcr.io/miguelangel-nubla/ipv6disc \
  -live \
  -plugin "router-main=mikrotik:60s,192.168.88.1,admin,password"

[!IMPORTANT] Docker IPv6 support is experimental/fragile. Active local discovery (NDP/Multicast) often fails inside containers depending on the host network configuration. It is highly recommended to use Plugins to fetch neighbor data directly from your router instead.

Or build from source

Use go install github.com/miguelangel-nubla/ipv6disc/cmd/ipv6disc@latest to build and install the binary.

How it Works

The tool uses two complementary methods to discover IPv6 hosts:

1. Active Local Discovery

It listens on all available IPv6 interfaces and actively scans for hosts using multiple protocols:

  • ICMPv6: Sends multicast Echo Requests (ping) to all nodes.
  • SSDP: Sends Simple Service Discovery Protocol M-SEARCH packets (used by UPnP).
  • WS-Discovery: Sends Web Services for Devices Probe messages (used by Windows network discovery).

When a host responds to any of these probes, the tool triggers a specific NDP (Neighbor Discovery Protocol) solicitation to resolve the host's MAC address and register it.

2. Plugin Integration

For hosts that might not be directly reachable via multicast (e.g. on different VLANs or filtered by firewalls) or to get a comprehensive view from the network gateway, ipv6disc supports plugins. Plugins connect to routers/firewalls (Linux, FreeBSD/OPNsense, Mikrotik) to fetch their known neighbor tables. This allows populating the list with hosts that the router sees.

Usage

By default, it will output the discovered hosts as JSON messages to stdout. You can easily parse the output with jq.

[!NOTE] This utility needs to be executed as a superuser to be able to listen for IPv6 ICMP packets.

sudo ipv6disc | jq 'select(.msg == "host identified") | .ipv6'

Alternatively, using -live the data will be displayed on the screen in a human-readable form.

If you need to pause and select/copy data use screen, launch ipv6disc -live [...] and press Ctrl+a and then ESC to enter copy mode. Now you can use the mouse to copy text. When finished ESC again will continue updating.

Flags

  • -log_level: Set the logging level (default: "info"). Available options: "debug", "info", "warn", "error", "fatal", "panic".
  • -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).
  • -plugin: Plugin configuration: name=type:params (can be specified multiple times, multiple instances of the same plugin connected to different hosts are allowed)

Plugins

Linux / OpenWrt

connect to a Linux host via SSH and parse ip -6 neigh output. format: name=linux:interval,address,username,password[,identity_file]

  • interval: Polling interval (e.g. 60s, 5m)
  • address: Hostname or IP address (and optional port) (e.g., 192.168.1.1, [2001:db8::1]:2222)
  • username: SSH username
  • password: SSH password (optional if identity_file provided)
  • identity_file: Path to SSH private key file (optional)
pfSense / OPNsense / FreeBSD

Connect to a FreeBSD host via SSH and parse ndp -an output. format: name=freebsd:interval,address,username,password[,identity_file]

  • interval: Polling interval (e.g. 60s, 5m)
  • address: Hostname or IP address (and optional port)
  • username: SSH username
  • password: SSH password (optional if identity_file provided)
  • identity_file: Path to SSH private key file (optional)
Mikrotik

Connect to a Mikrotik router via API and parse IPv6 neighbor list. format: name=mikrotik:interval,address,username,password[,use_tls[,tls_fingerprint]]

  • interval: Polling interval (e.g. 60s, 5m)
  • address: Hostname or IP address (and optional port). Defaults to port 8728 (plain) or 8729 (TLS).
  • username: API username
  • password: API password
  • use_tls: true/false (default: false)
  • tls_fingerprint: Expected SHA256 fingerprint of the server certificate (optional)

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Addr

type Addr struct {
	netip.Addr
	Hw      net.HardwareAddr
	Sources []string
	// contains filtered or unexported fields
}

func NewAddr

func NewAddr(hw net.HardwareAddr, addr netip.Addr, source string, lifetime time.Duration, onExpiration func(*Addr, AddrExpirationRemainingEvents)) *Addr

func (*Addr) GetExpiration

func (a *Addr) GetExpiration() time.Time

func (*Addr) Seen

func (a *Addr) Seen(source ...string)

func (*Addr) Unwatch

func (a *Addr) Unwatch()

func (*Addr) Valid added in v0.3.2

func (a *Addr) Valid() bool

func (*Addr) Watch

func (a *Addr) Watch()

type AddrCollection

type AddrCollection struct {
	// contains filtered or unexported fields
}

func NewAddrCollection

func NewAddrCollection() *AddrCollection

func (*AddrCollection) Add added in v0.3.3

func (c *AddrCollection) Add(addr *Addr) (*Addr, bool)

func (*AddrCollection) Contains

func (c *AddrCollection) Contains(addr *Addr) bool

func (*AddrCollection) Copy added in v0.2.1

func (c *AddrCollection) Copy() *AddrCollection

func (*AddrCollection) Equal

func (c *AddrCollection) Equal(addrCollection *AddrCollection) bool

func (*AddrCollection) Filter4

func (c *AddrCollection) Filter4() *AddrCollection

func (*AddrCollection) Filter6

func (c *AddrCollection) Filter6() *AddrCollection

func (*AddrCollection) FilterSubnets added in v0.3.0

func (c *AddrCollection) FilterSubnets(subnets []netip.Prefix) *AddrCollection

func (*AddrCollection) FilterValid added in v0.3.2

func (c *AddrCollection) FilterValid() *AddrCollection

func (*AddrCollection) Get

func (c *AddrCollection) Get() []*Addr

func (*AddrCollection) Join

func (c *AddrCollection) Join(addrCollection *AddrCollection)

func (*AddrCollection) PrettyPrint

func (c *AddrCollection) PrettyPrint(prefix string) string

func (*AddrCollection) Remove

func (c *AddrCollection) Remove(addr *Addr)

func (*AddrCollection) Seen added in v0.3.3

func (c *AddrCollection) Seen(addr *Addr, source ...string) (*Addr, bool)

func (*AddrCollection) Strings

func (c *AddrCollection) Strings() []string

type AddrExpirationRemainingEvents

type AddrExpirationRemainingEvents int

type DiscoveryProtocol added in v0.4.0

type DiscoveryProtocol struct {
	Name   string
	Worker worker
}

type HostCounter added in v0.4.0

type HostCounter interface {
	IncrementHostsFound()
}

type InterfaceProcesses added in v0.4.0

type InterfaceProcesses struct {
	InterfaceName string
	Address       netip.Addr
	Protocols     []*DiscoveryProtocol
}

type InvalidInterfaceError

type InvalidInterfaceError struct {
	// contains filtered or unexported fields
}

func (*InvalidInterfaceError) Error

func (e *InvalidInterfaceError) Error() string

type Plugin added in v0.4.0

type Plugin interface {
	// Name returns the name of the plugin.
	Name() string
	// Start starts the plugin. It should block until the context is cancelled.
	Start(ctx context.Context, state *State, onError func(error)) error
	// Stats returns the current statistics for the plugin.
	Stats() map[string]any
}

Plugin is the interface that must be implemented by discovery plugins.

type State

type State struct {
	// contains filtered or unexported fields
}

func NewState

func NewState() *State

func (*State) FilterMACs added in v0.3.0

func (s *State) FilterMACs(hws []net.HardwareAddr) *AddrCollection

func (*State) GetAll added in v0.7.0

func (s *State) GetAll() map[string]*AddrCollection

func (*State) PrettyPrint

func (s *State) PrettyPrint(prefix string, hideSensible bool) string

func (*State) Register added in v0.4.1

func (s *State) Register(hw net.HardwareAddr, ip netip.Addr, source string, lifetime time.Duration, onExpiration func(*Addr, AddrExpirationRemainingEvents)) (*Addr, bool)

Register adds a discovered address to the state. It handles creation of new Addr objects, calling Seen, and logging "host identified" events. If onExpiration is nil, a default handler is used which logs "host expired".

func (*State) Seen added in v0.3.3

func (s *State) Seen(addr *Addr, source ...string) (*Addr, bool)

accepts default TTL and onExpiration function

func (*State) SetLogger added in v0.4.1

func (s *State) SetLogger(l *zap.SugaredLogger)

type Worker

type Worker struct {
	*State
	// contains filtered or unexported fields
}

func NewWorker

func NewWorker(logger *zap.SugaredLogger, rediscover time.Duration, lifetime time.Duration, discoveryListen bool, discoveryActive bool) *Worker

func (*Worker) PrettyPrintStats added in v0.4.0

func (w *Worker) PrettyPrintStats(prefix string) string

func (*Worker) RegisterPlugin added in v0.4.0

func (w *Worker) RegisterPlugin(p Plugin)

func (*Worker) Start

func (w *Worker) Start() error

func (*Worker) StartInterfaceAddr

func (w *Worker) StartInterfaceAddr(ctx context.Context, iface net.Interface, addr netip.Addr)

Directories

Path Synopsis
cmd
ipv6disc command
pkg
ndp
wsd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL