Skip to content

Commit

Permalink
cmd/ndp: remove ra, simplify help
Browse files Browse the repository at this point in the history
  • Loading branch information
mdlayher committed Nov 22, 2022
1 parent 7242c5b commit 9478dfb
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 107 deletions.
36 changes: 13 additions & 23 deletions cmd/ndp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func main() {
ifiFlag = flag.String("i", "", "network interface to use for NDP communication (default: automatic)")
addrFlag = flag.String("a", string(ndp.LinkLocal), "address to use for NDP communication (unspecified, linklocal, uniquelocal, global, or a literal IPv6 address)")
targetFlag = flag.String("t", "", "IPv6 target address for neighbor solicitation NDP messages")
prefixFlag = flag.String("p", "", "IPv6 address prefix (without CIDR mask) to advertise with router advertisement NDP messages")
)

flag.Usage = func() {
Expand Down Expand Up @@ -55,17 +54,6 @@ func main() {
}
}

var prefix netip.Addr
if p := *prefixFlag; p != "" {
prefix, err = netip.ParseAddr(p)
if err != nil {
ll.Fatalf("failed to parse IPv6 prefix address: %v", err)
}
if prefix != netip.PrefixFrom(prefix, 64).Masked().Addr() {
ll.Fatalf("prefix must be a valid /64, got: %q", p)
}
}

sigC := make(chan os.Signal, 1)
signal.Notify(sigC, os.Interrupt)

Expand All @@ -88,7 +76,7 @@ func main() {
ll.Printf("interface: %s, link-layer address: %s, IPv6 address: %s",
ifi.Name, mac, ip)

if err := ndpcmd.Run(ctx, c, ifi, flag.Arg(0), target, prefix); err != nil {
if err := ndpcmd.Run(ctx, c, ifi, flag.Arg(0), target); err != nil {
// Context cancel means a signal was sent, so no need to log an error.
if err == context.Canceled {
os.Exit(1)
Expand Down Expand Up @@ -151,22 +139,24 @@ func findInterface(name string) (*net.Interface, error) {

const usage = `ndp: utility for working with the Neighbor Discovery Protocol.
By default, this tool will automatically bind to IPv6 link-local address of the first interface which is capable of using NDP.
To enable more convenient use without sudo on Linux, apply the CAP_NET_RAW capability:
$ sudo setcap cap_net_raw+ep ./ndp
Examples:
Listen for incoming NDP messages on interface eth0 to one of the interface's
global unicast addresses.
Listen for incoming NDP messages on the default interface.
$ sudo ndp -i eth0 -a global listen
$ sudo ndp -i eth0 -a 2001:db8::1 listen
$ ndp
Send router solicitations on interface eth0 from the interface's link-local
address until a router advertisement is received.
Send router solicitations on the default interface until a router advertisement is received.
$ sudo ndp -i eth0 -a linklocal rs
$ ndp rs
Send neighbor solicitations on interface eth0 to a neighbor's link-local
address until a neighbor advertisement is received.
Send neighbor solicitations on the default interface until a neighbor advertisement is received.
$ sudo ndp -i eth0 -a linklocal -t fe80::1 ns`
$ ndp -t fe80::1 ns`

func panicf(format string, a ...any) {
panic(fmt.Sprintf(format, a...))
Expand Down
86 changes: 2 additions & 84 deletions internal/ndpcmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,30 @@ import (
"net"
"net/netip"
"os"
"time"

"github.com/mdlayher/ndp"
"golang.org/x/sync/errgroup"
)

var (
errPrefixOp = errors.New("flag '-p' is only valid for router advertisement operation")
errTargetOp = errors.New("flag '-t' is only valid for neighbor solicitation operation")
)
var errTargetOp = errors.New("flag '-t' is only valid for neighbor solicitation operation")

// Run runs the ndp utility.
func Run(
ctx context.Context,
c *ndp.Conn,
ifi *net.Interface,
op string,
target, prefix netip.Addr,
target netip.Addr,
) error {
if op != "ns" && target.IsValid() {
return errTargetOp
}
if op != "ra" && prefix.IsValid() {
return errPrefixOp
}

switch op {
// listen is the default when no op is specified.
case "listen", "":
return listen(ctx, c)
case "ns":
return sendNS(ctx, c, ifi.HardwareAddr, target)
case "ra":
if !prefix.IsValid() || prefix == netip.IPv6Unspecified() {
return errors.New("flag '-p' is required for router advertisement operation")
}

return doRA(ctx, c, ifi.HardwareAddr, prefix)
case "rs":
return sendRS(ctx, c, ifi.HardwareAddr)
default:
Expand Down Expand Up @@ -115,74 +101,6 @@ func sendNS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr, target neti
return nil
}

func doRA(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr, prefix netip.Addr) error {
ll := log.New(os.Stderr, "ndp ra> ", 0)

ll.Printf("advertising prefix %s/64 for SLAAC", prefix)

// This tool is mostly meant for testing so hardcode a bunch of values.
m := &ndp.RouterAdvertisement{
CurrentHopLimit: 64,
RouterSelectionPreference: ndp.Medium,
RouterLifetime: 30 * time.Second,
Options: []ndp.Option{
&ndp.PrefixInformation{
PrefixLength: 64,
AutonomousAddressConfiguration: true,
ValidLifetime: 60 * time.Second,
PreferredLifetime: 30 * time.Second,
Prefix: prefix,
},
&ndp.LinkLayerAddress{
Direction: ndp.Source,
Addr: addr,
},
},
}

// Expect any router solicitation message.
check := func(m ndp.Message) bool {
_, ok := m.(*ndp.RouterSolicitation)
return ok
}

// Trigger an RA whenever an RS is received.
rsC := make(chan struct{})
recv := func(ll *log.Logger, msg ndp.Message, from netip.Addr) {
printMessage(ll, m, from)
rsC <- struct{}{}
}

// We are now a "router".
if err := c.JoinGroup(netip.MustParseAddr("ff02::2")); err != nil {
return fmt.Errorf("failed to join multicast group: %v", err)
}

var eg errgroup.Group
eg.Go(func() error {
// Send messages until cancelation or error.
for {
if err := c.WriteTo(m, nil, netip.IPv6LinkLocalAllNodes()); err != nil {
return fmt.Errorf("failed to send router advertisement: %v", err)
}

select {
case <-ctx.Done():
return nil
// Trigger RA at regular intervals or on demand.
case <-time.After(10 * time.Second):
case <-rsC:
}
}
})

if err := receiveLoop(ctx, c, ll, check, recv); err != nil {
return fmt.Errorf("failed to receive router solicitations: %v", err)
}

return eg.Wait()
}

func sendRS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr) error {
ll := log.New(os.Stderr, "ndp rs> ", 0)

Expand Down

0 comments on commit 9478dfb

Please sign in to comment.