|
package main |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"log" |
|
"net" |
|
"os" |
|
"syscall" |
|
"time" |
|
|
|
"github.com/cloudfoundry-incubator/garden-linux/integration/networking/netbat/timestamp" |
|
"golang.org/x/net/icmp" |
|
"golang.org/x/net/internal/iana" |
|
"golang.org/x/net/ipv4" |
|
"golang.org/x/net/ipv6" |
|
) |
|
|
|
var seqNo int |
|
|
|
const readTimeout = 5 * time.Second |
|
|
|
func listen() *net.IPConn { |
|
c, err := net.ListenIP("ip4:icmp", nil) |
|
if err != nil { |
|
log.Fatal("listen: ", err) |
|
} |
|
return c |
|
} |
|
|
|
func send(c *net.IPConn, dest string, msg icmp.Message) { |
|
wb, err := msg.Marshal(nil) |
|
if err != nil { |
|
log.Fatal("Marshall: ", err) |
|
} |
|
|
|
exIP, err := net.ResolveIPAddr("ip4:icmp", dest) |
|
if err != nil { |
|
log.Fatal("ResolveIPAddr: ", err) |
|
} |
|
|
|
if _, err := c.WriteTo(wb, exIP); err != nil { |
|
log.Fatal("WriteTo: ", err) |
|
} |
|
} |
|
|
|
func receive(c *net.IPConn) (*icmp.Message, net.Addr, error) { |
|
rb := make([]byte, 1500) |
|
err := c.SetReadDeadline(time.Now().Add(readTimeout)) |
|
if err != nil { |
|
log.Fatal("SetReadDeadline: ", err) |
|
} |
|
n, peer, err := c.ReadFrom(rb) |
|
if err != nil { |
|
log.Printf("ReadFrom: %s", err) |
|
return nil, nil, err |
|
} |
|
rm, err := parseMessage(iana.ProtocolICMP, rb[:n]) |
|
if err != nil { |
|
log.Fatal("ParseMessage: ", err) |
|
} |
|
return rm, peer, nil |
|
} |
|
|
|
// TODO: intercept timestamp messages and delegate others to icmp.ParseMessage. |
|
func parseMessage(proto int, b []byte) (*Message, error) { |
|
if len(b) < 4 { |
|
return nil, errMessageTooShort |
|
} |
|
var err error |
|
m := &Message{Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} |
|
switch proto { |
|
case iana.ProtocolICMP: |
|
m.Type = ipv4.ICMPType(b[0]) |
|
case iana.ProtocolIPv6ICMP: |
|
m.Type = ipv6.ICMPType(b[0]) |
|
default: |
|
return nil, syscall.EINVAL |
|
} |
|
if fn, ok := parseFns[m.Type]; !ok { |
|
m.Body, err = parseDefaultMessageBody(proto, b[4:]) |
|
} else { |
|
m.Body, err = fn(proto, b[4:]) |
|
} |
|
if err != nil { |
|
return nil, err |
|
} |
|
return m, nil |
|
} |
|
|
|
func echo(dest string) error { |
|
c := listen() |
|
defer c.Close() |
|
|
|
seqNo++ |
|
|
|
wm := icmp.Message{ |
|
Type: ipv4.ICMPTypeEcho, Code: 0, |
|
Body: &icmp.Echo{ |
|
ID: os.Getpid() & 0xffff, Seq: seqNo, |
|
Data: []byte("netbat calling"), |
|
}, |
|
} |
|
|
|
send(c, dest, wm) |
|
|
|
rm, peer, err := receive(c) |
|
if err != nil { |
|
fmt.Printf("Receive failed with %s; want echo reply", err) |
|
return errors.New("echo reply not received") |
|
} |
|
|
|
switch rm.Type { |
|
case ipv4.ICMPTypeEchoReply: |
|
fmt.Printf("got reply from %v\n", peer) |
|
return nil |
|
default: |
|
fmt.Printf("got %+v; want echo reply\n", rm) |
|
return errors.New("echo reply not received") |
|
} |
|
} |
|
|
|
func tstamp(dest string) error { |
|
c := listen() |
|
defer c.Close() |
|
|
|
seqNo++ |
|
|
|
wm := icmp.Message{ |
|
Type: ipv4.ICMPTypeTimestamp, Code: 0, |
|
Body: ×tamp.Timestamp{ |
|
ID: os.Getpid() & 0xffff, Seq: seqNo, |
|
OriginTimestamp: 0, |
|
}, |
|
} |
|
|
|
send(c, dest, wm) |
|
|
|
rm, peer, err := receive(c) |
|
if err != nil { |
|
fmt.Printf("Receive failed with %s; want timestamp reply", err) |
|
return errors.New("timestamp reply not received") |
|
} |
|
|
|
switch rm.Type { |
|
case ipv4.ICMPTypeTimestampReply: |
|
log.Printf("got reply from %v", peer) |
|
return nil |
|
case ipv4.ICMPTypeTimestamp: |
|
body, err := rm.Body.(icmp.DefaultMessageBody) |
|
if err != nil { |
|
log.Fatal("body type assertion: ", err) |
|
} |
|
ts, err := timestamp.ParseTimestamp(0, body) |
|
if err != nil { |
|
log.Fatal("ParseTimestamp: ", err) |
|
} |
|
log.Printf("got timestamp from %v, body: %+v", peer, ts) |
|
return errors.New("timestamp reply not received") |
|
default: |
|
log.Printf("got %+v; want timestamp reply", rm) |
|
return errors.New("timestamp reply not received") |
|
} |
|
} |
|
|
|
func main() { |
|
switch os.Args[1] { |
|
case "echo": |
|
echo(os.Args[2]) |
|
case "timestamp": |
|
tstamp(os.Args[2]) |
|
default: |
|
panic(fmt.Sprintf("not a valid action: %s", os.Args[1])) |
|
} |
|
} |