Skip to content

Commit

Permalink
cmd/ndp: ndp listen and other WIP printing code
Browse files Browse the repository at this point in the history
  • Loading branch information
mdlayher committed Jan 10, 2018
1 parent d38e520 commit f69c669
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 10 deletions.
6 changes: 6 additions & 0 deletions cmd/ndp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ func main() {
const usage = `ndp: utility for working with the Neighbor Discovery Protocol.
Examples:
Listen for incoming NDP messages on interface eth0 to one of the interface's
global unicast addresses.
$ sudo ndp -i eth0 -a global listen
$ sudo ndp -i eth0 -a 2001:db8::1 listen
Send router solicitations on interface eth0 from the interface's link-local
address until a router advertisement is received.
Expand Down
65 changes: 55 additions & 10 deletions internal/ndpcmd/rs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ func sendRS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr) error {
return fmt.Errorf("failed to write router solicitation: %v", err)
}

ra, err := receiveRA(c)
ra, from, err := receiveRA(c)
if err == nil {
printRA(ll, ra)
fmt.Println()
printRA(ll, ra, from)
return nil
}

Expand All @@ -55,35 +56,35 @@ func sendRS(ctx context.Context, c *ndp.Conn, addr net.HardwareAddr) error {
}
}

func receiveRA(c *ndp.Conn) (*ndp.RouterAdvertisement, error) {
func receiveRA(c *ndp.Conn) (*ndp.RouterAdvertisement, net.IP, error) {
if err := c.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil {
return nil, err
return nil, nil, err
}

for {
msg, _, _, err := c.ReadFrom()
msg, _, from, err := c.ReadFrom()
if err != nil {
return nil, err
return nil, nil, err
}

ra, ok := msg.(*ndp.RouterAdvertisement)
if !ok {
continue
}

return ra, nil
return ra, from, nil
}
}

func printRA(ll *log.Logger, ra *ndp.RouterAdvertisement) {
func printRA(ll *log.Logger, ra *ndp.RouterAdvertisement, from net.IP) {
var opts string
for _, o := range ra.Options {
opts += fmt.Sprintf(" - %s\n", optStr(o))
}

fmt.Println()
ll.Printf(
raFormat,
from.String(),
ra.CurrentHopLimit,
ra.ManagedConfiguration,
ra.OtherConfiguration,
Expand All @@ -94,7 +95,7 @@ func printRA(ll *log.Logger, ra *ndp.RouterAdvertisement) {
)
}

const raFormat = `router advertisement:
const raFormat = `router advertisement from: %s:
- hop limit: %d
- managed: %t
- other: %t
Expand All @@ -113,9 +114,53 @@ func optStr(o ndp.Option) string {
}

return fmt.Sprintf("%s link-layer address: %s", dir, o.Addr.String())
case *ndp.MTU:
return fmt.Sprintf("MTU: %d", *o)
case *ndp.PrefixInformation:
flags := "["
if o.OnLink {
flags += "O"
}
if o.AutonomousAddressConfiguration {
flags += "A"
}
flags += "]"

return fmt.Sprintf("prefix information: %s/%d, flags: %s, valid: %s, preferred: %s",
o.Prefix.String(),
o.PrefixLength,
flags,
o.ValidLifetime,
o.PreferredLifetime,
)
case *ndp.RawOption:
return fmt.Sprintf("type: %03d, value: %v", o.Type, o.Value)
default:
panic(fmt.Sprintf("unrecognized option: %v", o))
}
}

func printNA(ll *log.Logger, na *ndp.NeighborAdvertisement, from net.IP) {
var opts string
for _, o := range na.Options {
opts += fmt.Sprintf(" - %s\n", optStr(o))
}

ll.Printf(
naFormat,
from.String(),
na.Router,
na.Solicited,
na.Override,
na.TargetAddress.String(),
opts,
)
}

const naFormat = `neighbor advertisement from %s:
- router: %t
- solicited: %t
- override: %t
- target address: %s
- options:
%s`
50 changes: 50 additions & 0 deletions internal/ndpcmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,67 @@ package ndpcmd
import (
"context"
"fmt"
"log"
"net"
"os"
"time"

"github.com/mdlayher/ndp"
)

// Run runs the ndp utility.
func Run(ctx context.Context, c *ndp.Conn, ifi *net.Interface, op string) error {
switch op {
case "listen":
return listen(ctx, c)
case "rs":
return sendRS(ctx, c, ifi.HardwareAddr)
default:
return fmt.Errorf("unrecognized operation: %q", op)
}
}

func listen(ctx context.Context, c *ndp.Conn) error {
ll := log.New(os.Stderr, "ndp listen> ", 0)
ll.Println("listening for messages")

var recv int
for {
if err := c.SetReadDeadline(time.Now().Add(1 * time.Second)); err != nil {
return err
}

m, _, from, err := c.ReadFrom()
if err == nil {
recv++
printMessage(ll, m, from)
continue
}

// Was the context canceled already?
select {
case <-ctx.Done():
ll.Printf("received %d message(s)", recv)
return ctx.Err()
default:
}

// Was the error caused by a read timeout, and should the loop continue?
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
continue
}

return fmt.Errorf("failed to read message: %v", err)
}
}

func printMessage(ll *log.Logger, m ndp.Message, from net.IP) {
switch m := m.(type) {
case *ndp.NeighborAdvertisement:
printNA(ll, m, from)
case *ndp.RouterAdvertisement:
printRA(ll, m, from)
default:
ll.Printf("%s %#v", from, m)
}
}

0 comments on commit f69c669

Please sign in to comment.