Skip to content

Instantly share code, notes, and snippets.

@glyn
Last active August 29, 2015 14:13
Show Gist options
  • Save glyn/f2cca64a99268998c920 to your computer and use it in GitHub Desktop.
Save glyn/f2cca64a99268998c920 to your computer and use it in GitHub Desktop.
netbat prototype
               __  __          __ 
   ____  ___  / /_/ /_  ____ _/ /_
  / __ \/ _ \/ __/ __ \/ __ `/ __/
 / / / /  __/ /_/ /_/ / /_/ / /_  
/_/ /_/\___/\__/_.___/\__,_/\__/  

A utility for issuing ICMP requests.

Send ICMP echo and receive response:

$ sudo netbat echo www.example.com
2015/01/08 12:27:13 got reply from 93.184.216.34

Note: must be run as root.

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: &timestamp.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]))
}
}
package timestamp
import (
"fmt"
"golang.org/x/net/icmp"
)
type Timestamp struct {
ID int
Seq int
OriginTimestamp uint32
ReceiveTimestamp uint32
TransmitTimestamp uint32
}
const marshalledTimestampLen = 16
func (t *Timestamp) Len() int {
if t == nil {
return 0
}
return marshalledTimestampLen
}
func (t *Timestamp) Marshal(_ int) ([]byte, error) {
b := make([]byte, marshalledTimestampLen)
b[0], b[1] = byte(t.ID>>8), byte(t.ID)
b[2], b[3] = byte(t.Seq>>8), byte(t.Seq)
unparseInt := func(i uint32) (byte, byte, byte, byte) {
return byte(i >> 24), byte(i >> 16), byte(i >> 8), byte(i)
}
b[4], b[5], b[6], b[7] = unparseInt(t.OriginTimestamp)
b[8], b[9], b[10], b[11] = unparseInt(t.ReceiveTimestamp)
b[12], b[13], b[14], b[15] = unparseInt(t.TransmitTimestamp)
return b, nil
}
func ParseTimestamp(_ int, b []byte) (icmp.MessageBody, error) {
bodyLen := len(b)
if bodyLen != marshalledTimestampLen {
return nil, fmt.Errorf("timestamp body length %d not equal to 16", bodyLen)
}
p := &Timestamp{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])}
parseInt := func(start int) uint32 {
return uint32(b[start])<<24 | uint32(b[start+1])<<16 | uint32(b[start+2])<<8 | uint32(b[start+3])
}
p.OriginTimestamp = parseInt(4)
p.ReceiveTimestamp = parseInt(8)
p.TransmitTimestamp = parseInt(12)
return p, nil
}
package timestamp_test
import (
"github.com/cloudfoundry-incubator/garden-linux/integration/networking/netbat/timestamp"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Timestamp", func() {
var (
testTimestamp *timestamp.Timestamp
testMarshalledTimestamp []byte
)
BeforeEach(func() {
testTimestamp = &timestamp.Timestamp{
ID: 32,
Seq: 9,
OriginTimestamp: 42,
ReceiveTimestamp: 60,
TransmitTimestamp: 1729,
}
testMarshalledTimestamp = []byte{0, 32, 0, 9, 0, 0, 0, 42, 0, 0, 0, 60,
0, 0, 6, 193}
})
It("returns the correct length of a timestamp", func() {
Ω(testTimestamp.Len()).Should(Equal(16))
})
It("correctly marshalls a timestamp message", func() {
bb, err := testTimestamp.Marshal(0)
Ω(err).ShouldNot(HaveOccurred())
Ω(bb).Should(Equal(testMarshalledTimestamp))
})
It("correctly parses a timestamp message", func() {
ts, err := timestamp.ParseTimestamp(0, testMarshalledTimestamp)
Ω(err).ShouldNot(HaveOccurred())
Ω(ts).Should(Equal(testTimestamp))
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment