Re-sync to master.

shw_dev
Stephen Watt 7 years ago
parent e1f48ced94
commit a8f61a2d4e

1
.gitignore vendored

@ -1,3 +1,4 @@
*.iml *.iml
.idea/ .idea/
*~ *~
*.swp

49
Godeps/Godeps.json generated

@ -1,10 +1,7 @@
{ {
"ImportPath": "github.com/subgraph/fw-daemon", "ImportPath": "github.com/subgraph/fw-daemon",
"GoVersion": "go1.7", "GoVersion": "go1.7",
"GodepVersion": "v74", "GodepVersion": "v79",
"Packages": [
"./..."
],
"Deps": [ "Deps": [
{ {
"ImportPath": "github.com/godbus/dbus", "ImportPath": "github.com/godbus/dbus",
@ -17,29 +14,14 @@
"Rev": "32c6cc29c14570de4cf6d7e7737d68fb2d01ad15" "Rev": "32c6cc29c14570de4cf6d7e7737d68fb2d01ad15"
}, },
{ {
"ImportPath": "github.com/gotk3/gotk3/cairo", "ImportPath": "github.com/google/gopacket",
"Comment": "GOTK3_0_2_0-467-gefaac8f", "Comment": "v1.1.12-75-g8af772c",
"Rev": "efaac8f907aac2f965675d64fd163097db109f95" "Rev": "8af772c0bcc826f671fd7c133917fec9686d720d"
}, },
{ {
"ImportPath": "github.com/gotk3/gotk3/gdk", "ImportPath": "github.com/google/gopacket/layers",
"Comment": "GOTK3_0_2_0-467-gefaac8f", "Comment": "v1.1.12-75-g8af772c",
"Rev": "efaac8f907aac2f965675d64fd163097db109f95" "Rev": "8af772c0bcc826f671fd7c133917fec9686d720d"
},
{
"ImportPath": "github.com/gotk3/gotk3/glib",
"Comment": "GOTK3_0_2_0-467-gefaac8f",
"Rev": "efaac8f907aac2f965675d64fd163097db109f95"
},
{
"ImportPath": "github.com/gotk3/gotk3/gtk",
"Comment": "GOTK3_0_2_0-467-gefaac8f",
"Rev": "efaac8f907aac2f965675d64fd163097db109f95"
},
{
"ImportPath": "github.com/gotk3/gotk3/pango",
"Comment": "GOTK3_0_2_0-467-gefaac8f",
"Rev": "efaac8f907aac2f965675d64fd163097db109f95"
}, },
{ {
"ImportPath": "github.com/naoina/go-stringutil", "ImportPath": "github.com/naoina/go-stringutil",
@ -61,13 +43,26 @@
"Comment": "v1-7-g970db52", "Comment": "v1-7-g970db52",
"Rev": "970db520ece77730c7e4724c61121037378659d9" "Rev": "970db520ece77730c7e4724c61121037378659d9"
}, },
{
"ImportPath": "github.com/subgraph/go-nfnetlink",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47"
},
{
"ImportPath": "github.com/subgraph/go-nfnetlink/nfqueue",
"Rev": "34abd96bd88d0fbc1a5bbba9fff1bdc2c0448f47"
},
{ {
"ImportPath": "github.com/subgraph/go-procsnitch", "ImportPath": "github.com/subgraph/go-procsnitch",
"Rev": "fbc2965632eec2dcea9b8d630b081b10980d325d" "Rev": "fbc2965632eec2dcea9b8d630b081b10980d325d"
}, },
{ {
"ImportPath": "golang.org/x/net/proxy", "ImportPath": "github.com/subgraph/oz/ipc",
"Rev": "ef2e00e88c5e0a3569f0bb9df697e9cbc6215fea" "Comment": "0.2.5",
"Rev": "69e556d2d33a76f19546471d813a21f7b7fa4181"
},
{
"ImportPath": "github.com/subgraph/ozipc",
"Rev": "2cbf2ba8878e9dea012edb021f3b4836f7c93202"
} }
] ]
} }

@ -1,3 +1,15 @@
*** Please also refer to the notes found in fw-prompt/README.txt: you will need to copy both files in sources/etc/dbus-1/system.d/ into /etc/dbus-1/system.d/
Build complications:
You may need to run the following first, esp. on Ubuntu 16.04:
go install -tags gtk_3_18 github.com/gotk3/gotk3/gtk
and build with the following:
go build -v -tags gtk_3_18 -gcflags "-N -l" fw-prompt
Otherwise standard go build and go install procedures should suffice.
Before running fw-daemon, make sure to export: GODEBUG=cgocheck=0 Before running fw-daemon, make sure to export: GODEBUG=cgocheck=0
Also, here's a default fw-daemon-socks.json config file: Also, here's a default fw-daemon-socks.json config file:

@ -27,6 +27,8 @@ Read more in the [Subgraph OS Handbook](https://subgraph.com/sgos-handbook/sgos_
``` ```
# First install the build dependencies
apt install debhelper dh-golang dh-systemd golang-go libcairo2-dev libglib2.0-dev libgtk-3-dev libnetfilter-queue-dev
# To build the Debian package: # To build the Debian package:
git clone -b debian https://github.com/subgraph/fw-daemon.git git clone -b debian https://github.com/subgraph/fw-daemon.git
cd fw-daemon cd fw-daemon
@ -38,3 +40,14 @@ gbp buildpackage -us -uc --git-upstream-tree=master
dpkg -i /tmp/build-area/fw-daemon{,-gnome}-*.deb dpkg -i /tmp/build-area/fw-daemon{,-gnome}-*.deb
## Refresh your gnome-shell session 'alt-r' type 'r' hit enter. ## Refresh your gnome-shell session 'alt-r' type 'r' hit enter.
``` ```
You will be left to install the matching iptables rules. While this may vary depending on your environment, pre-existing ruleset
and preferred mechanism; something like the following needs to be added:
```
iptables -t mangle -A OUTPUT -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0 --queue-bypass
iptables -A INPUT -p udp -m udp --sport 53 -j NFQUEUE --queue-num 0 --queue-bypass
iptables -A OUTPUT -p tcp -m mark --mark 0x1 -j LOG
iptables -A OUTPUT -p tcp -m mark --mark 0x1 -j REJECT --reject-with icmp-port-unreachable
```

@ -1,12 +1,12 @@
package main package main
import ( import (
"fmt"
"flag" "flag"
"strconv" "fmt"
"io" "io"
"log" "log"
"net" "net"
"strconv"
) )
const ReceiverSocketPath = "/var/run/fw-daemon/fwoz.sock" const ReceiverSocketPath = "/var/run/fw-daemon/fwoz.sock"
@ -19,8 +19,8 @@ func reader(r io.Reader) {
if err != nil { if err != nil {
return return
} }
fmt.Println(string(buf[0:n])) fmt.Println(string(buf[0:n]))
} }
} }
func main() { func main() {
@ -87,4 +87,3 @@ func main() {
} }
} }

@ -4,13 +4,12 @@ import (
"errors" "errors"
"github.com/godbus/dbus" "github.com/godbus/dbus"
"log" "log"
// "github.com/gotk3/gotk3/glib" // "github.com/gotk3/gotk3/glib"
) )
type dbusServer struct { type dbusServer struct {
conn *dbus.Conn conn *dbus.Conn
run bool run bool
} }
type promptData struct { type promptData struct {
@ -34,7 +33,6 @@ type promptData struct {
Action int Action int
} }
func newDbusServer() (*dbusServer, error) { func newDbusServer() (*dbusServer, error) {
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()
@ -65,9 +63,9 @@ func newDbusServer() (*dbusServer, error) {
} }
func (ds *dbusServer) RequestPrompt(application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string, func (ds *dbusServer) RequestPrompt(application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string,
optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) { is_socks bool, optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) {
log.Printf("request prompt: app = %s, icon = %s, path = %s, address = %s, action = %v\n", application, icon, path, address, action) log.Printf("request prompt: app = %s, icon = %s, path = %s, address = %s, is_socks = %v, action = %v\n", application, icon, path, address, is_socks, action)
decision := addRequest(nil, path, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, optstring, sandbox) decision := addRequest(nil, path, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, is_socks, optstring, sandbox)
log.Print("Waiting on decision...") log.Print("Waiting on decision...")
decision.Cond.L.Lock() decision.Cond.L.Lock()
for !decision.Ready { for !decision.Ready {
@ -75,6 +73,6 @@ func (ds *dbusServer) RequestPrompt(application, icon, path, address string, por
} }
log.Print("Decision returned: ", decision.Rule) log.Print("Decision returned: ", decision.Rule)
decision.Cond.L.Unlock() decision.Cond.L.Unlock()
// glib.IdleAdd(func, data) // glib.IdleAdd(func, data)
return int32(decision.Scope), decision.Rule, nil return int32(decision.Scope), decision.Rule, nil
} }

@ -1,37 +1,36 @@
package main package main
import ( import (
"github.com/gotk3/gotk3/gtk" "encoding/json"
"errors"
"fmt"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"io/ioutil"
"log" "log"
"fmt"
"strings"
"strconv"
"os" "os"
"io/ioutil"
"encoding/json"
"os/user" "os/user"
"strconv"
"strings"
"sync" "sync"
"errors" "time"
"github.com/subgraph/fw-daemon/sgfw" "github.com/subgraph/fw-daemon/sgfw"
) )
type fpPreferences struct { type fpPreferences struct {
Winheight uint Winheight uint
Winwidth uint Winwidth uint
Wintop uint Wintop uint
Winleft uint Winleft uint
} }
type decisionWaiter struct { type decisionWaiter struct {
Cond *sync.Cond Cond *sync.Cond
Lock sync.Locker Lock sync.Locker
Ready bool Ready bool
Scope int Scope int
Rule string Rule string
} }
type ruleColumns struct { type ruleColumns struct {
@ -46,10 +45,9 @@ type ruleColumns struct {
Uname string Uname string
Gname string Gname string
Origin string Origin string
Scope int Scope int
} }
var userPrefs fpPreferences var userPrefs fpPreferences
var mainWin *gtk.Window var mainWin *gtk.Window
var Notebook *gtk.Notebook var Notebook *gtk.Notebook
@ -63,7 +61,6 @@ var radioOnce, radioProcess, radioParent, radioSession, radioPermanent *gtk.Radi
var btnApprove, btnDeny, btnIgnore *gtk.Button var btnApprove, btnDeny, btnIgnore *gtk.Button
var chkUser, chkGroup *gtk.CheckButton var chkUser, chkGroup *gtk.CheckButton
func dumpDecisions() { func dumpDecisions() {
fmt.Println("XXX Total of decisions pending: ", len(decisionWaiters)) fmt.Println("XXX Total of decisions pending: ", len(decisionWaiters))
for i := 0; i < len(decisionWaiters); i++ { for i := 0; i < len(decisionWaiters); i++ {
@ -80,7 +77,7 @@ func addDecision() *decisionWaiter {
func promptInfo(msg string) { func promptInfo(msg string) {
dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Displaying full log info:") dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Displaying full log info:")
// dialog.SetDefaultGeometry(500, 200) // dialog.SetDefaultGeometry(500, 200)
tv, err := gtk.TextViewNew() tv, err := gtk.TextViewNew()
@ -105,7 +102,7 @@ func promptInfo(msg string) {
} }
scrollbox.Add(tv) scrollbox.Add(tv)
scrollbox.SetSizeRequest(600, 100) scrollbox.SetSizeRequest(500, 100)
box, err := dialog.GetContentArea() box, err := dialog.GetContentArea()
@ -117,7 +114,7 @@ func promptInfo(msg string) {
dialog.ShowAll() dialog.ShowAll()
dialog.Run() dialog.Run()
dialog.Destroy() dialog.Destroy()
//self.set_default_size(150, 100) //self.set_default_size(150, 100)
} }
func promptChoice(msg string) int { func promptChoice(msg string) int {
@ -137,7 +134,7 @@ func getConfigPath() string {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n"); fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n")
return "" return ""
} }
@ -149,7 +146,7 @@ func savePreferences() bool {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n"); fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file:", err, "\n")
return false return false
} }
@ -176,7 +173,7 @@ func loadPreferences() bool {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file: %v", err, "\n"); fmt.Fprintf(os.Stderr, "Error: could not determine location of user preferences file: %v", err, "\n")
return false return false
} }
@ -192,7 +189,7 @@ func loadPreferences() bool {
err = json.Unmarshal(jfile, &userPrefs) err = json.Unmarshal(jfile, &userPrefs)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error: could not load preferences data from file: %v", err, "\n") fmt.Fprintf(os.Stderr, "Error: could not load preferences data from file: %v", err, "\n")
return false return false
} }
@ -201,23 +198,23 @@ func loadPreferences() bool {
} }
func get_hbox() *gtk.Box { func get_hbox() *gtk.Box {
hbox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0) hbox, err := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 0)
if err != nil { if err != nil {
log.Fatal("Unable to create horizontal box:", err) log.Fatal("Unable to create horizontal box:", err)
} }
return hbox return hbox
} }
func get_vbox() *gtk.Box { func get_vbox() *gtk.Box {
vbox, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0) vbox, err := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
if err != nil { if err != nil {
log.Fatal("Unable to create vertical box:", err) log.Fatal("Unable to create vertical box:", err)
} }
return vbox return vbox
} }
func get_checkbox(text string, activated bool) *gtk.CheckButton { func get_checkbox(text string, activated bool) *gtk.CheckButton {
@ -319,13 +316,42 @@ func createListStore(general bool) *gtk.ListStore {
return listStore return listStore
} }
func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin, optstring string, sandbox string) *decisionWaiter { func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin string, is_socks bool, optstring string, sandbox string) *decisionWaiter {
if listStore == nil { if listStore == nil {
listStore = globalLS listStore = globalLS
waitTimes := []int{1, 2, 5, 10}
if listStore == nil {
log.Print("SGFW prompter was not ready to receive firewall request... waiting")
}
for _, wtime := range waitTimes {
time.Sleep(time.Duration(wtime) * time.Second)
listStore = globalLS
if listStore != nil {
break
}
log.Print("SGFW prompter is still waiting...")
}
}
if listStore == nil {
log.Fatal("SGFW prompter GUI failed to load for unknown reasons")
} }
iter := listStore.Append() iter := listStore.Append()
if is_socks {
if (optstring != "") && (strings.Index(optstring, "SOCKS") == -1) {
optstring = "SOCKS5 / " + optstring
} else if optstring == "" {
optstring = "SOCKS5"
}
}
colVals := make([]interface{}, 11) colVals := make([]interface{}, 11)
colVals[0] = 1 colVals[0] = 1
colVals[1] = path colVals[1] = path
@ -416,7 +442,7 @@ func setup_settings() {
fmt.Println("CLICKED") fmt.Println("CLICKED")
if err != nil { if err != nil {
promptError("Unexpected error saving log file info: "+err.Error()) promptError("Unexpected error saving log file info: " + err.Error())
return return
} }
@ -508,7 +534,6 @@ func toggleValidRuleState() {
} }
} }
btnApprove.SetSensitive(ok) btnApprove.SetSensitive(ok)
btnDeny.SetSensitive(ok) btnDeny.SetSensitive(ok)
btnIgnore.SetSensitive(ok) btnIgnore.SetSensitive(ok)
@ -554,8 +579,8 @@ func createCurrentRule() (ruleColumns, error) {
rule.UID, rule.GID = 0, 0 rule.UID, rule.GID = 0, 0
rule.Uname, rule.Gname = "", "" rule.Uname, rule.Gname = "", ""
/* Pid int /* Pid int
Origin string */ Origin string */
return rule, nil return rule, nil
} }
@ -690,7 +715,7 @@ func getSelectedRule() (ruleColumns, int, error) {
func main() { func main() {
decisionWaiters = make([]*decisionWaiter, 0) decisionWaiters = make([]*decisionWaiter, 0)
_, err := newDbusServer(); _, err := newDbusServer()
if err != nil { if err != nil {
log.Fatal("Error:", err) log.Fatal("Error:", err)
return return
@ -711,7 +736,7 @@ func main() {
mainWin.Connect("destroy", func() { mainWin.Connect("destroy", func() {
fmt.Println("Shutting down...") fmt.Println("Shutting down...")
savePreferences() savePreferences()
gtk.MainQuit() gtk.MainQuit()
}) })
mainWin.Connect("configure-event", func() { mainWin.Connect("configure-event", func() {
@ -749,9 +774,6 @@ func main() {
log.Fatal("Unable to create scrolled window:", err) log.Fatal("Unable to create scrolled window:", err)
} }
scrollbox.Add(box)
tv, err := gtk.TreeViewNew() tv, err := gtk.TreeViewNew()
if err != nil { if err != nil {
@ -845,7 +867,9 @@ func main() {
box.PackStart(bb, false, false, 5) box.PackStart(bb, false, false, 5)
box.PackStart(editbox, false, false, 5) box.PackStart(editbox, false, false, 5)
box.PackStart(tv, false, true, 5) scrollbox.Add(tv)
// box.PackStart(tv, false, true, 5)
box.PackStart(scrollbox, false, true, 5)
tv.AppendColumn(createColumn("#", 0)) tv.AppendColumn(createColumn("#", 0))
tv.AppendColumn(createColumn("Path", 1)) tv.AppendColumn(createColumn("Path", 1))
@ -867,13 +891,13 @@ func main() {
btnApprove.Connect("clicked", func() { btnApprove.Connect("clicked", func() {
rule, idx, err := getSelectedRule() rule, idx, err := getSelectedRule()
if err != nil { if err != nil {
promptError("Error occurred processing request: "+err.Error()) promptError("Error occurred processing request: " + err.Error())
return return
} }
rule, err = createCurrentRule() rule, err = createCurrentRule()
if err != nil { if err != nil {
promptError("Error occurred constructing new rule: "+err.Error()) promptError("Error occurred constructing new rule: " + err.Error())
return return
} }
@ -886,20 +910,20 @@ func main() {
if err == nil { if err == nil {
clearEditor() clearEditor()
} else { } else {
promptError("Error setting new rule: "+err.Error()) promptError("Error setting new rule: " + err.Error())
} }
}) })
btnDeny.Connect("clicked", func() { btnDeny.Connect("clicked", func() {
rule, idx, err := getSelectedRule() rule, idx, err := getSelectedRule()
if err != nil { if err != nil {
promptError("Error occurred processing request: "+err.Error()) promptError("Error occurred processing request: " + err.Error())
return return
} }
rule, err = createCurrentRule() rule, err = createCurrentRule()
if err != nil { if err != nil {
promptError("Error occurred constructing new rule: "+err.Error()) promptError("Error occurred constructing new rule: " + err.Error())
return return
} }
@ -912,14 +936,14 @@ func main() {
if err == nil { if err == nil {
clearEditor() clearEditor()
} else { } else {
promptError("Error setting new rule: "+err.Error()) promptError("Error setting new rule: " + err.Error())
} }
}) })
btnIgnore.Connect("clicked", func() { btnIgnore.Connect("clicked", func() {
_, idx, err := getSelectedRule() _, idx, err := getSelectedRule()
if err != nil { if err != nil {
promptError("Error occurred processing request: "+err.Error()) promptError("Error occurred processing request: " + err.Error())
return return
} }
@ -929,15 +953,15 @@ func main() {
if err == nil { if err == nil {
clearEditor() clearEditor()
} else { } else {
promptError("Error setting new rule: "+err.Error()) promptError("Error setting new rule: " + err.Error())
} }
}) })
// tv.SetActivateOnSingleClick(true) // tv.SetActivateOnSingleClick(true)
tv.Connect("row-activated", func() { tv.Connect("row-activated", func() {
seldata, _, err := getSelectedRule() seldata, _, err := getSelectedRule()
if err != nil { if err != nil {
promptError("Unexpected error reading selected rule: "+err.Error()) promptError("Unexpected error reading selected rule: " + err.Error())
return return
} }
@ -980,14 +1004,14 @@ func main() {
return return
}) })
scrollbox.SetSizeRequest(600, 400) scrollbox.SetSizeRequest(600, 400)
Notebook.AppendPage(scrollbox, nbLabel) // Notebook.AppendPage(scrollbox, nbLabel)
// setup_settings() Notebook.AppendPage(box, nbLabel)
// setup_settings()
mainWin.Add(Notebook) mainWin.Add(Notebook)
if userPrefs.Winheight > 0 && userPrefs.Winwidth > 0 { if userPrefs.Winheight > 0 && userPrefs.Winwidth > 0 {
// fmt.Printf("height was %d, width was %d\n", userPrefs.Winheight, userPrefs.Winwidth) // fmt.Printf("height was %d, width was %d\n", userPrefs.Winheight, userPrefs.Winwidth)
mainWin.Resize(int(userPrefs.Winwidth), int(userPrefs.Winheight)) mainWin.Resize(int(userPrefs.Winwidth), int(userPrefs.Winheight))
} else { } else {
mainWin.SetDefaultSize(850, 450) mainWin.SetDefaultSize(850, 450)
@ -998,6 +1022,6 @@ func main() {
} }
mainWin.ShowAll() mainWin.ShowAll()
// mainWin.SetKeepAbove(true) // mainWin.SetKeepAbove(true)
gtk.Main() gtk.Main()
} }

@ -6,6 +6,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk" "github.com/gotk3/gotk3/gtk"
@ -35,6 +36,19 @@ func builderForDefinition(uiName string) *gtk.Builder {
template := getDefinitionWithFileFallback(uiName) template := getDefinitionWithFileFallback(uiName)
maj := gtk.GetMajorVersion()
min := gtk.GetMinorVersion()
if (maj == 3) && (min < 20) {
fmt.Fprintf(os.Stderr,
"Attempting runtime work-around for older versions of libgtk-3...\n")
dep_re := regexp.MustCompile(`<\s?property\s+name\s?=\s?"icon_size"\s?>.+<\s?/property\s?>`)
template = dep_re.ReplaceAllString(template, ``)
dep_re2 := regexp.MustCompile(`version\s?=\s?"3.20"`)
template = dep_re2.ReplaceAllString(template, `version="3.18"`)
}
builder, err := gtk.BuilderNew() builder, err := gtk.BuilderNew()
if err != nil { if err != nil {
//We cant recover from this //We cant recover from this

@ -3,12 +3,11 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/subgraph/fw-daemon/sgfw"
"github.com/godbus/dbus" "github.com/godbus/dbus"
"github.com/gotk3/gotk3/glib" "github.com/gotk3/gotk3/glib"
"github.com/subgraph/fw-daemon/sgfw"
) )
type dbusObject struct { type dbusObject struct {
dbus.BusObject dbus.BusObject
} }
@ -19,7 +18,7 @@ type dbusObjectP struct {
type dbusServer struct { type dbusServer struct {
conn *dbus.Conn conn *dbus.Conn
run bool run bool
} }
func newDbusObject() (*dbusObject, error) { func newDbusObject() (*dbusObject, error) {
@ -114,5 +113,5 @@ func (ds *dbusServer) Alert(data string) *dbus.Error {
} }
func (ob *dbusObjectP) alertRule(data string) { func (ob *dbusObjectP) alertRule(data string) {
ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data) ob.Call("com.subgraph.fwprompt.EventNotifier.Alert", 0, data)
} }

@ -98,6 +98,7 @@
<property name="active">0</property> <property name="active">0</property>
<items> <items>
<item id="allow" translatable="yes">Allow</item> <item id="allow" translatable="yes">Allow</item>
<item id="allow_tls" translatable="yes">Allow TLS Only</item>
<item id="deny" translatable="yes">Deny</item> <item id="deny" translatable="yes">Deny</item>
</items> </items>
</object> </object>
@ -162,7 +163,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
<property name="width">2</property> <property name="width">2</property>
</packing> </packing>
</child> </child>
@ -184,6 +185,34 @@
<property name="top_attach">1</property> <property name="top_attach">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="sandbox_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Sandbox:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

@ -108,6 +108,7 @@ func (*defRuleEdit) String() string {
<property name="active">0</property> <property name="active">0</property>
<items> <items>
<item id="allow" translatable="yes">Allow</item> <item id="allow" translatable="yes">Allow</item>
<item id="allow_tls" translatable="yes">Allow TLS Only</item>
<item id="deny" translatable="yes">Deny</item> <item id="deny" translatable="yes">Deny</item>
</items> </items>
</object> </object>
@ -172,7 +173,7 @@ func (*defRuleEdit) String() string {
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
<property name="width">2</property> <property name="width">2</property>
</packing> </packing>
</child> </child>
@ -194,6 +195,34 @@ func (*defRuleEdit) String() string {
<property name="top_attach">1</property> <property name="top_attach">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="sandbox_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">False</property>
<property name="label" translatable="yes">Sandbox:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sandbox_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

@ -108,7 +108,7 @@ func repopulateWin() {
rlSystem.loadRules(sgfw.RULE_MODE_SYSTEM) rlSystem.loadRules(sgfw.RULE_MODE_SYSTEM)
loadConfig(win, fwsbuilder, dbus) loadConfig(win, fwsbuilder, dbus)
// app.AddWindow(win) // app.AddWindow(win)
win.ShowAll() win.ShowAll()
} }
@ -179,11 +179,11 @@ func main() {
} }
app.Connect("activate", activate) app.Connect("activate", activate)
_, err = newDbusServer(); _, err = newDbusServer()
if err != nil { if err != nil {
panic(fmt.Sprintf("Error initializing Dbus server: %v", err)) panic(fmt.Sprintf("Error initializing Dbus server: %v", err))
} }
app.Run(os.Args) app.Run(os.Args)
} }

@ -18,13 +18,15 @@ const (
) )
type ruleEdit struct { type ruleEdit struct {
row *ruleRow row *ruleRow
dialog *gtk.Dialog dialog *gtk.Dialog
pathLabel *gtk.Label pathLabel *gtk.Label
verbCombo *gtk.ComboBoxText sandboxLabel *gtk.Label
hostEntry *gtk.Entry sandboxTitle *gtk.Label
portEntry *gtk.Entry verbCombo *gtk.ComboBoxText
ok *gtk.Button hostEntry *gtk.Entry
portEntry *gtk.Entry
ok *gtk.Button
} }
func newRuleEdit(rr *ruleRow, saveasnew bool) *ruleEdit { func newRuleEdit(rr *ruleRow, saveasnew bool) *ruleEdit {
@ -33,6 +35,8 @@ func newRuleEdit(rr *ruleRow, saveasnew bool) *ruleEdit {
b.getItems( b.getItems(
"dialog", &redit.dialog, "dialog", &redit.dialog,
"path_label", &redit.pathLabel, "path_label", &redit.pathLabel,
"sandbox_label", &redit.sandboxLabel,
"sandbox_title", &redit.sandboxTitle,
"verb_combo", &redit.verbCombo, "verb_combo", &redit.verbCombo,
"host_entry", &redit.hostEntry, "host_entry", &redit.hostEntry,
"port_entry", &redit.portEntry, "port_entry", &redit.portEntry,
@ -54,9 +58,17 @@ func (re *ruleEdit) updateDialogFields() {
re.pathLabel.SetText(r.Path) re.pathLabel.SetText(r.Path)
if sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW { if sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW {
re.verbCombo.SetActiveID("allow") re.verbCombo.SetActiveID("allow")
} else if sgfw.RuleAction(r.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
re.verbCombo.SetActiveID("allow_tls")
} else { } else {
re.verbCombo.SetActiveID("deny") re.verbCombo.SetActiveID("deny")
} }
if r.Sandbox != "" {
re.sandboxLabel.SetText(r.Sandbox)
} else {
re.sandboxLabel.SetVisible(false)
re.sandboxTitle.SetVisible(false)
}
target := strings.Split(r.Target, ":") target := strings.Split(r.Target, ":")
if len(target) != 2 { if len(target) != 2 {
return return
@ -67,7 +79,7 @@ func (re *ruleEdit) updateDialogFields() {
func (re *ruleEdit) validateFields() bool { func (re *ruleEdit) validateFields() bool {
id := re.verbCombo.GetActiveID() id := re.verbCombo.GetActiveID()
if id != "allow" && id != "deny" { if id != "allow" && id != "allow_tls" && id != "deny" {
return false return false
} }
host, _ := re.hostEntry.GetText() host, _ := re.hostEntry.GetText()
@ -121,6 +133,8 @@ func (re *ruleEdit) updateRow() {
switch re.verbCombo.GetActiveID() { switch re.verbCombo.GetActiveID() {
case "allow": case "allow":
r.Verb = uint16(sgfw.RULE_ACTION_ALLOW) r.Verb = uint16(sgfw.RULE_ACTION_ALLOW)
case "allow_tls":
r.Verb = uint16(sgfw.RULE_ACTION_ALLOW_TLSONLY)
case "deny": case "deny":
r.Verb = uint16(sgfw.RULE_ACTION_DENY) r.Verb = uint16(sgfw.RULE_ACTION_DENY)
} }

@ -3,8 +3,8 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"strconv" "strconv"
"strings"
"github.com/subgraph/fw-daemon/sgfw" "github.com/subgraph/fw-daemon/sgfw"
@ -122,7 +122,7 @@ func (rr *ruleRow) update() {
} }
rr.gtkLabelApp.SetTooltipText(rr.rule.Path) rr.gtkLabelApp.SetTooltipText(rr.rule.Path)
rr.gtkLabelVerb.SetText(getVerbText(rr.rule)) rr.gtkLabelVerb.SetText(getVerbText(rr.rule))
if (rr.rule.Proto == "tcp") { if rr.rule.Proto == "tcp" {
rr.gtkLabelOrigin.SetText(rr.rule.Origin) rr.gtkLabelOrigin.SetText(rr.rule.Origin)
} else { } else {
rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")") rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")")
@ -135,6 +135,9 @@ func getVerbText(rule *sgfw.DbusRule) string {
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW { if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW {
return sgfw.RuleActionString[sgfw.RULE_ACTION_ALLOW] + ":" return sgfw.RuleActionString[sgfw.RULE_ACTION_ALLOW] + ":"
} }
if sgfw.RuleAction(rule.Verb) == sgfw.RULE_ACTION_ALLOW_TLSONLY {
return sgfw.RuleActionString[sgfw.RULE_ACTION_ALLOW_TLSONLY] + ":"
}
return sgfw.RuleActionString[sgfw.RULE_ACTION_DENY] + ":" return sgfw.RuleActionString[sgfw.RULE_ACTION_DENY] + ":"
} }
@ -180,11 +183,24 @@ func (rr *ruleRow) onEdit() {
} }
func (rr *ruleRow) onDelete() { func (rr *ruleRow) onDelete() {
body := fmt.Sprintf(`Are you sure you want to delete this rule: var body string
if rr.rule.Sandbox != "" {
ss := `Are you sure you want to delete this rule:
<b>Path:</b> %s <b>Path:</b> %s
<b>Rule:</b> %s %s`, rr.rule.Path, getVerbText(rr.rule), getTargetText(rr.rule)) <b>Sandbox:</b> %s
<b>Rule:</b> %s %s`
body = fmt.Sprintf(ss, rr.rule.Path, rr.rule.Sandbox, getVerbText(rr.rule), getTargetText(rr.rule))
} else {
ss := `Are you sure you want to delete this rule:
<b>Path:</b> %s
<b>Rule:</b> %s %s`
body = fmt.Sprintf(ss, rr.rule.Path, getVerbText(rr.rule), getTargetText(rr.rule))
}
d := gtk.MessageDialogNewWithMarkup( d := gtk.MessageDialogNewWithMarkup(
rr.rl.win, rr.rl.win,
gtk.DIALOG_DESTROY_WITH_PARENT, gtk.DIALOG_DESTROY_WITH_PARENT,

@ -458,7 +458,7 @@ const PromptDialog = new Lang.Class({
Name: 'PromptDialog', Name: 'PromptDialog',
Extends: ModalDialog.ModalDialog, Extends: ModalDialog.ModalDialog,
_init: function(invocation, pid_known, sandboxed) { _init: function(invocation, pid_known, sandboxed, tlsguard) {
this.parent({ styleClass: 'fw-prompt-dialog' }); this.parent({ styleClass: 'fw-prompt-dialog' });
this._invocation = invocation; this._invocation = invocation;
this.header = new PromptDialogHeader(); this.header = new PromptDialogHeader();
@ -471,7 +471,7 @@ const PromptDialog = new Lang.Class({
this.info = new DetailSection(sandboxed); this.info = new DetailSection(sandboxed);
box.add_child(this.info.actor); box.add_child(this.info.actor);
this.optionList = new OptionList(pid_known, sandboxed); this.optionList = new OptionList(pid_known, tlsguard);
box.add_child(this.optionList.actor); box.add_child(this.optionList.actor);
this.optionList.addOptions([ this.optionList.addOptions([
"Only PORT AND ADDRESS", "Only PORT AND ADDRESS",
@ -479,7 +479,7 @@ const PromptDialog = new Lang.Class({
"Only PORT", "Only PORT",
"Any Connection"]); "Any Connection"]);
if (sandboxed) { if (tlsguard) {
this.optionList.addTLSOption(true); this.optionList.addTLSOption(true);
} }
@ -548,12 +548,12 @@ const PromptDialog = new Lang.Class({
return this.optionList.tlsGuard; return this.optionList.tlsGuard;
}, },
update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, sandbox, expanded, expert, action) { update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action) {
this._address = address; this._address = address;
this._port = port; this._port = port;
this._proto = proto; this._proto = proto;
this._sandbox = sandbox; this._sandbox = sandbox;
this._tlsGuard; this._tlsGuard = tlsguard;
let port_str = (proto+"").toUpperCase() + " Port "+ port; let port_str = (proto+"").toUpperCase() + " Port "+ port;

@ -58,6 +58,7 @@ const FirewallPromptInterface = '<node> \
<arg type="s" direction="in" name="group" /> \ <arg type="s" direction="in" name="group" /> \
<arg type="i" direction="in" name="pid" /> \ <arg type="i" direction="in" name="pid" /> \
<arg type="s" direction="in" name="sandbox" /> \ <arg type="s" direction="in" name="sandbox" /> \
<arg type="b" direction="in" name="tlsguard" /> \
<arg type="s" direction="in" name="optstring" /> \ <arg type="s" direction="in" name="optstring" /> \
<arg type="b" direction="in" name="expanded" /> \ <arg type="b" direction="in" name="expanded" /> \
<arg type="b" direction="in" name="expert" /> \ <arg type="b" direction="in" name="expert" /> \
@ -93,11 +94,11 @@ const FirewallPromptHandler = new Lang.Class({
}, },
RequestPromptAsync: function(params, invocation) { RequestPromptAsync: function(params, invocation) {
let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, optstring, expanded, expert, action] = params; let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, tlsguard, optstring, expanded, expert, action] = params;
// this._closeDialog(); // this._closeDialog();
this._dialog = new Dialog.PromptDialog(invocation, (pid >= 0), (sandbox != "")); this._dialog = new Dialog.PromptDialog(invocation, (pid >= 0), (sandbox != ""), tlsguard);
this._invocation = invocation; this._invocation = invocation;
this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, sandbox, expanded, expert, action); this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, tlsguard, optstring, sandbox, expanded, expert, action);
this._dialog.open(); this._dialog.open();
}, },

@ -8,8 +8,8 @@
} }
.fw-prompt-dialog { .fw-prompt-dialog {
min-width: 450px; min-width: 600px;
max-width: 550px; max-width: 700px;
} }
.fw-group-button:checked { .fw-group-button:checked {

@ -2,15 +2,14 @@ package pcoroner
import ( import (
"fmt" "fmt"
"time" "os"
"strings"
"strconv" "strconv"
"strings"
"sync" "sync"
"os"
"syscall" "syscall"
"time"
) )
type WatchProcess struct { type WatchProcess struct {
Pid int Pid int
Inode uint64 Inode uint64
@ -25,14 +24,11 @@ type CallbackEntry struct {
type procCB func(int, interface{}) type procCB func(int, interface{})
var Callbacks []CallbackEntry var Callbacks []CallbackEntry
var pmutex = &sync.Mutex{} var pmutex = &sync.Mutex{}
var pidMap map[int]WatchProcess = make(map[int]WatchProcess) var pidMap map[int]WatchProcess = make(map[int]WatchProcess)
func MonitorProcess(pid int) bool { func MonitorProcess(pid int) bool {
pmutex.Lock() pmutex.Lock()
defer pmutex.Unlock() defer pmutex.Unlock()
@ -68,14 +64,14 @@ func AddCallback(cbfunc procCB, param interface{}) {
func MonitorThread(cbfunc procCB, param interface{}) { func MonitorThread(cbfunc procCB, param interface{}) {
for { for {
/* if len(pidMap) == 0 { /* if len(pidMap) == 0 {
fmt.Println("TICK") fmt.Println("TICK")
} else { fmt.Println("len = ", len(pidMap)) } */ } else { fmt.Println("len = ", len(pidMap)) } */
pmutex.Lock() pmutex.Lock()
pmutex.Unlock() pmutex.Unlock()
for pkey, pval := range pidMap { for pkey, pval := range pidMap {
// fmt.Printf("PID %v -> %v\n", pkey, pval) // fmt.Printf("PID %v -> %v\n", pkey, pval)
res := checkProcess(&pval, false) res := checkProcess(&pval, false)
if !res { if !res {
@ -100,7 +96,7 @@ func checkProcess(proc *WatchProcess, init bool) bool {
ppath := fmt.Sprintf("/proc/%d/stat", proc.Pid) ppath := fmt.Sprintf("/proc/%d/stat", proc.Pid)
f, err := os.Open(ppath) f, err := os.Open(ppath)
if err != nil { if err != nil {
// fmt.Printf("Error opening path %s: %s\n", ppath, err) // fmt.Printf("Error opening path %s: %s\n", ppath, err)
return false return false
} }
defer f.Close() defer f.Close()
@ -133,7 +129,7 @@ func checkProcess(proc *WatchProcess, init bool) bool {
} }
bstr := string(buf[:]) bstr := string(buf[:])
// fmt.Println("sstr = ", bstr) // fmt.Println("sstr = ", bstr)
fields := strings.Split(bstr, " ") fields := strings.Split(bstr, " ")

@ -27,7 +27,13 @@ type FirewallConfigs struct {
var FirewallConfig FirewallConfigs var FirewallConfig FirewallConfigs
func _readConfig(file string) []byte { func _readConfig(file string) []byte {
f, err := os.Open(configDefaultPath) envFile := os.Getenv("SGFW_CONF")
if envFile != "" {
file = envFile
}
f, err := os.Open(file)
if err != nil { if err != nil {
log.Warning(err.Error()) log.Warning(err.Error())
return []byte{} return []byte{}

@ -12,32 +12,37 @@ const (
//RuleAction is the action to apply to a rule //RuleAction is the action to apply to a rule
type RuleAction uint16 type RuleAction uint16
const ( const (
RULE_ACTION_DENY RuleAction = iota RULE_ACTION_DENY RuleAction = iota
RULE_ACTION_ALLOW RULE_ACTION_ALLOW
RULE_ACTION_ALLOW_TLSONLY RULE_ACTION_ALLOW_TLSONLY
) )
// RuleActionString is used to get a string from an action id // RuleActionString is used to get a string from an action id
var RuleActionString = map[RuleAction]string{ var RuleActionString = map[RuleAction]string{
RULE_ACTION_DENY: "DENY", RULE_ACTION_DENY: "DENY",
RULE_ACTION_ALLOW: "ALLOW", RULE_ACTION_ALLOW: "ALLOW",
RULE_ACTION_ALLOW_TLSONLY: "ALLOW_TLSONLY", RULE_ACTION_ALLOW_TLSONLY: "ALLOW_TLSONLY",
} }
// RuleActionValue is used to get an action id using the action string // RuleActionValue is used to get an action id using the action string
var RuleActionValue = map[string]RuleAction{ var RuleActionValue = map[string]RuleAction{
RuleActionString[RULE_ACTION_DENY]: RULE_ACTION_DENY, RuleActionString[RULE_ACTION_DENY]: RULE_ACTION_DENY,
RuleActionString[RULE_ACTION_ALLOW]: RULE_ACTION_ALLOW, RuleActionString[RULE_ACTION_ALLOW]: RULE_ACTION_ALLOW,
RuleActionString[RULE_ACTION_ALLOW_TLSONLY]: RULE_ACTION_ALLOW_TLSONLY, RuleActionString[RULE_ACTION_ALLOW_TLSONLY]: RULE_ACTION_ALLOW_TLSONLY,
} }
//RuleMode contains the time scope of a rule //RuleMode contains the time scope of a rule
type RuleMode uint16 type RuleMode uint16
const ( const (
RULE_MODE_SESSION RuleMode = iota RULE_MODE_SESSION RuleMode = iota
RULE_MODE_PROCESS RULE_MODE_PROCESS
RULE_MODE_PERMANENT RULE_MODE_PERMANENT
RULE_MODE_SYSTEM RULE_MODE_SYSTEM
) )
// RuleModeString is used to get a rule mode string from its id // RuleModeString is used to get a rule mode string from its id
var RuleModeString = map[RuleMode]string{ var RuleModeString = map[RuleMode]string{
RULE_MODE_SESSION: "SESSION", RULE_MODE_SESSION: "SESSION",
@ -45,6 +50,7 @@ var RuleModeString = map[RuleMode]string{
RULE_MODE_PERMANENT: "PERMANENT", RULE_MODE_PERMANENT: "PERMANENT",
RULE_MODE_SYSTEM: "SYSTEM", RULE_MODE_SYSTEM: "SYSTEM",
} }
// RuleModeValue converts a mode string to its id // RuleModeValue converts a mode string to its id
var RuleModeValue = map[string]RuleMode{ var RuleModeValue = map[string]RuleMode{
RuleModeString[RULE_MODE_SESSION]: RULE_MODE_SESSION, RuleModeString[RULE_MODE_SESSION]: RULE_MODE_SESSION,
@ -55,12 +61,14 @@ var RuleModeValue = map[string]RuleMode{
//FilterScope contains a filter's time scope //FilterScope contains a filter's time scope
type FilterScope uint16 type FilterScope uint16
const ( const (
APPLY_ONCE FilterScope = iota APPLY_ONCE FilterScope = iota
APPLY_SESSION APPLY_SESSION
APPLY_PROCESS APPLY_PROCESS
APPLY_FOREVER APPLY_FOREVER
) )
// FilterScopeString converts a filter scope ID to its string // FilterScopeString converts a filter scope ID to its string
var FilterScopeString = map[FilterScope]string{ var FilterScopeString = map[FilterScope]string{
APPLY_ONCE: "ONCE", APPLY_ONCE: "ONCE",
@ -68,6 +76,7 @@ var FilterScopeString = map[FilterScope]string{
APPLY_PROCESS: "PROCESS", APPLY_PROCESS: "PROCESS",
APPLY_FOREVER: "FOREVER", APPLY_FOREVER: "FOREVER",
} }
// FilterScopeString converts a filter scope string to its ID // FilterScopeString converts a filter scope string to its ID
var FilterScopeValue = map[string]FilterScope{ var FilterScopeValue = map[string]FilterScope{
FilterScopeString[APPLY_ONCE]: APPLY_ONCE, FilterScopeString[APPLY_ONCE]: APPLY_ONCE,
@ -75,6 +84,7 @@ var FilterScopeValue = map[string]FilterScope{
FilterScopeString[APPLY_PROCESS]: APPLY_PROCESS, FilterScopeString[APPLY_PROCESS]: APPLY_PROCESS,
FilterScopeString[APPLY_FOREVER]: APPLY_FOREVER, FilterScopeString[APPLY_FOREVER]: APPLY_FOREVER,
} }
// GetFilterScopeString is used to safely return a filter scope string // GetFilterScopeString is used to safely return a filter scope string
func GetFilterScopeString(scope FilterScope) string { func GetFilterScopeString(scope FilterScope) string {
if val, ok := FilterScopeString[scope]; ok { if val, ok := FilterScopeString[scope]; ok {
@ -82,6 +92,7 @@ func GetFilterScopeString(scope FilterScope) string {
} }
return FilterScopeString[APPLY_SESSION] return FilterScopeString[APPLY_SESSION]
} }
// GetFilterScopeValue is used to safely return a filter scope ID // GetFilterScopeValue is used to safely return a filter scope ID
func GetFilterScopeValue(scope string) FilterScope { func GetFilterScopeValue(scope string) FilterScope {
scope = strings.ToUpper(scope) scope = strings.ToUpper(scope)
@ -93,40 +104,44 @@ func GetFilterScopeValue(scope string) FilterScope {
//FilterResult contains the filtering resulting action //FilterResult contains the filtering resulting action
type FilterResult uint16 type FilterResult uint16
const ( const (
FILTER_DENY FilterResult = iota FILTER_DENY FilterResult = iota
FILTER_ALLOW FILTER_ALLOW
FILTER_PROMPT FILTER_PROMPT
FILTER_ALLOW_TLSONLY FILTER_ALLOW_TLSONLY
) )
// FilterResultString converts a filter value ID to its string // FilterResultString converts a filter value ID to its string
var FilterResultString = map[FilterResult]string{ var FilterResultString = map[FilterResult]string{
FILTER_DENY: "DENY", FILTER_DENY: "DENY",
FILTER_ALLOW: "ALLOW", FILTER_ALLOW: "ALLOW",
FILTER_PROMPT: "PROMPT", FILTER_PROMPT: "PROMPT",
FILTER_ALLOW_TLSONLY: "ALLOW_TLSONLY", FILTER_ALLOW_TLSONLY: "ALLOW_TLSONLY",
} }
// FilterResultValue converts a filter value string to its ID // FilterResultValue converts a filter value string to its ID
var FilterResultValue = map[string]FilterResult{ var FilterResultValue = map[string]FilterResult{
FilterResultString[FILTER_DENY]: FILTER_DENY, FilterResultString[FILTER_DENY]: FILTER_DENY,
FilterResultString[FILTER_ALLOW]: FILTER_ALLOW, FilterResultString[FILTER_ALLOW]: FILTER_ALLOW,
FilterResultString[FILTER_PROMPT]: FILTER_PROMPT, FilterResultString[FILTER_PROMPT]: FILTER_PROMPT,
FilterResultString[FILTER_ALLOW_TLSONLY]: FILTER_ALLOW_TLSONLY, FilterResultString[FILTER_ALLOW_TLSONLY]: FILTER_ALLOW_TLSONLY,
} }
// DbusRule struct of the rule passed to the dbus interface // DbusRule struct of the rule passed to the dbus interface
type DbusRule struct { type DbusRule struct {
ID uint32 ID uint32
Net string Net string
Origin string Origin string
Proto string Proto string
Pid uint32 Pid uint32
Privs string Privs string
App string App string
Path string Path string
Verb uint16 Verb uint16
Target string Target string
Mode uint16 Mode uint16
Sandbox string
} }
/*const ( /*const (

@ -62,7 +62,6 @@ func newDbusObjectPrompt() (*dbusObjectP, error) {
return &dbusObjectP{conn.Object("com.subgraph.fwprompt.EventNotifier", "/com/subgraph/fwprompt/EventNotifier")}, nil return &dbusObjectP{conn.Object("com.subgraph.fwprompt.EventNotifier", "/com/subgraph/fwprompt/EventNotifier")}, nil
} }
type dbusServer struct { type dbusServer struct {
fw *Firewall fw *Firewall
conn *dbus.Conn conn *dbus.Conn
@ -154,18 +153,20 @@ func createDbusRule(r *Rule) DbusRule {
} else if r.gid >= 0 { } else if r.gid >= 0 {
pstr += ":" + strconv.Itoa(r.gid) pstr += ":" + strconv.Itoa(r.gid)
} }
log.Debugf("SANDBOX SANDBOX SANDBOX: %s", r.sandbox)
return DbusRule{ return DbusRule{
ID: uint32(r.id), ID: uint32(r.id),
Net: netstr, Net: netstr,
Origin: ostr, Origin: ostr,
Proto: r.proto, Proto: r.proto,
Pid: uint32(r.pid), Pid: uint32(r.pid),
Privs: pstr, Privs: pstr,
App: path.Base(r.policy.path), App: path.Base(r.policy.path),
Path: r.policy.path, Path: r.policy.path,
Verb: uint16(r.rtype), Verb: uint16(r.rtype),
Target: r.AddrString(false), Target: r.AddrString(false),
Mode: uint16(r.mode), Mode: uint16(r.mode),
Sandbox: r.sandbox,
} }
} }
@ -224,6 +225,7 @@ func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error {
r.addr = tmp.addr r.addr = tmp.addr
r.port = tmp.port r.port = tmp.port
r.mode = RuleMode(rule.Mode) r.mode = RuleMode(rule.Mode)
r.sandbox = rule.Sandbox
r.policy.lock.Unlock() r.policy.lock.Unlock()
if r.mode != RULE_MODE_SESSION { if r.mode != RULE_MODE_SESSION {
ds.fw.saveRules() ds.fw.saveRules()

@ -1,23 +1,23 @@
package sgfw package sgfw
import ( import (
"encoding/binary"
"net" "net"
"strings" "strings"
"sync" "sync"
"time" "time"
"encoding/binary"
// "github.com/subgraph/go-nfnetlink" // "github.com/subgraph/go-nfnetlink"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/subgraph/fw-daemon/proc-coroner"
nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" nfqueue "github.com/subgraph/go-nfnetlink/nfqueue"
"github.com/subgraph/go-procsnitch" "github.com/subgraph/go-procsnitch"
"github.com/subgraph/fw-daemon/proc-coroner"
) )
type dnsEntry struct { type dnsEntry struct {
name string name string
ttl uint32 ttl uint32
exp time.Time exp time.Time
} }
type dnsCache struct { type dnsCache struct {
@ -66,14 +66,14 @@ func (dc *dnsCache) processDNS(pkt *nfqueue.NFQPacket) {
srcip, _ := getPacketIPAddrs(pkt) srcip, _ := getPacketIPAddrs(pkt)
pinfo := getEmptyPInfo() pinfo := getEmptyPInfo()
if !isNSTrusted(srcip) { if !isNSTrusted(srcip) {
pinfo, _ = findProcessForPacket(pkt, true, procsnitch.MATCH_LOOSEST) pinfo, _ = findProcessForPacket(pkt, true, procsnitch.MATCH_LOOSEST)
if pinfo == nil { if pinfo == nil {
log.Warningf("Skipping attempted DNS cache entry for process that can't be found: %v -> %v\n", q.Name, dns.answer) log.Warningf("Skipping attempted DNS cache entry for process that can't be found: %v -> %v\n", q.Name, dns.answer)
return return
} }
} }
//log.Notice("XXX: PROCESS LOOKUP -> ", pinfo) //log.Notice("XXX: PROCESS LOOKUP -> ", pinfo)
dc.processRecordAddress(q.Name, dns.answer, pinfo.Pid) dc.processRecordAddress(q.Name, dns.answer, pinfo.Pid)
return return
} }
@ -166,7 +166,7 @@ func (dc *dnsCache) Lookup(ip net.IP, pid int) string {
entry, ok := dc.ipMap[pid][ip.String()] entry, ok := dc.ipMap[pid][ip.String()]
if ok { if ok {
if now.Before(entry.exp) { if now.Before(entry.exp) {
// log.Noticef("XXX: LOOKUP on %v / %v = %v, ttl = %v / %v\n", pid, ip.String(), entry.name, entry.ttl, entry.exp) // log.Noticef("XXX: LOOKUP on %v / %v = %v, ttl = %v / %v\n", pid, ip.String(), entry.name, entry.ttl, entry.exp)
return entry.name return entry.name
} else { } else {
log.Warningf("Skipping expired per-pid (%d) DNS cache entry: %s -> %s / exp. %v (%ds)\n", log.Warningf("Skipping expired per-pid (%d) DNS cache entry: %s -> %s / exp. %v (%ds)\n",
@ -180,13 +180,13 @@ func (dc *dnsCache) Lookup(ip net.IP, pid int) string {
if ok { if ok {
if now.Before(entry.exp) { if now.Before(entry.exp) {
str = entry.name str = entry.name
// log.Noticef("XXX: LOOKUP on %v / 0 RETURNING %v, ttl = %v / %v\n", ip.String(), str, entry.ttl, entry.exp) // log.Noticef("XXX: LOOKUP on %v / 0 RETURNING %v, ttl = %v / %v\n", ip.String(), str, entry.ttl, entry.exp)
} else { } else {
log.Warningf("Skipping expired global DNS cache entry: %s -> %s / exp. %v (%ds)\n", log.Warningf("Skipping expired global DNS cache entry: %s -> %s / exp. %v (%ds)\n",
ip.String(), entry.name, entry.exp, entry.ttl) ip.String(), entry.name, entry.exp, entry.ttl)
} }
} }
//log.Noticef("XXX: LOOKUP on %v / 0 RETURNING %v\n", ip.String(), str) //log.Noticef("XXX: LOOKUP on %v / 0 RETURNING %v\n", ip.String(), str)
return str return str
} }

@ -758,14 +758,14 @@ func unpackRR(msg []byte, off int) (rr dnsRR, off1 int, ok bool) {
// A manually-unpacked version of (id, bits). // A manually-unpacked version of (id, bits).
// This is in its own struct for easy printing. // This is in its own struct for easy printing.
type dnsMsgHdr struct { type dnsMsgHdr struct {
id uint16 id uint16
response bool response bool
opcode int opcode int
authoritative bool authoritative bool
truncated bool truncated bool
recursionDesired bool recursionDesired bool
recursionAvailable bool recursionAvailable bool
rcode int rcode int
} }
func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool { func (h *dnsMsgHdr) Walk(f func(v interface{}, name, tag string) bool) bool {

@ -1,31 +1,29 @@
package sgfw package sgfw
import ( import (
"bufio"
"errors"
"fmt" "fmt"
"net" "net"
"os" "os"
"bufio"
"strings"
"strconv" "strconv"
"errors" "strings"
"github.com/subgraph/oz/ipc" "github.com/subgraph/oz/ipc"
) )
const ReceiverSocketPath = "/var/run/fw-daemon/fwoz.sock" const ReceiverSocketPath = "/var/run/fw-daemon/fwoz.sock"
type OzInitProc struct { type OzInitProc struct {
Name string Name string
Pid int Pid int
SandboxID int SandboxID int
} }
var OzInitPids []OzInitProc = []OzInitProc{} var OzInitPids []OzInitProc = []OzInitProc{}
func addInitPid(pid int, name string, sboxid int) { func addInitPid(pid int, name string, sboxid int) {
fmt.Println("::::::::::: init pid added: ", pid, " -> ", name) fmt.Println("::::::::::: init pid added: ", pid, " -> ", name)
for i := 0; i < len(OzInitPids); i++ { for i := 0; i < len(OzInitPids); i++ {
if OzInitPids[i].Pid == pid { if OzInitPids[i].Pid == pid {
return return
@ -37,7 +35,7 @@ fmt.Println("::::::::::: init pid added: ", pid, " -> ", name)
} }
func removeInitPid(pid int) { func removeInitPid(pid int) {
fmt.Println("::::::::::: removing PID: ", pid) fmt.Println("::::::::::: removing PID: ", pid)
for i := 0; i < len(OzInitPids); i++ { for i := 0; i < len(OzInitPids); i++ {
if OzInitPids[i].Pid == pid { if OzInitPids[i].Pid == pid {
OzInitPids = append(OzInitPids[:i], OzInitPids[i+1:]...) OzInitPids = append(OzInitPids[:i], OzInitPids[i+1:]...)
@ -63,7 +61,7 @@ func addFWRule(fw *Firewall, whitelist bool, srchost, dsthost, dstport string) e
} }
func removeAllByIP(fw *Firewall, srcip string) bool { func removeAllByIP(fw *Firewall, srcip string) bool {
log.Notice("XXX: Attempting to remove all rules associated with Oz interface: ", srcip) log.Notice("XXX: Attempting to remove all rules associated with Oz interface: ", srcip)
saddr := net.ParseIP(srcip) saddr := net.ParseIP(srcip)
if saddr == nil { if saddr == nil {
@ -73,13 +71,13 @@ log.Notice("XXX: Attempting to remove all rules associated with Oz interface: ",
policy := fw.PolicyForPath("*") policy := fw.PolicyForPath("*")
nrm := 0 nrm := 0
for _, rr := range policy.rules { for _, rr := range policy.rules {
if rr.saddr != nil && rr.saddr.Equal(saddr) { if rr.saddr != nil && rr.saddr.Equal(saddr) {
log.Notice("XXX: removing ephemeral rules by Oz interface ", srcip, ": ", rr) log.Notice("XXX: removing ephemeral rules by Oz interface ", srcip, ": ", rr)
policy.removeRule(rr) policy.removeRule(rr)
nrm++ nrm++
} }
} }
if nrm == 0 { if nrm == 0 {
log.Notice("XXX: did not remove any rules for interface") log.Notice("XXX: did not remove any rules for interface")
@ -102,10 +100,10 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
data := string(buf) data := string(buf)
log.Notice("Received incoming IPC:",data) log.Notice("Received incoming IPC:", data)
if data[len(data)-1] == '\n' { if data[len(data)-1] == '\n' {
data = data[0:len(data)-1] data = data[0 : len(data)-1]
} }
if data == "dump" { if data == "dump" {
@ -141,18 +139,18 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
c.Write([]byte(ruledesc)) c.Write([]byte(ruledesc))
} }
/* for i := 0; i < len(sandboxRules); i++ { /* for i := 0; i < len(sandboxRules); i++ {
rulestr := "" rulestr := ""
if sandboxRules[i].Whitelist { if sandboxRules[i].Whitelist {
rulestr += "whitelist" rulestr += "whitelist"
} else { } else {
rulestr += "blacklist" rulestr += "blacklist"
} }
rulestr += " " + sandboxRules[i].SrcIf.String() + " -> " + sandboxRules[i].DstIP.String() + " : " + strconv.Itoa(int(sandboxRules[i].DstPort)) + "\n" rulestr += " " + sandboxRules[i].SrcIf.String() + " -> " + sandboxRules[i].DstIP.String() + " : " + strconv.Itoa(int(sandboxRules[i].DstPort)) + "\n"
c.Write([]byte(rulestr)) c.Write([]byte(rulestr))
} */ } */
return return
} else { } else {
@ -166,7 +164,7 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
if tokens[0] == "register-init" && len(tokens) >= 3 { if tokens[0] == "register-init" && len(tokens) >= 3 {
initp := tokens[1] initp := tokens[1]
initpid, err := strconv.Atoi(initp) initpid, err := strconv.Atoi(initp)
if err != nil { if err != nil {
@ -177,7 +175,7 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
sboxid, err := strconv.Atoi(tokens[3]) sboxid, err := strconv.Atoi(tokens[3])
if err != nil { if err != nil {
log.Notice("IPC received invalid oz sbox number: ",tokens[3]) log.Notice("IPC received invalid oz sbox number: ", tokens[3])
log.Notice("Data: %v", data) log.Notice("Data: %v", data)
c.Write([]byte("Bad command: sandbox id was invalid")) c.Write([]byte("Bad command: sandbox id was invalid"))
return return
@ -234,30 +232,30 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
if srcip == nil { if srcip == nil {
log.Notice("IP conversion failed: ", srchost) log.Notice("IP conversion failed: ", srchost)
srcip = net.IP{0,0,0,0} srcip = net.IP{0, 0, 0, 0}
} }
dstport := tokens[4] dstport := tokens[4]
dstp, err := strconv.Atoi(dstport) dstp, err := strconv.Atoi(dstport)
if dstport != "*" && (err != nil || dstp < 0 || dstp > 65535) { if dstport != "*" && (err != nil || dstp < 0 || dstp > 65535) {
log.Notice("IPC received invalid destination port: ", tokens[4]) log.Notice("IPC received invalid destination port: ", tokens[4])
c.Write([]byte("Bad command: dst port was invalid")) c.Write([]byte("Bad command: dst port was invalid"))
return return
} }
/* initp := tokens[5] /* initp := tokens[5]
initpid, err := strconv.Atoi(initp) initpid, err := strconv.Atoi(initp)
if err != nil { if err != nil {
log.Notice("IPC received invalid oz-init pid: ", initp) log.Notice("IPC received invalid oz-init pid: ", initp)
c.Write([]byte("Bad command: init pid was invalid")) c.Write([]byte("Bad command: init pid was invalid"))
return return
} */ } */
if add { if add {
log.Noticef("Adding new rule to oz sandbox/fw: %v / %v -> %v : %v", w, srchost, dsthost, dstport) log.Noticef("Adding new rule to oz sandbox/fw: %v / %v -> %v : %v", w, srchost, dsthost, dstport)
// addInitPid(initpid) // addInitPid(initpid)
err := addFWRule(fw, w, srchost, dsthost, dstport) err := addFWRule(fw, w, srchost, dsthost, dstport)
if err != nil { if err != nil {
log.Error("Error adding dynamic OZ firewall rule to fw-daemon: ", err) log.Error("Error adding dynamic OZ firewall rule to fw-daemon: ", err)
@ -268,13 +266,11 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
log.Notice("Removing new rule from oz sandbox/fw... ") log.Notice("Removing new rule from oz sandbox/fw... ")
} }
log.Notice("IPC received command: " + data) log.Notice("IPC received command: " + data)
c.Write([]byte("OK.\n")) c.Write([]byte("OK.\n"))
return return
} }
} }
} }
@ -303,7 +299,7 @@ func OzReceiver(fw *Firewall) {
os.Remove(ReceiverSocketPath) os.Remove(ReceiverSocketPath)
lfd, err := net.Listen("unix", ReceiverSocketPath) lfd, err := net.Listen("unix", ReceiverSocketPath)
if err != nil { if err != nil {
log.Fatal("Could not open oz receiver socket:", err) log.Fatal("Could not open oz receiver socket:", err)
} }
for { for {
@ -313,11 +309,10 @@ func OzReceiver(fw *Firewall) {
} }
go ReceiverLoop(fw, fd) go ReceiverLoop(fw, fd)
} }
} }
type ListProxiesMsg struct { type ListProxiesMsg struct {
_ string "ListProxies" _ string "ListProxies"
} }
@ -339,11 +334,12 @@ func ListProxies() ([]string, error) {
} }
const OzSocketName = "@oz-control" const OzSocketName = "@oz-control"
var bSockName = OzSocketName var bSockName = OzSocketName
var messageFactory = ipc.NewMsgFactory( var messageFactory = ipc.NewMsgFactory(
new(ListProxiesMsg), new(ListProxiesMsg),
new(ListProxiesResp), new(ListProxiesResp),
) )
func clientConnect() (*ipc.MsgConn, error) { func clientConnect() (*ipc.MsgConn, error) {

@ -1,10 +1,10 @@
package sgfw package sgfw
import ( import (
"fmt"
"os" "os"
"syscall" "syscall"
"unsafe" "unsafe"
"fmt"
"github.com/op/go-logging" "github.com/op/go-logging"
) )

@ -46,6 +46,7 @@ type pendingConnection interface {
dst() net.IP dst() net.IP
dstPort() uint16 dstPort() uint16
sandbox() string sandbox() string
socks() bool
accept() accept()
acceptTLSOnly() acceptTLSOnly()
drop() drop()
@ -78,6 +79,10 @@ func (pp *pendingPkt) sandbox() string {
return pp.pinfo.Sandbox return pp.pinfo.Sandbox
} }
func (pp *pendingPkt) socks() bool {
return false
}
func (pp *pendingPkt) policy() *Policy { func (pp *pendingPkt) policy() *Policy {
return pp.pol return pp.pol
} }
@ -175,7 +180,7 @@ func (pp *pendingPkt) print() string {
type Policy struct { type Policy struct {
fw *Firewall fw *Firewall
path string path string
sandbox string sandbox string
application string application string
icon string icon string
rules RuleList rules RuleList
@ -194,7 +199,7 @@ func (fw *Firewall) PolicyForPath(path string) *Policy {
func (fw *Firewall) PolicyForPathAndSandbox(path string, sandbox string) *Policy { func (fw *Firewall) PolicyForPathAndSandbox(path string, sandbox string) *Policy {
fw.lock.Lock() fw.lock.Lock()
defer fw.lock.Unlock() defer fw.lock.Unlock()
return fw.policyForPathAndSandbox(path, sandbox) return fw.policyForPathAndSandbox(path, sandbox)
} }
@ -212,7 +217,7 @@ func (fw *Firewall) policyForPathAndSandbox(path string, sandbox string) *Policy
p.icon = entry.icon p.icon = entry.icon
} }
fw.policyMap[policykey] = p fw.policyMap[policykey] = p
log.Infof("Creating new policy for path and sandbox: %s\n",policykey) log.Infof("Creating new policy for path and sandbox: %s\n", policykey)
fw.policies = append(fw.policies, p) fw.policies = append(fw.policies, p)
} }
return fw.policyMap[policykey] return fw.policyMap[policykey]
@ -268,8 +273,7 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, o
func (p *Policy) processPromptResult(pc pendingConnection) { func (p *Policy) processPromptResult(pc pendingConnection) {
p.pendingQueue = append(p.pendingQueue, pc) p.pendingQueue = append(p.pendingQueue, pc)
fmt.Println("im here now.. processing prompt result..") //fmt.Println("processPromptResult(): p.promptInProgress = ", p.promptInProgress)
fmt.Println("processPromptResult(): p.promptInProgress = ", p.promptInProgress)
if DoMultiPrompt || (!DoMultiPrompt && !p.promptInProgress) { if DoMultiPrompt || (!DoMultiPrompt && !p.promptInProgress) {
p.promptInProgress = true p.promptInProgress = true
go p.fw.dbus.prompt(p) go p.fw.dbus.prompt(p)
@ -332,7 +336,7 @@ func (p *Policy) processNewRule(r *Rule, scope FilterScope) bool {
} }
func (p *Policy) parseRule(s string, add bool) (*Rule, error) { func (p *Policy) parseRule(s string, add bool) (*Rule, error) {
log.Noticef("XXX: attempt to parse rule: |%s|\n", s) //log.Noticef("XXX: attempt to parse rule: |%s|\n", s)
r := new(Rule) r := new(Rule)
r.pid = -1 r.pid = -1
r.mode = RULE_MODE_PERMANENT r.mode = RULE_MODE_PERMANENT
@ -367,7 +371,7 @@ func (p *Policy) filterPending(rule *Rule) {
for _, pc := range p.pendingQueue { for _, pc := range p.pendingQueue {
if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.proto(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID)) { if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.proto(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID)) {
log.Infof("Adding rule for: %s", rule.getString(FirewallConfig.LogRedact)) log.Infof("Adding rule for: %s", rule.getString(FirewallConfig.LogRedact))
log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print()) // log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print())
if rule.rtype == RULE_ACTION_ALLOW { if rule.rtype == RULE_ACTION_ALLOW {
pc.accept() pc.accept()
} else if rule.rtype == RULE_ACTION_ALLOW_TLSONLY { } else if rule.rtype == RULE_ACTION_ALLOW_TLSONLY {
@ -502,7 +506,7 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) {
return return
} }
*/ */
policy := fw.PolicyForPathAndSandbox(ppath,pinfo.Sandbox) policy := fw.PolicyForPathAndSandbox(ppath, pinfo.Sandbox)
//log.Notice("XXX: flunked basicallowpacket; policy = ", policy) //log.Notice("XXX: flunked basicallowpacket; policy = ", policy)
policy.processPacket(pkt, pinfo, optstring) policy.processPacket(pkt, pinfo, optstring)
} }
@ -524,18 +528,33 @@ func readFileDirect(filename string) ([]byte, error) {
fd := int(res) fd := int(res)
data := make([]byte, 65535) data := make([]byte, 65535)
val, err := syscall.Read(fd, data) i := 0
val := 0
if err != nil { for i = 0; i < 65535; {
return nil, err val, err = syscall.Read(fd, data[i:])
i += val
if err != nil && val != 0 {
return nil, err
}
if val == 0 {
break
}
} }
syscall.Close(fd) data = data[0:i]
/*
if val < 65535 { val, err := syscall.Read(fd, data)
data = data[0:val]
}
if err != nil {
return nil, err
}
*/
syscall.Close(fd)
/*
if val < 65535 {
data = data[0:val]
}
*/
return data, nil return data, nil
} }
@ -584,6 +603,10 @@ func GetRealRoot(pathname string, pid int) string {
return pathname return pathname
} }
if lnk == "/" {
return pathname
}
if strings.HasPrefix(pathname, lnk) { if strings.HasPrefix(pathname, lnk) {
return pathname[len(lnk):] return pathname[len(lnk):]
} }
@ -615,7 +638,6 @@ func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, pro
data = string(bdata) data = string(bdata)
lines := strings.Split(data, "\n") lines := strings.Split(data, "\n")
rlines := make([]string, 0) rlines := make([]string, 0)
for l := 0; l < len(lines); l++ { for l := 0; l < len(lines); l++ {
lines[l] = strings.TrimSpace(lines[l]) lines[l] = strings.TrimSpace(lines[l])
ssplit := strings.Split(lines[l], ":") ssplit := strings.Split(lines[l], ":")
@ -627,12 +649,16 @@ func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, pro
rlines = append(rlines, strings.Join(ssplit, ":")) rlines = append(rlines, strings.Join(ssplit, ":"))
} }
// log.Warningf("Looking for %s:%d => %s:%d \n %s\n******\n", srcip, srcp, dstip, dstp, data)
if proto == "tcp" { if proto == "tcp" {
res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, rlines) res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, rlines)
} else if proto == "udp" { } else if proto == "udp" {
res = procsnitch.LookupUDPSocketProcessAll(srcip, srcp, dstip, dstp, rlines, strictness) res = procsnitch.LookupUDPSocketProcessAll(srcip, srcp, dstip, dstp, rlines, strictness)
} else if proto == "icmp" { } else if proto == "icmp" {
res = procsnitch.LookupICMPSocketProcessAll(srcip, dstip, icode, rlines) res = procsnitch.LookupICMPSocketProcessAll(srcip, dstip, icode, rlines)
} else {
fmt.Printf("unknown proto: %s", proto)
} }
if res != nil { if res != nil {
@ -640,7 +666,9 @@ func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, pro
res.Sandbox = OzInitPids[i].Name res.Sandbox = OzInitPids[i].Name
res.ExePath = GetRealRoot(res.ExePath, OzInitPids[i].Pid) res.ExePath = GetRealRoot(res.ExePath, OzInitPids[i].Pid)
break break
} } /*else {
log.Warningf("*****\nCouldn't find proc for %s:%d => %s:%d \n %s\n******\n", srcip, srcp, dstip, dstp, data)
} */
} }
} }
@ -681,7 +709,7 @@ func findProcessForPacket(pkt *nfqueue.NFQPacket, reverse bool, strictness int)
return nil, optstr return nil, optstr
} }
log.Noticef("XXX proto = %s, from %v : %v -> %v : %v\n", proto, srcip, srcp, dstip, dstp) // log.Noticef("XXX proto = %s, from %v : %v -> %v : %v\n", proto, srcip, srcp, dstip, dstp)
var res *procsnitch.Info = nil var res *procsnitch.Info = nil

@ -13,9 +13,10 @@ import (
"github.com/subgraph/fw-daemon/proc-coroner" "github.com/subgraph/fw-daemon/proc-coroner"
) )
var DoMultiPrompt = true var DoMultiPrompt = true
const MAX_PROMPTS = 3
const MAX_PROMPTS = 5
var outstandingPrompts = 0 var outstandingPrompts = 0
var promptLock = &sync.Mutex{} var promptLock = &sync.Mutex{}
@ -39,12 +40,12 @@ type prompter struct {
func (p *prompter) prompt(policy *Policy) { func (p *prompter) prompt(policy *Policy) {
p.lock.Lock() p.lock.Lock()
defer p.lock.Unlock() defer p.lock.Unlock()
_, ok := p.policyMap[policy.sandbox + "|" + policy.path] _, ok := p.policyMap[policy.sandbox+"|"+policy.path]
if ok { if ok {
return return
} }
p.policyMap[policy.sandbox + "|" + policy.path] = policy p.policyMap[policy.sandbox+"|"+policy.path] = policy
fmt.Println("Saving policy key:"+policy.sandbox + "|" + policy.path) fmt.Println("Saving policy key:" + policy.sandbox + "|" + policy.path)
p.policyQueue = append(p.policyQueue, policy) p.policyQueue = append(p.policyQueue, policy)
p.cond.Signal() p.cond.Signal()
} }
@ -52,11 +53,11 @@ func (p *prompter) prompt(policy *Policy) {
func (p *prompter) promptLoop() { func (p *prompter) promptLoop() {
p.lock.Lock() p.lock.Lock()
for { for {
fmt.Println("promptLoop() outer") // fmt.Println("XXX: promptLoop() outer")
for p.processNextPacket() { for p.processNextPacket() {
fmt.Println("promptLoop() inner") // fmt.Println("XXX: promptLoop() inner")
} }
fmt.Println("promptLoop() wait") // fmt.Println("promptLoop() wait")
p.cond.Wait() p.cond.Wait()
} }
} }
@ -78,7 +79,7 @@ func (p *prompter) processNextPacket() bool {
empty := true empty := true
for { for {
pc, empty = p.nextConnection() pc, empty = p.nextConnection()
fmt.Println("processNextPacket() loop; empty = ", empty, " / pc = ", pc) // fmt.Println("XXX: processNextPacket() loop; empty = ", empty, " / pc = ", pc)
if pc == nil && empty { if pc == nil && empty {
return false return false
} else if pc == nil { } else if pc == nil {
@ -89,7 +90,7 @@ fmt.Println("processNextPacket() loop; empty = ", empty, " / pc = ", pc)
} }
p.lock.Unlock() p.lock.Unlock()
defer p.lock.Lock() defer p.lock.Lock()
fmt.Println("Waiting for prompt lock go...") // fmt.Println("XXX: Waiting for prompt lock go...")
for { for {
promptLock.Lock() promptLock.Lock()
if outstandingPrompts >= MAX_PROMPTS { if outstandingPrompts >= MAX_PROMPTS {
@ -105,21 +106,21 @@ fmt.Println("processNextPacket() loop; empty = ", empty, " / pc = ", pc)
break break
} }
fmt.Println("Passed prompt lock!") // fmt.Println("XXX: Passed prompt lock!")
outstandingPrompts++ outstandingPrompts++
fmt.Println("Incremented outstanding to ", outstandingPrompts) // fmt.Println("XXX: Incremented outstanding to ", outstandingPrompts)
promptLock.Unlock() promptLock.Unlock()
// if !pc.getPrompting() { // if !pc.getPrompting() {
pc.setPrompting(true) pc.setPrompting(true)
go p.processConnection(pc) go p.processConnection(pc)
// } // }
return true return true
} }
func processReturn (pc pendingConnection) { func processReturn(pc pendingConnection) {
promptLock.Lock() promptLock.Lock()
outstandingPrompts-- outstandingPrompts--
fmt.Println("Return decremented outstanding to ", outstandingPrompts) // fmt.Println("XXX: Return decremented outstanding to ", outstandingPrompts)
promptLock.Unlock() promptLock.Unlock()
pc.setPrompting(false) pc.setPrompting(false)
} }
@ -161,6 +162,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
gidToGroup(pc.procInfo().GID), gidToGroup(pc.procInfo().GID),
int32(pc.procInfo().Pid), int32(pc.procInfo().Pid),
pc.sandbox(), pc.sandbox(),
pc.socks(),
pc.getOptString(), pc.getOptString(),
FirewallConfig.PromptExpanded, FirewallConfig.PromptExpanded,
FirewallConfig.PromptExpert, FirewallConfig.PromptExpert,
@ -173,14 +175,14 @@ func (p *prompter) processConnection(pc pendingConnection) {
return return
} }
// the prompt sends: // the prompt sends:
// ALLOW|dest or DENY|dest // ALLOW|dest or DENY|dest
// //
// rule string needs to be: // rule string needs to be:
// VERB|dst|class|uid:gid|sandbox|[src] // VERB|dst|class|uid:gid|sandbox|[src]
// sometimes there's a src // sometimes there's a src
// this needs to be re-visited // this needs to be re-visited
toks := strings.Split(rule, "|") toks := strings.Split(rule, "|")
//verb := toks[0] //verb := toks[0]
@ -190,19 +192,19 @@ func (p *prompter) processConnection(pc pendingConnection) {
if len(toks) > 2 { if len(toks) > 2 {
sandbox = toks[2] sandbox = toks[2]
} }
tempRule := fmt.Sprintf("%s|%s",toks[0],toks[1])
if (pc.src() != nil && !pc.src().Equal(net.ParseIP("127.0.0.1")) && sandbox != "") { tempRule := fmt.Sprintf("%s|%s", toks[0], toks[1])
if pc.src() != nil && !pc.src().Equal(net.ParseIP("127.0.0.1")) && sandbox != "" {
//if !strings.HasSuffix(rule, "SYSTEM") && !strings.HasSuffix(rule, "||") { //if !strings.HasSuffix(rule, "SYSTEM") && !strings.HasSuffix(rule, "||") {
//rule += "||" //rule += "||"
//} //}
//ule += "|||" + pc.src().String() //ule += "|||" + pc.src().String()
tempRule += "||-1:-1|"+sandbox+"|" + pc.src().String() tempRule += "||-1:-1|" + sandbox + "|" + pc.src().String()
} else { } else {
tempRule += "||-1:-1|"+sandbox+"|" tempRule += "||-1:-1|" + sandbox + "|"
} }
r, err := policy.parseRule(tempRule, false) r, err := policy.parseRule(tempRule, false)
if err != nil { if err != nil {
@ -228,7 +230,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
r.mode = RULE_MODE_PERMANENT r.mode = RULE_MODE_PERMANENT
policy.fw.saveRules() policy.fw.saveRules()
} }
log.Warningf("Prompt returning rule: %v", rule) log.Warningf("Prompt returning rule: %v", tempRule)
dbusp.alertRule("sgfw prompt added new rule") dbusp.alertRule("sgfw prompt added new rule")
} }
@ -270,7 +272,7 @@ func (p *prompter) removePolicy(policy *Policy) {
} }
} }
p.policyQueue = newQueue p.policyQueue = newQueue
delete(p.policyMap, policy.sandbox + "|" + policy.path) delete(p.policyMap, policy.sandbox+"|"+policy.path)
} }
var userMap = make(map[int]string) var userMap = make(map[int]string)

@ -1,7 +1,7 @@
package sgfw package sgfw
import ( import (
"encoding/binary" // "encoding/binary"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
@ -71,8 +71,6 @@ func (r *Rule) getString(redact bool) string {
sbox := "|" sbox := "|"
if r.sandbox != "" { if r.sandbox != "" {
sbox = "|" + sbox sbox = "|" + sbox
} else {
log.Notice("sandbox is ", r.sandbox)
} }
return fmt.Sprintf("%s|%s%s%s%s%s", rtype, protostr, r.AddrString(redact), rmode, rpriv, sbox) return fmt.Sprintf("%s|%s%s%s%s%s", rtype, protostr, r.AddrString(redact), rmode, rpriv, sbox)
@ -119,7 +117,7 @@ func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, pr
return false return false
} }
log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", r.addr, " / ", r.hostname, " : ", r.port) // log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", r.addr, " / ", r.hostname, " : ", r.port)
if r.port != matchAny && r.port != dstPort { if r.port != matchAny && r.port != dstPort {
return false return false
} }
@ -127,7 +125,6 @@ func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, pr
return true return true
} }
if r.hostname != "" { if r.hostname != "" {
log.Notice("comparing hostname")
if strings.ContainsAny(r.hostname, "*") { if strings.ContainsAny(r.hostname, "*") {
regstr := strings.Replace(r.hostname, "*", ".?", -1) regstr := strings.Replace(r.hostname, "*", ".?", -1)
match, err := regexp.MatchString(regstr, hostname) match, err := regexp.MatchString(regstr, hostname)
@ -144,7 +141,7 @@ func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, pr
return true return true
} }
if proto == "icmp" { if proto == "icmp" {
fmt.Printf("network = %v, src = %v, r.addr = %x, src to4 = %x\n", r.network, src, r.addr, binary.BigEndian.Uint32(src.To4())) //fmt.Printf("network = %v, src = %v, r.addr = %x, src to4 = %x\n", r.network, src, r.addr, binary.BigEndian.Uint32(src.To4()))
if (r.network != nil && r.network.Contains(src)) || (r.addr.Equal(src)) { if (r.network != nil && r.network.Contains(src)) || (r.addr.Equal(src)) {
return true return true
} }
@ -169,10 +166,9 @@ func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, src, dst net.IP, dstPort uint
} }
// sandboxed := strings.HasPrefix(optstr, "SOCKS5|Tor / Sandbox") // sandboxed := strings.HasPrefix(optstr, "SOCKS5|Tor / Sandbox")
for _, r := range *rl { for _, r := range *rl {
log.Notice("fuck ",r)
nfqproto := "" nfqproto := ""
log.Notice("------------ trying match of src ", src, " against: ", r, " | ", r.saddr, " / optstr = ", optstr, "; pid ", pinfo.Pid, " vs rule pid ", r.pid) //log.Notice("------------ trying match of src ", src, " against: ", r, " | ", r.saddr, " / optstr = ", optstr, "; pid ", pinfo.Pid, " vs rule pid ", r.pid)
log.Notice("r.saddr: ", r.saddr, "src: ", src, "sandboxed ", sandboxed, "optstr: ", optstr) //log.Notice("r.saddr: ", r.saddr, "src: ", src, "sandboxed ", sandboxed, "optstr: ", optstr)
if r.saddr == nil && src != nil && sandboxed { if r.saddr == nil && src != nil && sandboxed {
log.Notice("! Skipping comparison against incompatible rule types: rule src = ", r.saddr, " / packet src = ", src) log.Notice("! Skipping comparison against incompatible rule types: rule src = ", r.saddr, " / packet src = ", src)
// continue // continue
@ -187,16 +183,27 @@ func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, src, dst net.IP, dstPort uint
if pkt != nil { if pkt != nil {
nfqproto = getNFQProto(pkt) nfqproto = getNFQProto(pkt)
} else { } else {
log.Notice("Weird state.") if r.saddr == nil && src == nil && sandboxed == false && (r.port == dstPort || r.port == matchAny) && (r.addr.Equal(anyAddress) || r.hostname == "" || r.hostname == hostname) {
// log.Notice("+ Socks5 MATCH SUCCEEDED")
if r.rtype == RULE_ACTION_DENY {
return FILTER_DENY
} else if r.rtype == RULE_ACTION_ALLOW {
return FILTER_ALLOW
} else if r.rtype == RULE_ACTION_ALLOW_TLSONLY {
return FILTER_ALLOW_TLSONLY
}
} else {
return FILTER_PROMPT
}
} }
} }
log.Notice("r.saddr = ", r.saddr, "src = ", src, "\n") // log.Notice("r.saddr = ", r.saddr, "src = ", src, "\n")
if r.pid >= 0 && r.pid != pinfo.Pid { if r.pid >= 0 && r.pid != pinfo.Pid {
//log.Notice("! Skipping comparison of mismatching PIDs") //log.Notice("! Skipping comparison of mismatching PIDs")
continue continue
} }
if r.match(src, dst, dstPort, hostname, nfqproto, pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) { if r.match(src, dst, dstPort, hostname, nfqproto, pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) {
log.Notice("+ MATCH SUCCEEDED") // log.Notice("+ MATCH SUCCEEDED")
dstStr := dst.String() dstStr := dst.String()
if FirewallConfig.LogRedact { if FirewallConfig.LogRedact {
dstStr = STR_REDACTED dstStr = STR_REDACTED
@ -207,12 +214,10 @@ func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, src, dst net.IP, dstPort uint
srcp, _ := getPacketPorts(pkt) srcp, _ := getPacketPorts(pkt)
srcStr = fmt.Sprintf("%s:%d", srcip, srcp) srcStr = fmt.Sprintf("%s:%d", srcip, srcp)
} }
log.Noticef("%s > %s %s %s -> %s:%d", // log.Noticef("%s > %s %s %s -> %s:%d",
r.getString(FirewallConfig.LogRedact), //r.getString(FirewallConfig.LogRedact), pinfo.ExePath, r.proto, srcStr, dstStr, dstPort)
pinfo.ExePath, r.proto,
srcStr,
dstStr, dstPort)
if r.rtype == RULE_ACTION_DENY { if r.rtype == RULE_ACTION_DENY {
//TODO: Optionally redact below log entry
log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d", log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d",
pinfo.ExePath, r.proto, pinfo.ExePath, r.proto,
srcStr, srcStr,
@ -222,19 +227,20 @@ func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, src, dst net.IP, dstPort uint
result = FILTER_ALLOW result = FILTER_ALLOW
return result return result
/* /*
if r.saddr != nil { if r.saddr != nil {
return result return result
} }
*/ */
} else if r.rtype == RULE_ACTION_ALLOW_TLSONLY { } else if r.rtype == RULE_ACTION_ALLOW_TLSONLY {
result = FILTER_ALLOW_TLSONLY result = FILTER_ALLOW_TLSONLY
return result return result
} }
} else {
log.Notice("+ MATCH FAILED")
} }
/**else {
log.Notice("+ MATCH FAILED")
} */
} }
log.Notice("--- RESULT = ", result) // log.Notice("--- RESULT = ", result)
return result return result
} }
@ -269,7 +275,7 @@ func (r *Rule) parse(s string) bool {
return false return false
} }
fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v, sandbox = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname, r.sandbox) // fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v, sandbox = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname, r.sandbox)
if len(parts) == 6 && len(strings.TrimSpace(parts[5])) > 0 { if len(parts) == 6 && len(strings.TrimSpace(parts[5])) > 0 {
r.saddr = net.ParseIP(parts[5]) r.saddr = net.ParseIP(parts[5])
@ -439,7 +445,7 @@ func savePolicy(f *os.File, p *Policy) {
if !p.hasPersistentRules() { if !p.hasPersistentRules() {
return return
} }
log.Warningf("p.path: ",p.path) log.Warningf("p.path: ", p.path)
if !writeLine(f, "["+p.sandbox+"|"+p.path+"]") { if !writeLine(f, "["+p.sandbox+"|"+p.path+"]") {
return return
} }
@ -495,7 +501,7 @@ func (fw *Firewall) loadRules() {
func (fw *Firewall) processPathLine(line string) *Policy { func (fw *Firewall) processPathLine(line string) *Policy {
pathLine := line[1 : len(line)-1] pathLine := line[1 : len(line)-1]
toks := strings.Split(pathLine, "|") toks := strings.Split(pathLine, "|")
policy := fw.policyForPathAndSandbox(toks[1],toks[0]) policy := fw.policyForPathAndSandbox(toks[1], toks[0])
policy.lock.Lock() policy.lock.Lock()
defer policy.lock.Unlock() defer policy.lock.Unlock()
policy.rules = nil policy.rules = nil

@ -241,7 +241,13 @@ func Main() {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
config, err := loadSocksConfiguration(defaultSocksCfgPath) scfile := os.Getenv("SGFW_SOCKS_CONFIG")
if scfile == "" {
scfile = defaultSocksCfgPath
}
config, err := loadSocksConfiguration(scfile)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
panic(err) panic(err)
} }
@ -250,7 +256,7 @@ func Main() {
chain := NewSocksChain(socksConfig, &wg, fw) chain := NewSocksChain(socksConfig, &wg, fw)
chain.start() chain.start()
} else { } else {
log.Notice("Did not find SOCKS5 configuration file at", defaultSocksCfgPath, "; ignoring subsystem...") log.Notice("Did not find SOCKS5 configuration file at", scfile, "; ignoring subsystem...")
} }
dbusp, err = newDbusObjectPrompt() dbusp, err = newDbusObjectPrompt()

@ -8,8 +8,8 @@ import (
"time" "time"
"github.com/subgraph/go-procsnitch" "github.com/subgraph/go-procsnitch"
"strings"
"strconv" "strconv"
"strings"
) )
type socksChainConfig struct { type socksChainConfig struct {
@ -17,7 +17,7 @@ type socksChainConfig struct {
TargetSocksAddr string TargetSocksAddr string
ListenSocksNet string ListenSocksNet string
ListenSocksAddr string ListenSocksAddr string
Name string Name string
} }
type socksChain struct { type socksChain struct {
@ -36,33 +36,37 @@ type socksChainSession struct {
bndAddr *Address bndAddr *Address
optData []byte optData []byte
procInfo procsnitch.ProcInfo procInfo procsnitch.ProcInfo
pinfo *procsnitch.Info pinfo *procsnitch.Info
server *socksChain server *socksChain
} }
const ( const (
socksVerdictDrop = 1 socksVerdictDrop = 1
socksVerdictAccept = 2 socksVerdictAccept = 2
socksVerdictAcceptTLSOnly = 3 socksVerdictAcceptTLSOnly = 3
) )
type pendingSocksConnection struct { type pendingSocksConnection struct {
pol *Policy pol *Policy
hname string hname string
srcIP net.IP srcIP net.IP
destIP net.IP destIP net.IP
sourcePort uint16 sourcePort uint16
destPort uint16 destPort uint16
pinfo *procsnitch.Info pinfo *procsnitch.Info
verdict chan int verdict chan int
prompting bool prompting bool
optstr string optstr string
} }
func (sc *pendingSocksConnection) sandbox() string { func (sc *pendingSocksConnection) sandbox() string {
return sc.pinfo.Sandbox return sc.pinfo.Sandbox
} }
func (sc *pendingSocksConnection) socks() bool {
return true
}
func (sc *pendingSocksConnection) policy() *Policy { func (sc *pendingSocksConnection) policy() *Policy {
return sc.pol return sc.pol
} }
@ -97,15 +101,21 @@ func (sc *pendingSocksConnection) src() net.IP {
} }
func (sc *pendingSocksConnection) deliverVerdict(v int) { func (sc *pendingSocksConnection) deliverVerdict(v int) {
defer func() {
if r := recover(); r != nil {
log.Warning("SOCKS5 server recovered from panic while delivering firewall verdict:", r)
}
}()
sc.verdict <- v sc.verdict <- v
close(sc.verdict) close(sc.verdict)
} }
func (sc *pendingSocksConnection) accept() { sc.deliverVerdict(socksVerdictAccept) } func (sc *pendingSocksConnection) accept() { sc.deliverVerdict(socksVerdictAccept) }
// need to generalize special accept // need to generalize special accept
func (sc *pendingSocksConnection) acceptTLSOnly() {sc.deliverVerdict(socksVerdictAcceptTLSOnly) } func (sc *pendingSocksConnection) acceptTLSOnly() { sc.deliverVerdict(socksVerdictAcceptTLSOnly) }
func (sc *pendingSocksConnection) drop() { sc.deliverVerdict(socksVerdictDrop) } func (sc *pendingSocksConnection) drop() { sc.deliverVerdict(socksVerdictDrop) }
@ -172,7 +182,7 @@ func (c *socksChainSession) sessionWorker() {
if len(c.req.Auth.Uname) == 0 && len(c.req.Auth.Passwd) == 0 { if len(c.req.Auth.Uname) == 0 && len(c.req.Auth.Passwd) == 0 {
// Randomize username and password to force a new TOR circuit with each connection // Randomize username and password to force a new TOR circuit with each connection
rndbytes := []byte("sgfw" + strconv.Itoa(int(time.Now().UnixNano()) ^ os.Getpid())) rndbytes := []byte("sgfw" + strconv.Itoa(int(time.Now().UnixNano())^os.Getpid()))
c.req.Auth.Uname = rndbytes c.req.Auth.Uname = rndbytes
c.req.Auth.Passwd = rndbytes c.req.Auth.Passwd = rndbytes
} }
@ -230,7 +240,7 @@ func findProxyEndpoint(pdata []string, conn net.Conn) (*procsnitch.Info, string)
s1, d1, s2, d2 := toks[0], toks[2], toks[3], toks[5] s1, d1, s2, d2 := toks[0], toks[2], toks[3], toks[5]
if strings.HasSuffix(d1, ",") { if strings.HasSuffix(d1, ",") {
d1 = d1[0:len(d1)-1] d1 = d1[0 : len(d1)-1]
} }
if conn.LocalAddr().String() == d2 && conn.RemoteAddr().String() == s2 { if conn.LocalAddr().String() == d2 && conn.RemoteAddr().String() == s2 {
@ -275,14 +285,21 @@ func (c *socksChainSession) filterConnect() (bool, bool) {
var pinfo *procsnitch.Info = nil var pinfo *procsnitch.Info = nil
var optstr = "" var optstr = ""
// try to find process via oz-daemon known proxy endpoints
if err == nil { if err == nil {
pinfo, optstr = findProxyEndpoint(allProxies, c.clientConn) pinfo, optstr = findProxyEndpoint(allProxies, c.clientConn)
} }
// fall back to system-wide processes
if pinfo == nil { if pinfo == nil {
pinfo = procsnitch.FindProcessForConnection(c.clientConn, c.procInfo) pinfo = procsnitch.FindProcessForConnection(c.clientConn, c.procInfo)
} }
// connection maybe doesn't exist anymore
if pinfo == nil { if pinfo == nil {
log.Warningf("No proc found for [socks5] connection from: %s", c.clientConn.RemoteAddr()) log.Warningf("No proc found for [socks5] connection from: %s", c.clientConn.RemoteAddr())
return false, false return false, false
@ -296,15 +313,13 @@ func (c *socksChainSession) filterConnect() (bool, bool) {
optstr = "[Via SOCKS5: " + c.cfg.Name + "] " + optstr optstr = "[Via SOCKS5: " + c.cfg.Name + "] " + optstr
} }
log.Warningf("Lookup policy for %v %v",pinfo.ExePath,pinfo.Sandbox) policy := c.server.fw.PolicyForPathAndSandbox(GetRealRoot(pinfo.ExePath, pinfo.Pid), pinfo.Sandbox)
policy := c.server.fw.PolicyForPathAndSandbox(GetRealRoot(pinfo.ExePath,pinfo.Pid),pinfo.Sandbox)
hostname, ip, port := c.addressDetails() hostname, ip, port := c.addressDetails()
if ip == nil && hostname == "" { if ip == nil && hostname == "" {
return false, false return false, false
} }
result := policy.rules.filter(nil, nil, ip, port, hostname, pinfo, optstr) result := policy.rules.filter(nil, nil, ip, port, hostname, pinfo, optstr)
log.Errorf("result %v",result)
switch result { switch result {
case FILTER_DENY: case FILTER_DENY:
return false, false return false, false
@ -315,7 +330,7 @@ func (c *socksChainSession) filterConnect() (bool, bool) {
case FILTER_PROMPT: case FILTER_PROMPT:
caddr := c.clientConn.RemoteAddr().String() caddr := c.clientConn.RemoteAddr().String()
caddrt := strings.Split(caddr, ":") caddrt := strings.Split(caddr, ":")
caddrIP := net.IP{0,0,0,0} caddrIP := net.IP{0, 0, 0, 0}
caddrPort := uint16(0) caddrPort := uint16(0)
if len(caddrt) != 2 { if len(caddrt) != 2 {
@ -392,9 +407,9 @@ func (c *socksChainSession) forwardTraffic(tls bool) {
if err != nil { if err != nil {
if c.pinfo.Sandbox != "" { if c.pinfo.Sandbox != "" {
log.Errorf("Dropping traffic from %s (sandbox: %s) to %s due to TLSGuard violation: %v", c.pinfo.ExePath, c.pinfo.Sandbox, c.req.Addr.addrStr, err) log.Errorf("TLSGuard violation: Dropping traffic from %s (sandbox: %s) to %s: %v", c.pinfo.ExePath, c.pinfo.Sandbox, c.req.Addr.addrStr, err)
} else { } else {
log.Errorf("Dropping traffic from %s (unsandboxed) to %s due to TLSGuard violation: %v", c.pinfo.ExePath, c.req.Addr.addrStr, err) log.Errorf("TLSGuard violation: Dropping traffic from %s (unsandboxed) to %s: %v", c.pinfo.ExePath, c.req.Addr.addrStr, err)
} }
return return
} else { } else {

@ -2,15 +2,14 @@ package sgfw
import ( import (
"crypto/x509" "crypto/x509"
"errors"
"io" "io"
"net" "net"
"errors"
) )
func TLSGuard(conn, conn2 net.Conn, fqdn string) error { func TLSGuard(conn, conn2 net.Conn, fqdn string) error {
// Should this be a requirement? // Should this be a requirement?
// if strings.HasSuffix(request.DestAddr.FQDN, "onion") { // if strings.HasSuffix(request.DestAddr.FQDN, "onion") {
handshakeByte, err := readNBytes(conn, 1) handshakeByte, err := readNBytes(conn, 1)
if err != nil { if err != nil {
@ -118,7 +117,7 @@ func TLSGuard(conn, conn2 net.Conn, fqdn string) error {
for remaining > 0 { for remaining > 0 {
certLen := int(int(pos[0])<<16 | int(pos[1])<<8 | int(pos[2])) certLen := int(int(pos[0])<<16 | int(pos[1])<<8 | int(pos[2]))
// fmt.Printf("Certs chain len %d, cert 1 len %d:\n", certChainLen, certLen) // fmt.Printf("Certs chain len %d, cert 1 len %d:\n", certChainLen, certLen)
cert := pos[3 : 3+certLen] cert := pos[3 : 3+certLen]
certs, err := x509.ParseCertificates(cert) certs, err := x509.ParseCertificates(cert)
if remaining == certChainLen { if remaining == certChainLen {
@ -143,18 +142,18 @@ func TLSGuard(conn, conn2 net.Conn, fqdn string) error {
} else { } else {
valid = true valid = true
} }
// else if s == 0x0d { fmt.Printf("found a client cert request, sending buf to client\n") } // else if s == 0x0d { fmt.Printf("found a client cert request, sending buf to client\n") }
} else if s == 0x0e { } else if s == 0x0e {
sendToClient = true sendToClient = true
} else if s == 0x0d { } else if s == 0x0d {
sendToClient = true sendToClient = true
} }
// fmt.Printf("Version bytes: %x %x\n", responseBuf[1], responseBuf[2]) // fmt.Printf("Version bytes: %x %x\n", responseBuf[1], responseBuf[2])
// fmt.Printf("Len bytes: %x %x\n", responseBuf[3], responseBuf[4]) // fmt.Printf("Len bytes: %x %x\n", responseBuf[3], responseBuf[4])
// fmt.Printf("Message type: %x\n", responseBuf[5]) // fmt.Printf("Message type: %x\n", responseBuf[5])
// fmt.Printf("Message len: %x %x %x\n", responseBuf[6], responseBuf[7], responseBuf[8]) // fmt.Printf("Message len: %x %x %x\n", responseBuf[6], responseBuf[7], responseBuf[8])
// fmt.Printf("Message body: %v\n", responseBuf[9:]) // fmt.Printf("Message body: %v\n", responseBuf[9:])
conn.Write(responseBuf) conn.Write(responseBuf)
responseBuf = []byte{} responseBuf = []byte{}
} }

@ -0,0 +1,5 @@
log_level="NOTICE"
log_redact=false
prompt_expanded=true
prompt_expert=true
default_action="SESSION"

@ -1,11 +0,0 @@
[Unit]
Documentation=https://github.com/subgraph/fw-daemon
Description=Subgraph Firewall Daemon
[Service]
Environment="GODEBUG=cgocheck=0"
ExecStartPre=/usr/bin/install -d /var/run/fw-daemon
ExecStart=/usr/sbin/fw-daemon
[Install]
WantedBy=multi-user.target

@ -1,5 +1,4 @@
[Desktop Entry] [Desktop Entry]
Encoding=UTF-8
Name=Subgraph Firewall Settings Name=Subgraph Firewall Settings
Comment=Launch the Subgraph Firewall Settings Comment=Launch the Subgraph Firewall Settings
TryExec=fw-settings TryExec=fw-settings
@ -7,4 +6,5 @@ Exec=fw-settings
Terminal=false Terminal=false
Type=Application Type=Application
Icon=security-medium Icon=security-medium
Categories=Network;Admin; Categories=Security;Settings;
X-Desktop-File-Install-Version=0.23

@ -0,0 +1,38 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
#*
*~
# examples binaries
examples/synscan/synscan
examples/pfdump/pfdump
examples/pcapdump/pcapdump
examples/httpassembly/httpassembly
examples/statsassembly/statsassembly
examples/arpscan/arpscan
examples/bidirectional/bidirectional
examples/bytediff/bytediff
examples/reassemblydump/reassemblydump
layers/gen
macs/gen
pcap/pcap_tester

@ -0,0 +1,7 @@
#!/bin/bash
cd "$(dirname $0)"
if [ -n "$(go fmt ./...)" ]; then
echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
exit 1
fi

@ -0,0 +1,25 @@
#!/bin/bash
cd "$(dirname $0)"
go get github.com/golang/lint/golint
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing"
# Add subdirectories here as we clean up golint on each.
for subdir in $DIRS; do
pushd $subdir
if golint |
grep -v CannotSetRFMon | # pcap exported error name
grep -v DataLost | # tcpassembly/tcpreader exported error name
grep .; then
exit 1
fi
popd
done
pushd layers
for file in $(cat .linted); do
if golint $file | grep .; then
exit 1
fi
done
popd

@ -0,0 +1,10 @@
#!/bin/bash
cd "$(dirname $0)"
DIRS=". layers pcap pcapgo pfring tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs"
set -e
for subdir in $DIRS; do
pushd $subdir
go vet
popd
done

@ -0,0 +1,14 @@
language: go
install:
- go get github.com/google/gopacket
- go get github.com/google/gopacket/layers
- go get github.com/google/gopacket/tcpassembly
- go get github.com/google/gopacket/reassembly
script:
- go test github.com/google/gopacket
- go test github.com/google/gopacket/layers
- go test github.com/google/gopacket/tcpassembly
- go test github.com/google/gopacket/reassembly
- ./.travis.gofmt.sh
- ./.travis.govet.sh
- ./.travis.golint.sh

@ -0,0 +1,46 @@
AUTHORS AND MAINTAINERS:
MAIN DEVELOPERS:
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
AUTHORS:
Nigel Tao <nigeltao@google.com>
Cole Mickens <cole.mickens@gmail.com>
Ben Daglish <bdaglish@restorepoint.com>
Luis Martinez <martinezlc99@gmail.com>
Remco Verhoef <remco@dutchcoders.io>
Hiroaki Kawai <Hiroaki.Kawai@gmail.com>
Lukas Lueg <lukas.lueg@gmail.com>
Laurent Hausermann <laurent.hausermann@gmail.com>
Bill Green <bgreen@newrelic.com>
CONTRIBUTORS:
Attila Oláh <attila@attilaolah.eu>
Vittus Mikiassen <matt.miki.vimik@gmail.com>
Matthias Radestock <matthias.radestock@gmail.com>
Matthew Sackman <matthew@wellquite.org>
Loic Prylli <loicp@google.com>
Alexandre Fiori <fiorix@gmail.com>
Adrian Tam <adrian.c.m.tam@gmail.com>
Satoshi Matsumoto <kaorimatz@gmail.com>
David Stainton <dstainton415@gmail.com>
Jesse Ward <jesse@jesseward.com>
Kane Mathers <kane@kanemathers.name>
-----------------------------------------------
FORKED FROM github.com/akrennmair/gopcap
ALL THE FOLLOWING ARE FOR THAT PROJECT
MAIN DEVELOPERS:
Andreas Krennmair <ak@synflood.at>
CONTRIBUTORS:
Andrea Nall <anall@andreanall.com>
Daniel Arndt <danielarndt@gmail.com>
Dustin Sallings <dustin@spy.net>
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
Guillaume Savary <guillaume@savary.name>
Mark Smith <mark@qq.is>
Miek Gieben <miek@miek.nl>
Mike Bell <mike@mikebell.org>
Trevor Strohman <strohman@google.com>

@ -0,0 +1,215 @@
Contributing To gopacket
========================
So you've got some code and you'd like it to be part of gopacket... wonderful!
We're happy to accept contributions, whether they're fixes to old protocols, new
protocols entirely, or anything else you think would improve the gopacket
library. This document is designed to help you to do just that.
The first section deals with the plumbing: how to actually get a change
submitted.
The second section deals with coding style... Go is great in that it
has a uniform style implemented by 'go fmt', but there's still some decisions
we've made that go above and beyond, and if you follow them, they won't come up
in your code review.
The third section deals with some of the implementation decisions we've made,
which may help you to understand the current code and which we may ask you to
conform to (or provide compelling reasons for ignoring).
Overall, we hope this document will help you to understand our system and write
great code which fits in, and help us to turn around on your code review quickly
so the code can make it into the master branch as quickly as possible.
How To Submit Code
------------------
We use github.com's Pull Request feature to receive code contributions from
external contributors. See
https://help.github.com/articles/creating-a-pull-request/ for details on
how to create a request.
Also, there's a local script `gc` in the base directory of GoPacket that
runs a local set of checks, which should give you relatively high confidence
that your pull won't fail github pull checks.
```sh
go get github.com/google/gopacket
cd $GOROOT/src/pkg/github.com/google/gopacket
git checkout -b <mynewfeature> # create a new branch to work from
... code code code ...
./gc # Run this to do local commits, it performs a number of checks
```
To sum up:
* DO
+ Pull down the latest version.
+ Make a feature-specific branch.
+ Code using the style and methods discussed in the rest of this document.
+ Use the ./gc command to do local commits or check correctness.
+ Push your new feature branch up to github.com, as a pull request.
+ Handle comments and requests from reviewers, pushing new commits up to
your feature branch as problems are addressed.
+ Put interesting comments and discussions into commit comments.
* DON'T
+ Push to someone else's branch without their permission.
Coding Style
------------
* Go code must be run through `go fmt`, `go vet`, and `golint`
* Follow http://golang.org/doc/effective_go.html as much as possible.
+ In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums
should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs.
TcpSourcePort or TCP_SOURCE_PORT).
* Bonus points for giving enum types a String() field.
* Any exported types or functions should have commentary
(http://golang.org/doc/effective_go.html#commentary)
Coding Methods And Implementation Notes
---------------------------------------
### Error Handling
Many times, you'll be decoding a protocol and run across something bad, a packet
corruption or the like. How do you handle this? First off, ALWAYS report the
error. You can do this either by returning the error from the decode() function
(most common), or if you're up for it you can implement and add an ErrorLayer
through the packet builder (the first method is a simple shortcut that does
exactly this, then stops any future decoding).
Often, you'll already have decode some part of your protocol by the time you hit
your error. Use your own discretion to determine whether the stuff you've
already decoded should be returned to the caller or not:
```go
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
prot := &MyProtocol{}
if len(data) < 10 {
// This error occurred before we did ANYTHING, so there's nothing in my
// protocol that the caller could possibly want. Just return the error.
return fmt.Errorf("Length %d less than 10", len(data))
}
prot.ImportantField1 = data[:5]
prot.ImportantField2 = data[5:10]
// At this point, we've already got enough information in 'prot' to
// warrant returning it to the caller, so we'll add it now.
p.AddLayer(prot)
if len(data) < 15 {
// We encountered an error later in the packet, but the caller already
// has the important info we've gleaned so far.
return fmt.Errorf("Length %d less than 15", len(data))
}
prot.ImportantField3 = data[10:15]
return nil // We've already added the layer, we can just return success.
}
```
In general, our code follows the approach of returning the first error it
encounters. In general, we don't trust any bytes after the first error we see.
### What Is A Layer?
The definition of a layer is up to the discretion of the coder. It should be
something important enough that it's actually useful to the caller (IE: every
TLV value should probably NOT be a layer). However, it can be more granular
than a single protocol... IPv6 and SCTP both implement many layers to handle the
various parts of the protocol. Use your best judgement, and prepare to defend
your decisions during code review. ;)
### Performance
We strive to make gopacket as fast as possible while still providing lots of
features. In general, this means:
* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize
others on an as-needed basis (tons of MPLS on your network? Time to optimize
MPLS!)
* Use fast operations. See the toplevel benchmark_test for benchmarks of some
of Go's underlying features and types.
* Test your performance changes! You should use the ./gc script's --benchmark
flag to submit any performance-related changes. Use pcap/gopacket_benchmark
to test your change against a PCAP file based on your traffic patterns.
* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes
a huge performance hit, due to the way that Go currently handles its segmented
stack... don't be afraid to clean it up anyway. We'll trust the Go compiler
to get good enough over time to handle this. Also, this type of
compiler-specific optimization is very fragile; someone adding a field to an
entirely different struct elsewhere in the codebase could reverse any gains
you might achieve by aligning your allocations.
* Try to minimize memory allocations. If possible, use []byte to reference
pieces of the input, instead of using string, which requires copying the bytes
into a new memory allocation.
* Think hard about what should be evaluated lazily vs. not. In general, a
layer's struct should almost exactly mirror the layer's frame. Anything
that's more interesting should be a function. This may not always be
possible, but it's a good rule of thumb.
* Don't fear micro-optimizations. With the above in mind, we welcome
micro-optimizations that we think will have positive/neutral impacts on the
majority of workloads. A prime example of this is pre-allocating certain
structs within a larger one:
```go
type MyProtocol struct {
// Most packets have 1-4 of VeryCommon, so we preallocate it here.
initialAllocation [4]uint32
VeryCommon []uint32
}
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
prot := &MyProtocol{}
prot.VeryCommon = proto.initialAllocation[:0]
for len(data) > 4 {
field := binary.BigEndian.Uint32(data[:4])
data = data[4:]
// Since we're using the underlying initialAllocation, we won't need to
// allocate new memory for the following append unless we more than 16
// bytes of data, which should be the uncommon case.
prot.VeryCommon = append(prot.VeryCommon, field)
}
p.AddLayer(prot)
if len(data) > 0 {
return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data))
}
return nil
}
```
### Slices And Data
If you're pulling a slice from the data you're decoding, don't copy it. Just
use the slice itself.
```go
type MyProtocol struct {
A, B net.IP
}
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
p.AddLayer(&MyProtocol{
A: data[:4],
B: data[4:8],
})
return nil
}
```
The caller has already agreed, by using this library, that they won't modify the
set of bytes they pass in to the decoder, or the library has already copied the
set of bytes to a read-only location. See DecodeOptions.NoCopy for more
information.
### Enums/Types
If a protocol has an integer field (uint8, uint16, etc) with a couple of known
values that mean something special, make it a type. This allows us to do really
nice things like adding a String() function to them, so we can more easily
display those to users. Check out layers/enums.go for one example, as well as
layers/icmp.go for layer-specific enums.
When naming things, try for descriptiveness over suscinctness. For example,
choose DNSResponseRecord over DNSRR.

@ -0,0 +1,28 @@
Copyright (c) 2012 Google, Inc. All rights reserved.
Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Andreas Krennmair, Google, nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,10 @@
# GoPacket
This library provides packet decoding capabilities for Go.
See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket)
[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket)
Originally forked from the gopcap project written by Andreas
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).

@ -0,0 +1,178 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
import (
"fmt"
)
// Layer represents a single decoded packet layer (using either the
// OSI or TCP/IP definition of a layer). When decoding, a packet's data is
// broken up into a number of layers. The caller may call LayerType() to
// figure out which type of layer they've received from the packet. Optionally,
// they may then use a type assertion to get the actual layer type for deep
// inspection of the data.
type Layer interface {
// LayerType is the gopacket type for this layer.
LayerType() LayerType
// LayerContents returns the set of bytes that make up this layer.
LayerContents() []byte
// LayerPayload returns the set of bytes contained within this layer, not
// including the layer itself.
LayerPayload() []byte
}
// Payload is a Layer containing the payload of a packet. The definition of
// what constitutes the payload of a packet depends on previous layers; for
// TCP and UDP, we stop decoding above layer 4 and return the remaining
// bytes as a Payload. Payload is an ApplicationLayer.
type Payload []byte
// LayerType returns LayerTypePayload
func (p Payload) LayerType() LayerType { return LayerTypePayload }
// LayerContents returns the bytes making up this layer.
func (p Payload) LayerContents() []byte { return []byte(p) }
// LayerPayload returns the payload within this layer.
func (p Payload) LayerPayload() []byte { return nil }
// Payload returns this layer as bytes.
func (p Payload) Payload() []byte { return []byte(p) }
// String implements fmt.Stringer.
func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) }
// GoString implements fmt.GoStringer.
func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) }
// CanDecode implements DecodingLayer.
func (p Payload) CanDecode() LayerClass { return LayerTypePayload }
// NextLayerType implements DecodingLayer.
func (p Payload) NextLayerType() LayerType { return LayerTypeZero }
// DecodeFromBytes implements DecodingLayer.
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
*p = Payload(data)
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
bytes, err := b.PrependBytes(len(p))
if err != nil {
return err
}
copy(bytes, p)
return nil
}
// decodePayload decodes data by returning it all in a Payload layer.
func decodePayload(data []byte, p PacketBuilder) error {
payload := &Payload{}
if err := payload.DecodeFromBytes(data, p); err != nil {
return nil
}
p.AddLayer(payload)
p.SetApplicationLayer(payload)
return nil
}
// Fragment is a Layer containing a fragment of a larger frame, used by layers
// like IPv4 and IPv6 that allow for fragmentation of their payloads.
type Fragment []byte
// LayerType returns LayerTypeFragment
func (p *Fragment) LayerType() LayerType { return LayerTypeFragment }
// LayerContents implements Layer.
func (p *Fragment) LayerContents() []byte { return []byte(*p) }
// LayerPayload implements Layer.
func (p *Fragment) LayerPayload() []byte { return nil }
// Payload returns this layer as a byte slice.
func (p *Fragment) Payload() []byte { return []byte(*p) }
// String implements fmt.Stringer.
func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) }
// CanDecode implements DecodingLayer.
func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment }
// NextLayerType implements DecodingLayer.
func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero }
// DecodeFromBytes implements DecodingLayer.
func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error {
*p = Fragment(data)
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
bytes, err := b.PrependBytes(len(*p))
if err != nil {
return err
}
copy(bytes, *p)
return nil
}
// decodeFragment decodes data by returning it all in a Fragment layer.
func decodeFragment(data []byte, p PacketBuilder) error {
payload := &Fragment{}
if err := payload.DecodeFromBytes(data, p); err != nil {
return nil
}
p.AddLayer(payload)
p.SetApplicationLayer(payload)
return nil
}
// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
// corresponding OSI layers, as best as possible.
// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
type LinkLayer interface {
Layer
LinkFlow() Flow
}
// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
// layer 3)
type NetworkLayer interface {
Layer
NetworkFlow() Flow
}
// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
// layer 4)
type TransportLayer interface {
Layer
TransportFlow() Flow
}
// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
// layer 7), also known as the packet payload.
type ApplicationLayer interface {
Layer
Payload() []byte
}
// ErrorLayer is a packet layer created when decoding of the packet has failed.
// Its payload is all the bytes that we were unable to decode, and the returned
// error details why the decoding failed.
type ErrorLayer interface {
Layer
Error() error
}

@ -0,0 +1,157 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
import (
"errors"
)
// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata.
type DecodeFeedback interface {
// SetTruncated should be called if during decoding you notice that a packet
// is shorter than internal layer variables (HeaderLength, or the like) say it
// should be. It sets packet.Metadata().Truncated.
SetTruncated()
}
type nilDecodeFeedback struct{}
func (nilDecodeFeedback) SetTruncated() {}
// NilDecodeFeedback implements DecodeFeedback by doing nothing.
var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{}
// PacketBuilder is used by layer decoders to store the layers they've decoded,
// and to defer future decoding via NextDecoder.
// Typically, the pattern for use is:
// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error {
// if myLayer, err := myDecodingLogic(data); err != nil {
// return err
// } else {
// p.AddLayer(myLayer)
// }
// // maybe do this, if myLayer is a LinkLayer
// p.SetLinkLayer(myLayer)
// return p.NextDecoder(nextDecoder)
// }
type PacketBuilder interface {
DecodeFeedback
// AddLayer should be called by a decoder immediately upon successful
// decoding of a layer.
AddLayer(l Layer)
// The following functions set the various specific layers in the final
// packet. Note that if many layers call SetX, the first call is kept and all
// other calls are ignored.
SetLinkLayer(LinkLayer)
SetNetworkLayer(NetworkLayer)
SetTransportLayer(TransportLayer)
SetApplicationLayer(ApplicationLayer)
SetErrorLayer(ErrorLayer)
// NextDecoder should be called by a decoder when they're done decoding a
// packet layer but not done with decoding the entire packet. The next
// decoder will be called to decode the last AddLayer's LayerPayload.
// Because of this, NextDecoder must only be called once all other
// PacketBuilder calls have been made. Set*Layer and AddLayer calls after
// NextDecoder calls will behave incorrectly.
NextDecoder(next Decoder) error
// DumpPacketData is used solely for decoding. If you come across an error
// you need to diagnose while processing a packet, call this and your packet's
// data will be dumped to stderr so you can create a test. This should never
// be called from a production decoder.
DumpPacketData()
// DecodeOptions returns the decode options
DecodeOptions() *DecodeOptions
}
// Decoder is an interface for logic to decode a packet layer. Users may
// implement a Decoder to handle their own strange packet types, or may use one
// of the many decoders available in the 'layers' subpackage to decode things
// for them.
type Decoder interface {
// Decode decodes the bytes of a packet, sending decoded values and other
// information to PacketBuilder, and returning an error if unsuccessful. See
// the PacketBuilder documentation for more details.
Decode([]byte, PacketBuilder) error
}
// DecodeFunc wraps a function to make it a Decoder.
type DecodeFunc func([]byte, PacketBuilder) error
// Decode implements Decoder by calling itself.
func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error {
// function, call thyself.
return d(data, p)
}
// DecodePayload is a Decoder that returns a Payload layer containing all
// remaining bytes.
var DecodePayload Decoder = DecodeFunc(decodePayload)
// DecodeUnknown is a Decoder that returns an Unknown layer containing all
// remaining bytes, useful if you run up against a layer that you're unable to
// decode yet. This layer is considered an ErrorLayer.
var DecodeUnknown Decoder = DecodeFunc(decodeUnknown)
// DecodeFragment is a Decoder that returns a Fragment layer containing all
// remaining bytes.
var DecodeFragment Decoder = DecodeFunc(decodeFragment)
// LayerTypeZero is an invalid layer type, but can be used to determine whether
// layer type has actually been set correctly.
var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown})
// LayerTypeDecodeFailure is the layer type for the default error layer.
var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown})
// LayerTypePayload is the layer type for a payload that we don't try to decode
// but treat as a success, IE: an application-level payload.
var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload})
// LayerTypeFragment is the layer type for a fragment of a layer transported
// by an underlying layer that supports fragmentation.
var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment})
// DecodeFailure is a packet layer created if decoding of the packet data failed
// for some reason. It implements ErrorLayer. LayerContents will be the entire
// set of bytes that failed to parse, and Error will return the reason parsing
// failed.
type DecodeFailure struct {
data []byte
err error
stack []byte
}
// Error returns the error encountered during decoding.
func (d *DecodeFailure) Error() error { return d.err }
// LayerContents implements Layer.
func (d *DecodeFailure) LayerContents() []byte { return d.data }
// LayerPayload implements Layer.
func (d *DecodeFailure) LayerPayload() []byte { return nil }
// String implements fmt.Stringer.
func (d *DecodeFailure) String() string {
return "Packet decoding error: " + d.Error().Error()
}
// Dump implements Dumper.
func (d *DecodeFailure) Dump() (s string) {
if d.stack != nil {
s = string(d.stack)
}
return
}
// LayerType returns LayerTypeDecodeFailure
func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure }
// decodeUnknown "decodes" unsupported data types by returning an error.
// This decoder will thus always return a DecodeFailure layer.
func decodeUnknown(data []byte, p PacketBuilder) error {
return errors.New("Layer type not currently supported")
}

@ -0,0 +1,365 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
/*
Package gopacket provides packet decoding for the Go language.
gopacket contains many sub-packages with additional functionality you may find
useful, including:
* layers: You'll probably use this every time. This contains of the logic
built into gopacket for decoding packet protocols. Note that all example
code below assumes that you have imported both gopacket and
gopacket/layers.
* pcap: C bindings to use libpcap to read packets off the wire.
* pfring: C bindings to use PF_RING to read packets off the wire.
* afpacket: C bindings for Linux's AF_PACKET to read packets off the wire.
* tcpassembly: TCP stream reassembly
Also, if you're looking to dive right into code, see the examples subdirectory
for numerous simple binaries built using gopacket libraries.
Basic Usage
gopacket takes in packet data as a []byte and decodes it into a packet with
a non-zero number of "layers". Each layer corresponds to a protocol
within the bytes. Once a packet has been decoded, the layers of the packet
can be requested from the packet.
// Decode a packet
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
// Get the TCP layer from this packet
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
fmt.Println("This is a TCP packet!")
// Get actual TCP data from this layer
tcp, _ := tcpLayer.(*layers.TCP)
fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort)
}
// Iterate over all layers, printing out each layer type
for _, layer := range packet.Layers() {
fmt.Println("PACKET LAYER:", layer.LayerType())
}
Packets can be decoded from a number of starting points. Many of our base
types implement Decoder, which allow us to decode packets for which
we don't have full data.
// Decode an ethernet packet
ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default)
// Decode an IPv6 header and everything it contains
ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default)
// Decode a TCP header and its payload
tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default)
Reading Packets From A Source
Most of the time, you won't just have a []byte of packet data lying around.
Instead, you'll want to read packets in from somewhere (file, interface, etc)
and process them. To do that, you'll want to build a PacketSource.
First, you'll need to construct an object that implements the PacketDataSource
interface. There are implementations of this interface bundled with gopacket
in the gopacket/pcap and gopacket/pfring subpackages... see their documentation
for more information on their usage. Once you have a PacketDataSource, you can
pass it into NewPacketSource, along with a Decoder of your choice, to create
a PacketSource.
Once you have a PacketSource, you can read packets from it in multiple ways.
See the docs for PacketSource for more details. The easiest method is the
Packets function, which returns a channel, then asynchronously writes new
packets into that channel, closing the channel if the packetSource hits an
end-of-file.
packetSource := ... // construct using pcap or pfring
for packet := range packetSource.Packets() {
handlePacket(packet) // do something with each packet
}
You can change the decoding options of the packetSource by setting fields in
packetSource.DecodeOptions... see the following sections for more details.
Lazy Decoding
gopacket optionally decodes packet data lazily, meaning it
only decodes a packet layer when it needs to handle a function call.
// Create a packet, but don't actually decode anything yet
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
// Now, decode the packet up to the first IPv4 layer found but no further.
// If no IPv4 layer was found, the whole packet will be decoded looking for
// it.
ip4 := packet.Layer(layers.LayerTypeIPv4)
// Decode all layers and return them. The layers up to the first IPv4 layer
// are already decoded, and will not require decoding a second time.
layers := packet.Layers()
Lazily-decoded packets are not concurrency-safe. Since layers have not all been
decoded, each call to Layer() or Layers() has the potential to mutate the packet
in order to decode the next layer. If a packet is used
in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket
will decode the packet fully, and all future function calls won't mutate the
object.
NoCopy Decoding
By default, gopacket will copy the slice passed to NewPacket and store the
copy within the packet, so future mutations to the bytes underlying the slice
don't affect the packet and its layers. If you can guarantee that the
underlying slice bytes won't be changed, you can use NoCopy to tell
gopacket.NewPacket, and it'll use the passed-in slice itself.
// This channel returns new byte slices, each of which points to a new
// memory location that's guaranteed immutable for the duration of the
// packet.
for data := range myByteSliceChannel {
p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
doSomethingWithPacket(p)
}
The fastest method of decoding is to use both Lazy and NoCopy, but note from
the many caveats above that for some implementations either or both may be
dangerous.
Pointers To Known Layers
During decoding, certain layers are stored in the packet as well-known
layer types. For example, IPv4 and IPv6 are both considered NetworkLayer
layers, while TCP and UDP are both TransportLayer layers. We support 4
layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly
anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these,
you can use the packet.LinkLayer, packet.NetworkLayer,
packet.TransportLayer, and packet.ApplicationLayer functions. Each of
these functions returns a corresponding interface
(gopacket.{Link,Network,Transport,Application}Layer). The first three
provide methods for getting src/dst addresses for that particular layer,
while the final layer provides a Payload function to get payload data.
This is helpful, for example, to get payloads for all packets regardless
of their underlying data type:
// Get packets from some source
for packet := range someSource {
if app := packet.ApplicationLayer(); app != nil {
if strings.Contains(string(app.Payload()), "magic string") {
fmt.Println("Found magic string in a packet!")
}
}
}
A particularly useful layer is ErrorLayer, which is set whenever there's
an error parsing part of the packet.
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
if err := packet.ErrorLayer(); err != nil {
fmt.Println("Error decoding some part of the packet:", err)
}
Note that we don't return an error from NewPacket because we may have decoded
a number of layers successfully before running into our erroneous layer. You
may still be able to get your Ethernet and IPv4 layers correctly, even if
your TCP layer is malformed.
Flow And Endpoint
gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol
independent manner the fact that a packet is coming from A and going to B.
The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide
methods for extracting their flow information, without worrying about the type
of the underlying Layer.
A Flow is a simple object made up of a set of two Endpoints, one source and one
destination. It details the sender and receiver of the Layer of the Packet.
An Endpoint is a hashable representation of a source or destination. For
example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4
IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined
into Flows:
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
netFlow := packet.NetworkLayer().NetworkFlow()
src, dst := netFlow.Endpoints()
reverseFlow := gopacket.NewFlow(dst, src)
Both Endpoint and Flow objects can be used as map keys, and the equality
operator can compare them, so you can easily group together all packets
based on endpoint criteria:
flows := map[gopacket.Endpoint]chan gopacket.Packet
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
// Send all TCP packets to channels based on their destination port.
if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil {
flows[tcp.TransportFlow().Dst()] <- packet
}
// Look for all packets with the same source and destination network address
if net := packet.NetworkLayer(); net != nil {
src, dst := net.NetworkFlow().Endpoints()
if src == dst {
fmt.Println("Fishy packet has same network source and dst: %s", src)
}
}
// Find all packets coming from UDP port 1000 to UDP port 500
interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow {
fmt.Println("Found that UDP flow I was looking for!")
}
For load-balancing purposes, both Flow and Endpoint have FastHash() functions,
which provide quick, non-cryptographic hashes of their contents. Of particular
importance is the fact that Flow FastHash() is symmetric: A->B will have the same
hash as B->A. An example usage could be:
channels := [8]chan gopacket.Packet
for i := 0; i < 8; i++ {
channels[i] = make(chan gopacket.Packet)
go packetHandler(channels[i])
}
for packet := range getPackets() {
if net := packet.NetworkLayer(); net != nil {
channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet
}
}
This allows us to split up a packet stream while still making sure that each
stream sees all packets for a flow (and its bidirectional opposite).
Implementing Your Own Decoder
If your network has some strange encapsulation, you can implement your own
decoder. In this example, we handle Ethernet packets which are encapsulated
in a 4-byte header.
// Create a layer type, should be unique and high, so it doesn't conflict,
// giving it a name and a decoder to use.
var MyLayerType = gopacket.RegisterLayerType(12345, "MyLayerType", gopacket.DecodeFunc(decodeMyLayer))
// Implement my layer
type MyLayer struct {
StrangeHeader []byte
payload []byte
}
func (m MyLayer) LayerType() LayerType { return MyLayerType }
func (m MyLayer) LayerContents() []byte { return m.StrangeHeader }
func (m MyLayer) LayerPayload() []byte { return m.payload }
// Now implement a decoder... this one strips off the first 4 bytes of the
// packet.
func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error {
// Create my layer
p.AddLayer(&MyLayer{data[:4], data[4:]})
// Determine how to handle the rest of the packet
return p.NextDecoder(layers.LayerTypeEthernet)
}
// Finally, decode your packets:
p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy)
See the docs for Decoder and PacketBuilder for more details on how coding
decoders works, or look at RegisterLayerType and RegisterEndpointType to see how
to add layer/endpoint types to gopacket.
Fast Decoding With DecodingLayerParser
TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode
packet data, but only for known packet stacks.
Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow
due to its need to allocate a new packet and every respective layer. It's very
versatile and can handle all known layer types, but sometimes you really only
care about a specific set of layers regardless, so that versatility is wasted.
DecodingLayerParser avoids memory allocation altogether by decoding packet
layers directly into preallocated objects, which you can then reference to get
the packet's information. A quick example:
func main() {
var eth layers.Ethernet
var ip4 layers.IPv4
var ip6 layers.IPv6
var tcp layers.TCP
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ip6, &tcp)
decoded := []gopacket.LayerType{}
for packetData := range somehowGetPacketData() {
err := parser.DecodeLayers(packetData, &decoded)
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeIPv6:
fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP)
case layers.LayerTypeIPv4:
fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP)
}
}
}
}
The important thing to note here is that the parser is modifying the passed in
layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly
speeding up the decoding process. It's even branching based on layer type...
it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't
handle any other type... since no other decoders were passed in, an (eth, ip4,
udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet,
LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't
decode a UDP packet).
Unfortunately, not all layers can be used by DecodingLayerParser... only those
implementing the DecodingLayer interface are usable. Also, it's possible to
create DecodingLayers that are not themselves Layers... see
layers.IPv6ExtensionSkipper for an example of this.
Creating Packet Data
As well as offering the ability to decode packet data, gopacket will allow you
to create packets from scratch, as well. A number of gopacket layers implement
the SerializableLayer interface; these layers can be serialized to a []byte in
the following manner:
ip := &layers.IPv4{
SrcIP: net.IP{1, 2, 3, 4},
DstIP: net.IP{5, 6, 7, 8},
// etc...
}
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{} // See SerializeOptions for more details.
err := ip.SerializeTo(&buf, opts)
if err != nil { panic(err) }
fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer.
SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat
the current buffer's Bytes() slice as the payload of the serializing layer.
Therefore, you can serialize an entire packet by serializing a set of layers in
reverse order (Payload, then TCP, then IP, then Ethernet, for example). The
SerializeBuffer's SerializeLayers function is a helper that does exactly that.
To generate a (empty and useless, because no fields are set)
Ethernet(IPv4(TCP(Payload))) packet, for example, you can run:
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{}
gopacket.SerializeLayers(buf, opts,
&layers.Ethernet{},
&layers.IPv4{},
&layers.TCP{},
gopacket.Payload([]byte{1, 2, 3, 4}))
packetData := buf.Bytes()
A Final Note
If you use gopacket, you'll almost definitely want to make sure gopacket/layers
is imported, since when imported it sets all the LayerType variables and fills
in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore,
it's recommended that even if you don't use any layers functions directly, you still import with:
import (
_ "github.com/google/gopacket/layers"
)
*/
package gopacket

@ -0,0 +1,236 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
import (
"bytes"
"fmt"
"strconv"
)
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
//
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
// can't use a byte slice. The two obvious choices are to use a string or a
// byte array. Strings work great, but string creation requires memory
// allocation, which can be slow. Arrays work great, but have a fixed size. We
// originally used the former, now we've switched to the latter. Use of a fixed
// byte-array doubles the speed of constructing a flow (due to not needing to
// allocate). This is a huge increase... too much for us to pass up.
//
// The end result of this, though, is that an endpoint/flow can't be created
// using more than MaxEndpointSize bytes per address.
const MaxEndpointSize = 16
// Endpoint is the set of bytes used to address packets at various layers.
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
// Endpoints are usable as map keys.
type Endpoint struct {
typ EndpointType
len int
raw [MaxEndpointSize]byte
}
// EndpointType returns the endpoint type associated with this endpoint.
func (a Endpoint) EndpointType() EndpointType { return a.typ }
// Raw returns the raw bytes of this endpoint. These aren't human-readable
// most of the time, but they are faster than calling String.
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
// LessThan provides a stable ordering for all endpoints. It sorts first based
// on the EndpointType of an endpoint, then based on the raw bytes of that
// endpoint.
//
// For some endpoints, the actual comparison may not make sense, however this
// ordering does provide useful information for most Endpoint types.
// Ordering is based first on endpoint type, then on raw endpoint bytes.
// Endpoint bytes are sorted lexigraphically.
func (a Endpoint) LessThan(b Endpoint) bool {
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
}
// fnvHash is used by our FastHash functions, and implements the FNV hash
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
// See http://isthe.com/chongo/tech/comp/fnv/.
func fnvHash(s []byte) (h uint64) {
h = fnvBasis
for i := 0; i < len(s); i++ {
h ^= uint64(s[i])
h *= fnvPrime
}
return
}
const fnvBasis = 14695981039346656037
const fnvPrime = 1099511628211
// FastHash provides a quick hashing function for an endpoint, useful if you'd
// like to split up endpoints by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (a Endpoint) FastHash() (h uint64) {
h = fnvHash(a.raw[:a.len])
h ^= uint64(a.typ)
h *= fnvPrime
return
}
// NewEndpoint creates a new Endpoint object.
//
// The size of raw must be less than MaxEndpointSize, otherwise this function
// will panic.
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
e.len = len(raw)
if e.len > MaxEndpointSize {
panic("raw byte length greater than MaxEndpointSize")
}
e.typ = typ
copy(e.raw[:], raw)
return
}
// EndpointTypeMetadata is used to register a new endpoint type.
type EndpointTypeMetadata struct {
// Name is the string returned by an EndpointType's String function.
Name string
// Formatter is called from an Endpoint's String function to format the raw
// bytes in an Endpoint into a human-readable string.
Formatter func([]byte) string
}
// EndpointType is the type of a gopacket Endpoint. This type determines how
// the bytes stored in the endpoint should be interpreted.
type EndpointType int64
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
// RegisterEndpointType creates a new EndpointType and registers it globally.
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
// reserved for gopacket's use.
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
t := EndpointType(num)
if _, ok := endpointTypes[t]; ok {
panic("Endpoint type number already in use")
}
endpointTypes[t] = meta
return t
}
func (e EndpointType) String() string {
if t, ok := endpointTypes[e]; ok {
return t.Name
}
return strconv.Itoa(int(e))
}
func (a Endpoint) String() string {
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
return t.Formatter(a.raw[:a.len])
}
return fmt.Sprintf("%v:%v", a.typ, a.raw)
}
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
// Flows are usable as map keys.
type Flow struct {
typ EndpointType
slen, dlen int
src, dst [MaxEndpointSize]byte
}
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
// The endpoints must have the same EndpointType, or this function will return
// an error.
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
if src.typ != dst.typ {
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
return
}
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
}
// FastHash provides a quick hashing function for a flow, useful if you'd
// like to split up flows by modulos or other load-balancing techniques.
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
// B->A.
//
// The output of FastHash is not guaranteed to remain the same through future
// code revisions, so should not be used to key values in persistent storage.
func (f Flow) FastHash() (h uint64) {
// This combination must be commutative. We don't use ^, since that would
// give the same hash for all A->A flows.
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
h ^= uint64(f.typ)
h *= fnvPrime
return
}
// String returns a human-readable representation of this flow, in the form
// "Src->Dst"
func (f Flow) String() string {
s, d := f.Endpoints()
return fmt.Sprintf("%v->%v", s, d)
}
// EndpointType returns the EndpointType for this Flow.
func (f Flow) EndpointType() EndpointType {
return f.typ
}
// Endpoints returns the two Endpoints for this flow.
func (f Flow) Endpoints() (src, dst Endpoint) {
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
}
// Src returns the source Endpoint for this flow.
func (f Flow) Src() (src Endpoint) {
src, _ = f.Endpoints()
return
}
// Dst returns the destination Endpoint for this flow.
func (f Flow) Dst() (dst Endpoint) {
_, dst = f.Endpoints()
return
}
// Reverse returns a new flow with endpoints reversed.
func (f Flow) Reverse() Flow {
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
}
// NewFlow creates a new flow.
//
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
// panic.
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
f.slen = len(src)
f.dlen = len(dst)
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
panic("flow raw byte length greater than MaxEndpointSize")
}
f.typ = t
copy(f.src[:], src)
copy(f.dst[:], dst)
return
}
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
// that are specified incorrectly during creation.
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
return fmt.Sprintf("%v", b)
}})
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
// InvalidFlow is a singleton Flow of type EndpointInvalid.
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)

@ -0,0 +1,278 @@
#!/bin/bash
# Copyright 2012 Google, Inc. All rights reserved.
# This script provides a simple way to run benchmarks against previous code and
# keep a log of how benchmarks change over time. When used with the --benchmark
# flag, it runs benchmarks from the current code and from the last commit run
# with --benchmark, then stores the results in the git commit description. We
# rerun the old benchmarks along with the new ones, since there's no guarantee
# that git commits will happen on the same machine, so machine differences could
# cause wildly inaccurate results.
#
# If you're making changes to 'gopacket' which could cause performance changes,
# you may be requested to use this commit script to make sure your changes don't
# have large detrimental effects (or to show off how awesome your performance
# improvements are).
#
# If not run with the --benchmark flag, this script is still very useful... it
# makes sure all the correct go formatting, building, and testing work as
# expected.
function Usage {
cat <<EOF
USAGE: $0 [--benchmark regexp] [--root] [--gen] <git commit flags...>
--benchmark: Run benchmark comparisons against last benchmark'd commit
--root: Run tests that require root priviledges
--gen: Generate code for MACs/ports by pulling down external data
Note, some 'git commit' flags are necessary, if all else fails, pass in -a
EOF
exit 1
}
BENCH=""
GEN=""
ROOT=""
while [ ! -z "$1" ]; do
case "$1" in
"--benchmark")
BENCH="$2"
shift
shift
;;
"--gen")
GEN="yes"
shift
;;
"--root")
ROOT="yes"
shift
;;
"--help")
Usage
;;
"-h")
Usage
;;
"help")
Usage
;;
*)
break
;;
esac
done
function Root {
if [ ! -z "$ROOT" ]; then
local exec="$1"
# Some folks (like me) keep source code in places inaccessible by root (like
# NFS), so to make sure things run smoothly we copy them to a /tmp location.
local tmpfile="$(mktemp -t gopacket_XXXXXXXX)"
echo "Running root test executable $exec as $tmpfile"
cp "$exec" "$tmpfile"
chmod a+x "$tmpfile"
shift
sudo "$tmpfile" "$@"
fi
}
if [ "$#" -eq "0" ]; then
Usage
fi
cd $(dirname $0)
# Check for copyright notices.
for filename in $(find ./ -type f -name '*.go'); do
if ! head -n 1 "$filename" | grep -q Copyright; then
echo "File '$filename' may not have copyright notice"
exit 1
fi
done
set -e
set -x
if [ ! -z "$ROOT" ]; then
echo "Running SUDO to get root priviledges for root tests"
sudo echo "have root"
fi
if [ ! -z "$GEN" ]; then
pushd macs
go run gen.go | gofmt > valid_mac_prefixes.go
popd
pushd layers
go run gen.go | gofmt > iana_ports.go
popd
fi
# Make sure everything is formatted, compiles, and tests pass.
go fmt ./...
go test -i ./... 2>/dev/null >/dev/null || true
go test
go build
pushd examples/bytediff
go build
popd
if [ -f /usr/include/pcap.h ]; then
pushd pcap
go test ./...
go build ./...
go build pcap_tester.go
Root pcap_tester --mode=basic
Root pcap_tester --mode=filtered
Root pcap_tester --mode=timestamp || echo "You might not support timestamp sources"
popd
pushd examples/pcapdump
go build
popd
pushd examples/arpscan
go build
popd
pushd examples/bidirectional
go build
popd
pushd examples/synscan
go build
popd
pushd examples/httpassembly
go build
popd
pushd examples/statsassembly
go build
popd
fi
pushd macs
go test ./...
gofmt -w gen.go
go build gen.go
popd
pushd tcpassembly
go test ./...
popd
pushd reassembly
go test ./...
popd
pushd layers
gofmt -w gen.go
go build gen.go
go test ./...
popd
pushd pcapgo
go test ./...
go build ./...
popd
if [ -f /usr/include/linux/if_packet.h ]; then
if grep -q TPACKET_V3 /usr/include/linux/if_packet.h; then
pushd afpacket
go build ./...
go test ./...
popd
fi
fi
if [ -f /usr/include/pfring.h ]; then
pushd pfring
go test ./...
go build ./...
popd
pushd examples/pfdump
go build
popd
fi
for travis_script in `ls .travis.*.sh`; do
./$travis_script
done
# Run our initial commit
git commit "$@"
if [ -z "$BENCH" ]; then
set +x
echo "We're not benchmarking and we've committed... we're done!"
exit
fi
### If we get here, we want to run benchmarks from current commit, and compare
### then to benchmarks from the last --benchmark commit.
# Get our current branch.
BRANCH="$(git branch | grep '^*' | awk '{print $2}')"
# File we're going to build our commit description in.
COMMIT_FILE="$(mktemp /tmp/tmp.XXXXXXXX)"
# Add the word "BENCH" to the start of the git commit.
echo -n "BENCH " > $COMMIT_FILE
# Get the current description... there must be an easier way.
git log -n 1 | grep '^ ' | sed 's/^ //' >> $COMMIT_FILE
# Get the commit sha for the last benchmark commit
PREV=$(git log -n 1 --grep='BENCHMARK_MARKER_DO_NOT_CHANGE' | head -n 1 | awk '{print $2}')
## Run current benchmarks
cat >> $COMMIT_FILE <<EOF
----------------------------------------------------------
BENCHMARK_MARKER_DO_NOT_CHANGE
----------------------------------------------------------
Go version $(go version)
TEST BENCHMARKS "$BENCH"
EOF
# go seems to have trouble with 'go test --bench=. ./...'
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
pushd layers
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
popd
cat >> $COMMIT_FILE <<EOF
PCAP BENCHMARK
EOF
if [ "$BENCH" -eq ".*" ]; then
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
fi
## Reset to last benchmark commit, run benchmarks
git checkout $PREV
cat >> $COMMIT_FILE <<EOF
----------------------------------------------------------
BENCHMARKING AGAINST COMMIT $PREV
----------------------------------------------------------
OLD TEST BENCHMARKS
EOF
# go seems to have trouble with 'go test --bench=. ./...'
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
pushd layers
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
popd
cat >> $COMMIT_FILE <<EOF
OLD PCAP BENCHMARK
EOF
if [ "$BENCH" -eq ".*" ]; then
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
fi
## Reset back to the most recent commit, edit the commit message by appending
## benchmark results.
git checkout $BRANCH
git commit --amend -F $COMMIT_FILE

@ -0,0 +1,107 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package gopacket
// LayerClass is a set of LayerTypes, used for grabbing one of a number of
// different types from a packet.
type LayerClass interface {
// Contains returns true if the given layer type should be considered part
// of this layer class.
Contains(LayerType) bool
// LayerTypes returns the set of all layer types in this layer class.
// Note that this may not be a fast operation on all LayerClass
// implementations.
LayerTypes() []LayerType
}
// Contains implements LayerClass.
func (l LayerType) Contains(a LayerType) bool {
return l == a
}
// LayerTypes implements LayerClass.
func (l LayerType) LayerTypes() []LayerType {
return []LayerType{l}
}
// LayerClassSlice implements a LayerClass with a slice.
type LayerClassSlice []bool
// Contains returns true if the given layer type should be considered part
// of this layer class.
func (s LayerClassSlice) Contains(t LayerType) bool {
return int(t) < len(s) && s[t]
}
// LayerTypes returns all layer types in this LayerClassSlice.
// Because of LayerClassSlice's implementation, this could be quite slow.
func (s LayerClassSlice) LayerTypes() (all []LayerType) {
for i := 0; i < len(s); i++ {
if s[i] {
all = append(all, LayerType(i))
}
}
return
}
// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of
// size max(types) and setting slice[t] to true for each type t. Note, if
// you implement your own LayerType and give it a high value, this WILL create
// a very large slice.
func NewLayerClassSlice(types []LayerType) LayerClassSlice {
var max LayerType
for _, typ := range types {
if typ > max {
max = typ
}
}
t := make([]bool, int(max+1))
for _, typ := range types {
t[typ] = true
}
return t
}
// LayerClassMap implements a LayerClass with a map.
type LayerClassMap map[LayerType]bool
// Contains returns true if the given layer type should be considered part
// of this layer class.
func (m LayerClassMap) Contains(t LayerType) bool {
return m[t]
}
// LayerTypes returns all layer types in this LayerClassMap.
func (m LayerClassMap) LayerTypes() (all []LayerType) {
for t := range m {
all = append(all, t)
}
return
}
// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each
// type in types.
func NewLayerClassMap(types []LayerType) LayerClassMap {
m := LayerClassMap{}
for _, typ := range types {
m[typ] = true
}
return m
}
// NewLayerClass creates a LayerClass, attempting to be smart about which type
// it creates based on which types are passed in.
func NewLayerClass(types []LayerType) LayerClass {
for _, typ := range types {
if typ > maxLayerType {
// NewLayerClassSlice could create a very large object, so instead create
// a map.
return NewLayerClassMap(types)
}
}
return NewLayerClassSlice(types)
}

@ -0,0 +1,40 @@
arp.go
base.go
base_test.go
cdp.go
ctp.go
decode_test.go
dhcp_test.go
dhcpv4.go
dns.go
dns_test.go
doc.go
dot11_test.go
dot1q.go
dot1q_test.go
eapol.go
etherip.go
fddi.go
gen.go
gre.go
gre_test.go
iana_ports.go
icmp6_test.go
igmp_test.go
ip4_test.go
ipsec.go
ipsec_test.go
loopback.go
mpls_test.go
ntp_test.go
ports.go
ppp.go
prism_test.go
radiotap_test.go
sflow_test.go
tcp_test.go
udp_test.go
usb_test.go
vrrp_test.go
vxlan.go
vxlan_test.go

@ -0,0 +1,109 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
// Potential values for ARP.Operation.
const (
ARPRequest = 1
ARPReply = 2
)
// ARP is a ARP packet header.
type ARP struct {
BaseLayer
AddrType LinkType
Protocol EthernetType
HwAddressSize uint8
ProtAddressSize uint8
Operation uint16
SourceHwAddress []byte
SourceProtAddress []byte
DstHwAddress []byte
DstProtAddress []byte
}
// LayerType returns LayerTypeARP
func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }
// DecodeFromBytes decodes the given bytes into this layer.
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
arp.HwAddressSize = data[4]
arp.ProtAddressSize = data[5]
arp.Operation = binary.BigEndian.Uint16(data[6:8])
arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
arp.Contents = data[:arpLength]
arp.Payload = data[arpLength:]
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
bytes, err := b.PrependBytes(size)
if err != nil {
return err
}
if opts.FixLengths {
if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
return errors.New("mismatched hardware address sizes")
}
arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
return errors.New("mismatched prot address sizes")
}
arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
}
binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
bytes[4] = arp.HwAddressSize
bytes[5] = arp.ProtAddressSize
binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
start := 8
for _, addr := range [][]byte{
arp.SourceHwAddress,
arp.SourceProtAddress,
arp.DstHwAddress,
arp.DstProtAddress,
} {
copy(bytes[start:], addr)
start += len(addr)
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (arp *ARP) CanDecode() gopacket.LayerClass {
return LayerTypeARP
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (arp *ARP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeARP(data []byte, p gopacket.PacketBuilder) error {
arp := &ARP{}
return decodingLayerDecoder(arp, data, p)
}

@ -0,0 +1,52 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"github.com/google/gopacket"
)
// BaseLayer is a convenience struct which implements the LayerData and
// LayerPayload functions of the Layer interface.
type BaseLayer struct {
// Contents is the set of bytes that make up this layer. IE: for an
// Ethernet packet, this would be the set of bytes making up the
// Ethernet frame.
Contents []byte
// Payload is the set of bytes contained by (but not part of) this
// Layer. Again, to take Ethernet as an example, this would be the
// set of bytes encapsulated by the Ethernet protocol.
Payload []byte
}
// LayerContents returns the bytes of the packet layer.
func (b *BaseLayer) LayerContents() []byte { return b.Contents }
// LayerPayload returns the bytes contained within the packet layer.
func (b *BaseLayer) LayerPayload() []byte { return b.Payload }
type layerDecodingLayer interface {
gopacket.Layer
DecodeFromBytes([]byte, gopacket.DecodeFeedback) error
NextLayerType() gopacket.LayerType
}
func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error {
err := d.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(d)
next := d.NextLayerType()
if next == gopacket.LayerTypeZero {
return nil
}
return p.NextDecoder(next)
}
// hacky way to zero out memory... there must be a better way?
var lotsOfZeros [1024]byte

@ -0,0 +1,651 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// Enum types courtesy of...
// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm
// https://code.google.com/p/ladvd/
// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c
package layers
import (
"encoding/binary"
"fmt"
"net"
"github.com/google/gopacket"
)
// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
type CDPTLVType uint16
// CDPTLVType values.
const (
CDPTLVDevID CDPTLVType = 0x0001
CDPTLVAddress CDPTLVType = 0x0002
CDPTLVPortID CDPTLVType = 0x0003
CDPTLVCapabilities CDPTLVType = 0x0004
CDPTLVVersion CDPTLVType = 0x0005
CDPTLVPlatform CDPTLVType = 0x0006
CDPTLVIPPrefix CDPTLVType = 0x0007
CDPTLVHello CDPTLVType = 0x0008
CDPTLVVTPDomain CDPTLVType = 0x0009
CDPTLVNativeVLAN CDPTLVType = 0x000a
CDPTLVFullDuplex CDPTLVType = 0x000b
CDPTLVVLANReply CDPTLVType = 0x000e
CDPTLVVLANQuery CDPTLVType = 0x000f
CDPTLVPower CDPTLVType = 0x0010
CDPTLVMTU CDPTLVType = 0x0011
CDPTLVExtendedTrust CDPTLVType = 0x0012
CDPTLVUntrustedCOS CDPTLVType = 0x0013
CDPTLVSysName CDPTLVType = 0x0014
CDPTLVSysOID CDPTLVType = 0x0015
CDPTLVMgmtAddresses CDPTLVType = 0x0016
CDPTLVLocation CDPTLVType = 0x0017
CDPTLVExternalPortID CDPTLVType = 0x0018
CDPTLVPowerRequested CDPTLVType = 0x0019
CDPTLVPowerAvailable CDPTLVType = 0x001a
CDPTLVPortUnidirectional CDPTLVType = 0x001b
CDPTLVEnergyWise CDPTLVType = 0x001d
CDPTLVSparePairPOE CDPTLVType = 0x001f
)
// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
type CiscoDiscoveryValue struct {
Type CDPTLVType
Length uint16
Value []byte
}
// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
type CiscoDiscovery struct {
BaseLayer
Version byte
TTL byte
Checksum uint16
Values []CiscoDiscoveryValue
}
// CDPCapability is the set of capabilities advertised by a CDP device.
type CDPCapability uint32
// CDPCapability values.
const (
CDPCapMaskRouter CDPCapability = 0x0001
CDPCapMaskTBBridge CDPCapability = 0x0002
CDPCapMaskSPBridge CDPCapability = 0x0004
CDPCapMaskSwitch CDPCapability = 0x0008
CDPCapMaskHost CDPCapability = 0x0010
CDPCapMaskIGMPFilter CDPCapability = 0x0020
CDPCapMaskRepeater CDPCapability = 0x0040
CDPCapMaskPhone CDPCapability = 0x0080
CDPCapMaskRemote CDPCapability = 0x0100
)
// CDPCapabilities represents the capabilities of a device
type CDPCapabilities struct {
L3Router bool
TBBridge bool
SPBridge bool
L2Switch bool
IsHost bool
IGMPFilter bool
L1Repeater bool
IsPhone bool
RemotelyManaged bool
}
// CDP Power-over-Ethernet values.
const (
CDPPoEFourWire byte = 0x01
CDPPoEPDArch byte = 0x02
CDPPoEPDRequest byte = 0x04
CDPPoEPSE byte = 0x08
)
// CDPSparePairPoE provides information on PoE.
type CDPSparePairPoE struct {
PSEFourWire bool // Supported / Not supported
PDArchShared bool // Shared / Independent
PDRequestOn bool // On / Off
PSEOn bool // On / Off
}
// CDPVLANDialogue encapsulates a VLAN Query/Reply
type CDPVLANDialogue struct {
ID uint8
VLAN uint16
}
// CDPPowerDialogue encapsulates a Power Query/Reply
type CDPPowerDialogue struct {
ID uint16
MgmtID uint16
Values []uint32
}
// CDPLocation provides location information for a CDP device.
type CDPLocation struct {
Type uint8 // Undocumented
Location string
}
// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
type CDPHello struct {
OUI []byte
ProtocolID uint16
ClusterMaster net.IP
Unknown1 net.IP
Version byte
SubVersion byte
Status byte
Unknown2 byte
ClusterCommander net.HardwareAddr
SwitchMAC net.HardwareAddr
Unknown3 byte
ManagementVLAN uint16
}
// CDPEnergyWiseSubtype is used within CDP to define TLV values.
type CDPEnergyWiseSubtype uint32
// CDPEnergyWiseSubtype values.
const (
CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007
CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008
CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009
CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
)
// CDPEnergyWise is used by CDP to monitor and control power usage.
type CDPEnergyWise struct {
EncryptedData []byte
Unknown1 uint32
SequenceNumber uint32
ModelNumber string
Unknown2 uint16
HardwareID string
SerialNum string
Unknown3 []byte
Role string
Domain string
Name string
ReplyUnknown1 []byte
ReplyPort []byte
ReplyAddress []byte
ReplyUnknown2 []byte
ReplyUnknown3 []byte
}
// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
type CiscoDiscoveryInfo struct {
BaseLayer
CDPHello
DeviceID string
Addresses []net.IP
PortID string
Capabilities CDPCapabilities
Version string
Platform string
IPPrefixes []net.IPNet
VTPDomain string
NativeVLAN uint16
FullDuplex bool
VLANReply CDPVLANDialogue
VLANQuery CDPVLANDialogue
PowerConsumption uint16
MTU uint32
ExtendedTrust uint8
UntrustedCOS uint8
SysName string
SysOID string
MgmtAddresses []net.IP
Location CDPLocation
PowerRequest CDPPowerDialogue
PowerAvailable CDPPowerDialogue
SparePairPoe CDPSparePairPoE
EnergyWise CDPEnergyWise
Unknown []CiscoDiscoveryValue
}
// LayerType returns gopacket.LayerTypeCiscoDiscovery.
func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
return LayerTypeCiscoDiscovery
}
func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
c := &CiscoDiscovery{
Version: data[0],
TTL: data[1],
Checksum: binary.BigEndian.Uint16(data[2:4]),
}
if c.Version != 1 && c.Version != 2 {
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
}
var err error
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
if err != nil {
return err
}
c.Contents = data[0:4]
c.Payload = data[4:]
p.AddLayer(c)
return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
}
// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
return LayerTypeCiscoDiscoveryInfo
}
func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
for len(data) > 0 {
val := CiscoDiscoveryValue{
Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
Length: binary.BigEndian.Uint16(data[2:4]),
}
if val.Length < 4 {
err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
break
}
val.Value = data[4:val.Length]
values = append(values, val)
data = data[val.Length:]
}
return
}
func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
var err error
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
p.AddLayer(info)
values, err := decodeCiscoDiscoveryTLVs(data)
if err != nil { // Unlikely, as parent decode will fail, but better safe...
return err
}
for _, val := range values {
switch val.Type {
case CDPTLVDevID:
info.DeviceID = string(val.Value)
case CDPTLVAddress:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
info.Addresses, err = decodeAddresses(val.Value)
if err != nil {
return err
}
case CDPTLVPortID:
info.PortID = string(val.Value)
case CDPTLVCapabilities:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
case CDPTLVVersion:
info.Version = string(val.Value)
case CDPTLVPlatform:
info.Platform = string(val.Value)
case CDPTLVIPPrefix:
v := val.Value
l := len(v)
if l%5 == 0 && l >= 5 {
for len(v) > 0 {
_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
info.IPPrefixes = append(info.IPPrefixes, *ipnet)
v = v[5:]
}
} else {
return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
}
case CDPTLVHello:
if err = checkCDPTLVLen(val, 32); err != nil {
return err
}
v := val.Value
info.CDPHello.OUI = v[0:3]
info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
info.CDPHello.ClusterMaster = v[5:9]
info.CDPHello.Unknown1 = v[9:13]
info.CDPHello.Version = v[13]
info.CDPHello.SubVersion = v[14]
info.CDPHello.Status = v[15]
info.CDPHello.Unknown2 = v[16]
info.CDPHello.ClusterCommander = v[17:23]
info.CDPHello.SwitchMAC = v[23:29]
info.CDPHello.Unknown3 = v[29]
info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
case CDPTLVVTPDomain:
info.VTPDomain = string(val.Value)
case CDPTLVNativeVLAN:
if err = checkCDPTLVLen(val, 2); err != nil {
return err
}
info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
case CDPTLVFullDuplex:
if err = checkCDPTLVLen(val, 1); err != nil {
return err
}
info.FullDuplex = (val.Value[0] == 1)
case CDPTLVVLANReply:
if err = checkCDPTLVLen(val, 3); err != nil {
return err
}
info.VLANReply.ID = uint8(val.Value[0])
info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
case CDPTLVVLANQuery:
if err = checkCDPTLVLen(val, 3); err != nil {
return err
}
info.VLANQuery.ID = uint8(val.Value[0])
info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
case CDPTLVPower:
if err = checkCDPTLVLen(val, 2); err != nil {
return err
}
info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
case CDPTLVMTU:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
case CDPTLVExtendedTrust:
if err = checkCDPTLVLen(val, 1); err != nil {
return err
}
info.ExtendedTrust = uint8(val.Value[0])
case CDPTLVUntrustedCOS:
if err = checkCDPTLVLen(val, 1); err != nil {
return err
}
info.UntrustedCOS = uint8(val.Value[0])
case CDPTLVSysName:
info.SysName = string(val.Value)
case CDPTLVSysOID:
info.SysOID = string(val.Value)
case CDPTLVMgmtAddresses:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
info.MgmtAddresses, err = decodeAddresses(val.Value)
if err != nil {
return err
}
case CDPTLVLocation:
if err = checkCDPTLVLen(val, 2); err != nil {
return err
}
info.Location.Type = uint8(val.Value[0])
info.Location.Location = string(val.Value[1:])
// case CDPTLVLExternalPortID:
// Undocumented
case CDPTLVPowerRequested:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
for n := 4; n < len(val.Value); n += 4 {
info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
}
case CDPTLVPowerAvailable:
if err = checkCDPTLVLen(val, 4); err != nil {
return err
}
info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
for n := 4; n < len(val.Value); n += 4 {
info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
}
// case CDPTLVPortUnidirectional
// Undocumented
case CDPTLVEnergyWise:
if err = checkCDPTLVLen(val, 72); err != nil {
return err
}
info.EnergyWise.EncryptedData = val.Value[0:20]
info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
info.EnergyWise.ModelNumber = string(val.Value[28:44])
info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
info.EnergyWise.HardwareID = string(val.Value[46:49])
info.EnergyWise.SerialNum = string(val.Value[49:60])
info.EnergyWise.Unknown3 = val.Value[60:68]
tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
data := val.Value[72:]
if len(data) < int(tlvLen) {
return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
}
numSeen := 0
for len(data) > 8 {
numSeen++
if numSeen > int(tlvNum) { // Too many TLV's ?
return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
}
tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
tLen := int(binary.BigEndian.Uint32(data[4:8]))
if tLen > len(data)-8 {
return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
}
data = data[8:]
switch tType {
case CDPEnergyWiseRole:
info.EnergyWise.Role = string(data[:])
case CDPEnergyWiseDomain:
info.EnergyWise.Domain = string(data[:])
case CDPEnergyWiseName:
info.EnergyWise.Name = string(data[:])
case CDPEnergyWiseReplyTo:
if len(data) >= 18 {
info.EnergyWise.ReplyUnknown1 = data[0:2]
info.EnergyWise.ReplyPort = data[2:4]
info.EnergyWise.ReplyAddress = data[4:8]
info.EnergyWise.ReplyUnknown2 = data[8:10]
info.EnergyWise.ReplyUnknown3 = data[10:14]
}
}
data = data[tLen:]
}
case CDPTLVSparePairPOE:
if err = checkCDPTLVLen(val, 1); err != nil {
return err
}
v := val.Value[0]
info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
default:
info.Unknown = append(info.Unknown, val)
}
}
return nil
}
// CDP Protocol Types
const (
CDPProtocolTypeNLPID byte = 1
CDPProtocolType802_2 byte = 2
)
// CDPAddressType is used to define TLV values within CDP addresses.
type CDPAddressType uint64
// CDP Address types.
const (
CDPAddressTypeCLNP CDPAddressType = 0x81
CDPAddressTypeIPV4 CDPAddressType = 0xcc
CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800
CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003
CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137
CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4
CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600
CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019
)
func decodeAddresses(v []byte) (addresses []net.IP, err error) {
numaddr := int(binary.BigEndian.Uint32(v[0:4]))
if numaddr < 1 {
return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
}
v = v[4:]
if len(v) < numaddr*8 {
return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
}
for i := 0; i < numaddr; i++ {
prottype := v[0]
if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
}
protlen := int(v[1])
if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
(prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
}
plen := make([]byte, 8)
copy(plen[8-protlen:], v[2:2+protlen])
protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
v = v[2+protlen:]
addrlen := binary.BigEndian.Uint16(v[0:2])
ab := v[2 : 2+addrlen]
if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
} else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
addresses = append(addresses, net.IP(ab))
} else {
// only handle IPV4 & IPV6 for now
}
v = v[2+addrlen:]
if len(v) < 8 {
break
}
}
return
}
func (t CDPTLVType) String() (s string) {
switch t {
case CDPTLVDevID:
s = "Device ID"
case CDPTLVAddress:
s = "Addresses"
case CDPTLVPortID:
s = "Port ID"
case CDPTLVCapabilities:
s = "Capabilities"
case CDPTLVVersion:
s = "Software Version"
case CDPTLVPlatform:
s = "Platform"
case CDPTLVIPPrefix:
s = "IP Prefix"
case CDPTLVHello:
s = "Protocol Hello"
case CDPTLVVTPDomain:
s = "VTP Management Domain"
case CDPTLVNativeVLAN:
s = "Native VLAN"
case CDPTLVFullDuplex:
s = "Full Duplex"
case CDPTLVVLANReply:
s = "VoIP VLAN Reply"
case CDPTLVVLANQuery:
s = "VLANQuery"
case CDPTLVPower:
s = "Power consumption"
case CDPTLVMTU:
s = "MTU"
case CDPTLVExtendedTrust:
s = "Extended Trust Bitmap"
case CDPTLVUntrustedCOS:
s = "Untrusted Port CoS"
case CDPTLVSysName:
s = "System Name"
case CDPTLVSysOID:
s = "System OID"
case CDPTLVMgmtAddresses:
s = "Management Addresses"
case CDPTLVLocation:
s = "Location"
case CDPTLVExternalPortID:
s = "External Port ID"
case CDPTLVPowerRequested:
s = "Power Requested"
case CDPTLVPowerAvailable:
s = "Power Available"
case CDPTLVPortUnidirectional:
s = "Port Unidirectional"
case CDPTLVEnergyWise:
s = "Energy Wise"
case CDPTLVSparePairPOE:
s = "Spare Pair POE"
default:
s = "Unknown"
}
return
}
func (a CDPAddressType) String() (s string) {
switch a {
case CDPAddressTypeCLNP:
s = "Connectionless Network Protocol"
case CDPAddressTypeIPV4:
s = "IPv4"
case CDPAddressTypeIPV6:
s = "IPv6"
case CDPAddressTypeDECNET:
s = "DECnet Phase IV"
case CDPAddressTypeAPPLETALK:
s = "Apple Talk"
case CDPAddressTypeIPX:
s = "Novell IPX"
case CDPAddressTypeVINES:
s = "Banyan VINES"
case CDPAddressTypeXNS:
s = "Xerox Network Systems"
case CDPAddressTypeAPOLLO:
s = "Apollo"
default:
s = "Unknown"
}
return
}
func (t CDPEnergyWiseSubtype) String() (s string) {
switch t {
case CDPEnergyWiseRole:
s = "Role"
case CDPEnergyWiseDomain:
s = "Domain"
case CDPEnergyWiseName:
s = "Name"
case CDPEnergyWiseReplyTo:
s = "ReplyTo"
default:
s = "Unknown"
}
return
}
func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
if len(v.Value) < l {
err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))
}
return
}

@ -0,0 +1,109 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each
// EthernetCTP layer.
type EthernetCTPFunction uint16
// EthernetCTPFunction values.
const (
EthernetCTPFunctionReply EthernetCTPFunction = 1
EthernetCTPFunctionForwardData EthernetCTPFunction = 2
)
// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html.
// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more
// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer.
type EthernetCTP struct {
BaseLayer
SkipCount uint16
}
// LayerType returns gopacket.LayerTypeEthernetCTP.
func (c *EthernetCTP) LayerType() gopacket.LayerType {
return LayerTypeEthernetCTP
}
// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more
// details.
type EthernetCTPForwardData struct {
BaseLayer
Function EthernetCTPFunction
ForwardAddress []byte
}
// LayerType returns gopacket.LayerTypeEthernetCTPForwardData.
func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType {
return LayerTypeEthernetCTPForwardData
}
// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint.
func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint {
return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress)
}
// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details.
type EthernetCTPReply struct {
BaseLayer
Function EthernetCTPFunction
ReceiptNumber uint16
Data []byte
}
// LayerType returns gopacket.LayerTypeEthernetCTPReply.
func (c *EthernetCTPReply) LayerType() gopacket.LayerType {
return LayerTypeEthernetCTPReply
}
// Payload returns the EthernetCTP reply's Data bytes.
func (c *EthernetCTPReply) Payload() []byte { return c.Data }
func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error {
c := &EthernetCTP{
SkipCount: binary.LittleEndian.Uint16(data[:2]),
BaseLayer: BaseLayer{data[:2], data[2:]},
}
if c.SkipCount%2 != 0 {
return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount)
}
p.AddLayer(c)
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
}
// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP
// layer type to decode next, then decodes based on that.
func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error {
function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2]))
switch function {
case EthernetCTPFunctionReply:
reply := &EthernetCTPReply{
Function: function,
ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]),
Data: data[4:],
BaseLayer: BaseLayer{data, nil},
}
p.AddLayer(reply)
p.SetApplicationLayer(reply)
return nil
case EthernetCTPFunctionForwardData:
forward := &EthernetCTPForwardData{
Function: function,
ForwardAddress: data[2:8],
BaseLayer: BaseLayer{data[:8], data[8:]},
}
p.AddLayer(forward)
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
}
return fmt.Errorf("Unknown EthernetCTP function type %v", function)
}

@ -0,0 +1,571 @@
// Copyright 2016 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
// DHCPOp rerprents a bootp operation
type DHCPOp byte
// bootp operations
const (
DHCPOpRequest DHCPOp = 1
DHCPOpReply DHCPOp = 2
)
// String returns a string version of a DHCPOp.
func (o DHCPOp) String() string {
switch o {
case DHCPOpRequest:
return "Request"
case DHCPOpReply:
return "Reply"
default:
return "Unknown"
}
}
// DHCPMsgType represents a DHCP operation
type DHCPMsgType byte
// Constants that represent DHCP operations
const (
DHCPMsgTypeUnspecified DHCPMsgType = iota
DHCPMsgTypeDiscover
DHCPMsgTypeOffer
DHCPMsgTypeRequest
DHCPMsgTypeDecline
DHCPMsgTypeAck
DHCPMsgTypeNak
DHCPMsgTypeRelease
DHCPMsgTypeInform
)
// String returns a string version of a DHCPMsgType.
func (o DHCPMsgType) String() string {
switch o {
case DHCPMsgTypeUnspecified:
return "Unspecified"
case DHCPMsgTypeDiscover:
return "Discover"
case DHCPMsgTypeOffer:
return "Offer"
case DHCPMsgTypeRequest:
return "Request"
case DHCPMsgTypeDecline:
return "Decline"
case DHCPMsgTypeAck:
return "Ack"
case DHCPMsgTypeNak:
return "Nak"
case DHCPMsgTypeRelease:
return "Release"
case DHCPMsgTypeInform:
return "Inform"
default:
return "Unknown"
}
}
//DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
var DHCPMagic uint32 = 0x63825363
// DHCPv4 contains data for a single DHCP packet.
type DHCPv4 struct {
BaseLayer
Operation DHCPOp
HardwareType LinkType
HardwareLen uint8
HardwareOpts uint8
Xid uint32
Secs uint16
Flags uint16
ClientIP net.IP
YourClientIP net.IP
NextServerIP net.IP
RelayAgentIP net.IP
ClientHWAddr net.HardwareAddr
ServerName []byte
File []byte
Options DHCPOptions
}
// DHCPOptions is used to get nicely printed option lists which would normally
// be cut off after 5 options.
type DHCPOptions []DHCPOption
// String returns a string version of the options list.
func (o DHCPOptions) String() string {
buf := &bytes.Buffer{}
buf.WriteByte('[')
for i, opt := range o {
buf.WriteString(opt.String())
if i+1 != len(o) {
buf.WriteString(", ")
}
}
buf.WriteByte(']')
return buf.String()
}
// LayerType returns gopacket.LayerTypeDHCPv4
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
d.Operation = DHCPOp(data[0])
d.HardwareType = LinkType(data[1])
d.HardwareLen = data[2]
d.HardwareOpts = data[3]
d.Xid = binary.BigEndian.Uint32(data[4:8])
d.Secs = binary.BigEndian.Uint16(data[8:10])
d.Flags = binary.BigEndian.Uint16(data[10:12])
d.ClientIP = net.IP(data[12:16])
d.YourClientIP = net.IP(data[16:20])
d.NextServerIP = net.IP(data[20:24])
d.RelayAgentIP = net.IP(data[24:28])
d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
d.ServerName = data[44:108]
d.File = data[108:236]
if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
return errors.New("Bad DHCP header")
}
if len(data) <= 240 {
// DHCP Packet could have no option (??)
return nil
}
options := data[240:]
stop := len(options)
start := 0
for start < stop {
o := DHCPOption{}
if err := o.decode(options[start:]); err != nil {
return err
}
if o.Type == DHCPOptEnd {
break
}
d.Options = append(d.Options, o)
// Check if the option is a single byte pad
if o.Type == DHCPOptPad {
start++
} else {
start += int(o.Length) + 2
}
}
return nil
}
// Len returns the length of a DHCPv4 packet.
func (d *DHCPv4) Len() uint16 {
n := uint16(240)
for _, o := range d.Options {
if o.Type == DHCPOptPad {
n++
} else {
n += uint16(o.Length) + 2
}
}
n++ // for opt end
return n
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
plen := int(d.Len())
data, err := b.PrependBytes(plen)
if err != nil {
return err
}
data[0] = byte(d.Operation)
data[1] = byte(d.HardwareType)
if opts.FixLengths {
d.HardwareLen = uint8(len(d.ClientHWAddr))
}
data[2] = d.HardwareLen
data[3] = d.HardwareOpts
binary.BigEndian.PutUint32(data[4:8], d.Xid)
binary.BigEndian.PutUint16(data[8:10], d.Secs)
binary.BigEndian.PutUint16(data[10:12], d.Flags)
copy(data[12:16], d.ClientIP.To4())
copy(data[16:20], d.YourClientIP.To4())
copy(data[20:24], d.NextServerIP.To4())
copy(data[24:28], d.RelayAgentIP.To4())
copy(data[28:44], d.ClientHWAddr)
copy(data[44:108], d.ServerName)
copy(data[108:236], d.File)
binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
if len(d.Options) > 0 {
offset := 240
for _, o := range d.Options {
if err := o.encode(data[offset:]); err != nil {
return err
}
// A pad option is only a single byte
if o.Type == DHCPOptPad {
offset++
} else {
offset += 2 + len(o.Data)
}
}
optend := NewDHCPOption(DHCPOptEnd, nil)
if err := optend.encode(data[offset:]); err != nil {
return err
}
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (d *DHCPv4) CanDecode() gopacket.LayerClass {
return LayerTypeDHCPv4
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (d *DHCPv4) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
dhcp := &DHCPv4{}
err := dhcp.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(dhcp)
return p.NextDecoder(gopacket.LayerTypePayload)
}
// DHCPOpt represents a DHCP option or parameter from RFC-2132
type DHCPOpt byte
// Constants for the DHCPOpt options.
const (
DHCPOptPad DHCPOpt = 0
DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP
DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC)
DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP
DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP
DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP
DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP
DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP
DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP
DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP
DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP
DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP
DHCPOptHostname DHCPOpt = 12 // n, string
DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16
DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string
DHCPOptDomainName DHCPOpt = 15 // n, string
DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP
DHCPOptRootPath DHCPOpt = 17 // n, string
DHCPOptExtensionsPath DHCPOpt = 18 // n, string
DHCPOptIPForwarding DHCPOpt = 19 // 1, bool
DHCPOptSourceRouting DHCPOpt = 20 // 1, bool
DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP}
DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16
DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte
DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32
DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16
DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16
DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool
DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP
DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool
DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool
DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool
DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP
DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
DHCPOptARPTrailers DHCPOpt = 34 // 1, bool
DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32
DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool
DHCPOptTCPTTL DHCPOpt = 37 // 1, byte
DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32
DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool
DHCPOptNISDomain DHCPOpt = 40 // n, string
DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP
DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP
DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated.
DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP
DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP
DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte
DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string
DHCPOptXFontServer DHCPOpt = 48 // n, string
DHCPOptXDisplayManager DHCPOpt = 49 // n, string
DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP
DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32
DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3
DHCPOptMessageType DHCPOpt = 53 // 1, 1-7
DHCPOptServerID DHCPOpt = 54 // 4, net.IP
DHCPOptParamsRequest DHCPOpt = 55 // n, []byte
DHCPOptMessage DHCPOpt = 56 // n, 3
DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16
DHCPOptT1 DHCPOpt = 58 // 4, uint32
DHCPOptT2 DHCPOpt = 59 // 4, uint32
DHCPOptClassID DHCPOpt = 60 // n, []byte
DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte
DHCPOptDomainSearch DHCPOpt = 119 // n, string
DHCPOptSIPServers DHCPOpt = 120 // n, url
DHCPOptClasslessStaticRoute DHCPOpt = 121 //
DHCPOptEnd DHCPOpt = 255
)
// String returns a string version of a DHCPOpt.
func (o DHCPOpt) String() string {
switch o {
case DHCPOptPad:
return "(padding)"
case DHCPOptSubnetMask:
return "SubnetMask"
case DHCPOptTimeOffset:
return "TimeOffset"
case DHCPOptRouter:
return "Router"
case DHCPOptTimeServer:
return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
case DHCPOptNameServer:
return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
case DHCPOptDNS:
return "DNS"
case DHCPOptLogServer:
return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
case DHCPOptCookieServer:
return "CookieServer"
case DHCPOptLPRServer:
return "LPRServer"
case DHCPOptImpressServer:
return "ImpressServer"
case DHCPOptResLocServer:
return "ResourceLocationServer"
case DHCPOptHostname:
return "Hostname"
case DHCPOptBootfileSize:
return "BootfileSize"
case DHCPOptMeritDumpFile:
return "MeritDumpFile"
case DHCPOptDomainName:
return "DomainName"
case DHCPOptSwapServer:
return "SwapServer"
case DHCPOptRootPath:
return "RootPath"
case DHCPOptExtensionsPath:
return "ExtensionsPath"
case DHCPOptIPForwarding:
return "IPForwarding"
case DHCPOptSourceRouting:
return "SourceRouting"
case DHCPOptPolicyFilter:
return "PolicyFilter"
case DHCPOptDatagramMTU:
return "DatagramMTU"
case DHCPOptDefaultTTL:
return "DefaultTTL"
case DHCPOptPathMTUAgingTimeout:
return "PathMTUAgingTimeout"
case DHCPOptPathPlateuTableOption:
return "PathPlateuTableOption"
case DHCPOptInterfaceMTU:
return "InterfaceMTU"
case DHCPOptAllSubsLocal:
return "AllSubsLocal"
case DHCPOptBroadcastAddr:
return "BroadcastAddress"
case DHCPOptMaskDiscovery:
return "MaskDiscovery"
case DHCPOptMaskSupplier:
return "MaskSupplier"
case DHCPOptRouterDiscovery:
return "RouterDiscovery"
case DHCPOptSolicitAddr:
return "SolicitAddr"
case DHCPOptStaticRoute:
return "StaticRoute"
case DHCPOptARPTrailers:
return "ARPTrailers"
case DHCPOptARPTimeout:
return "ARPTimeout"
case DHCPOptEthernetEncap:
return "EthernetEncap"
case DHCPOptTCPTTL:
return "TCPTTL"
case DHCPOptTCPKeepAliveInt:
return "TCPKeepAliveInt"
case DHCPOptTCPKeepAliveGarbage:
return "TCPKeepAliveGarbage"
case DHCPOptNISDomain:
return "NISDomain"
case DHCPOptNISServers:
return "NISServers"
case DHCPOptNTPServers:
return "NTPServers"
case DHCPOptVendorOption:
return "VendorOption"
case DHCPOptNetBIOSTCPNS:
return "NetBIOSOverTCPNS"
case DHCPOptNetBIOSTCPDDS:
return "NetBiosOverTCPDDS"
case DHCPOptNETBIOSTCPNodeType:
return "NetBIOSOverTCPNodeType"
case DHCPOptNetBIOSTCPScope:
return "NetBIOSOverTCPScope"
case DHCPOptXFontServer:
return "XFontServer"
case DHCPOptXDisplayManager:
return "XDisplayManager"
case DHCPOptEnd:
return "(end)"
case DHCPOptSIPServers:
return "SipServers"
case DHCPOptRequestIP:
return "RequestIP"
case DHCPOptLeaseTime:
return "LeaseTime"
case DHCPOptExtOptions:
return "ExtOpts"
case DHCPOptMessageType:
return "MessageType"
case DHCPOptServerID:
return "ServerID"
case DHCPOptParamsRequest:
return "ParamsRequest"
case DHCPOptMessage:
return "Message"
case DHCPOptMaxMessageSize:
return "MaxDHCPSize"
case DHCPOptT1:
return "Timer1"
case DHCPOptT2:
return "Timer2"
case DHCPOptClassID:
return "ClassID"
case DHCPOptClientID:
return "ClientID"
case DHCPOptDomainSearch:
return "DomainSearch"
case DHCPOptClasslessStaticRoute:
return "ClasslessStaticRoute"
default:
return "Unknown"
}
}
// DHCPOption rerpresents a DHCP option.
type DHCPOption struct {
Type DHCPOpt
Length uint8
Data []byte
}
// String returns a string version of a DHCP Option.
func (o DHCPOption) String() string {
switch o.Type {
case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
case DHCPOptMessageType:
if len(o.Data) != 1 {
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
}
return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
if len(o.Data) < 4 {
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
}
return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
if len(o.Data) != 4 {
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
}
return fmt.Sprintf("Option(%s:%d)", o.Type,
uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
case DHCPOptParamsRequest:
buf := &bytes.Buffer{}
buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
for i, v := range o.Data {
buf.WriteString(DHCPOpt(v).String())
if i+1 != len(o.Data) {
buf.WriteByte(',')
}
}
buf.WriteString(")")
return buf.String()
default:
return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
}
}
// NewDHCPOption constructs a new DHCPOption with a given type and data.
func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
o := DHCPOption{Type: t}
if data != nil {
o.Data = data
o.Length = uint8(len(data))
}
return o
}
func (o *DHCPOption) encode(b []byte) error {
switch o.Type {
case DHCPOptPad, DHCPOptEnd:
b[0] = byte(o.Type)
default:
if o.Length > 253 {
return errors.New("data too long to encode")
}
b[0] = byte(o.Type)
b[1] = o.Length
copy(b[2:], o.Data)
}
return nil
}
func (o *DHCPOption) decode(data []byte) error {
if len(data) < 1 {
// Pad/End have a length of 1
return errors.New("Not enough data to decode")
}
o.Type = DHCPOpt(data[0])
switch o.Type {
case DHCPOptPad, DHCPOptEnd:
o.Data = nil
default:
if len(data) < 3 {
return errors.New("Not enough data to decode")
}
o.Length = data[1]
if o.Length > 253 {
return errors.New("data too long to decode")
}
o.Data = data[2 : 2+o.Length]
}
return nil
}

@ -0,0 +1,894 @@
// Copyright 2014 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
// DNSClass defines the class associated with a request/response. Different DNS
// classes can be thought of as an array of parallel namespace trees.
type DNSClass uint16
// DNSClass known values.
const (
DNSClassIN DNSClass = 1 // Internet
DNSClassCS DNSClass = 2 // the CSNET class (Obsolete)
DNSClassCH DNSClass = 3 // the CHAOS class
DNSClassHS DNSClass = 4 // Hesiod [Dyer 87]
DNSClassAny DNSClass = 255 // AnyClass
)
func (dc DNSClass) String() string {
switch dc {
default:
return "Unknown"
case DNSClassIN:
return "IN"
case DNSClassCS:
return "CS"
case DNSClassCH:
return "CH"
case DNSClassHS:
return "HS"
case DNSClassAny:
return "Any"
}
}
// DNSType defines the type of data being requested/returned in a
// question/answer.
type DNSType uint16
// DNSType known values.
const (
DNSTypeA DNSType = 1 // a host address
DNSTypeNS DNSType = 2 // an authoritative name server
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
DNSTypeWKS DNSType = 11 // a well known service description
DNSTypePTR DNSType = 12 // a domain name pointer
DNSTypeHINFO DNSType = 13 // host information
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
DNSTypeMX DNSType = 15 // mail exchange
DNSTypeTXT DNSType = 16 // text strings
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
)
func (dt DNSType) String() string {
switch dt {
default:
return "Unknown"
case DNSTypeA:
return "A"
case DNSTypeNS:
return "NS"
case DNSTypeMD:
return "MD"
case DNSTypeMF:
return "MF"
case DNSTypeCNAME:
return "CNAME"
case DNSTypeSOA:
return "SOA"
case DNSTypeMB:
return "MB"
case DNSTypeMG:
return "MG"
case DNSTypeMR:
return "MR"
case DNSTypeNULL:
return "NULL"
case DNSTypeWKS:
return "WKS"
case DNSTypePTR:
return "PTR"
case DNSTypeHINFO:
return "HINFO"
case DNSTypeMINFO:
return "MINFO"
case DNSTypeMX:
return "MX"
case DNSTypeTXT:
return "TXT"
case DNSTypeAAAA:
return "AAAA"
case DNSTypeSRV:
return "SRV"
}
}
// DNSResponseCode provides response codes for question answers.
type DNSResponseCode uint8
// DNSResponseCode known values.
const (
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
)
func (drc DNSResponseCode) String() string {
switch drc {
default:
return "Unknown"
case DNSResponseCodeNoErr:
return "No Error"
case DNSResponseCodeFormErr:
return "Format Error"
case DNSResponseCodeServFail:
return "Server Failure "
case DNSResponseCodeNXDomain:
return "Non-Existent Domain"
case DNSResponseCodeNotImp:
return "Not Implemented"
case DNSResponseCodeRefused:
return "Query Refused"
case DNSResponseCodeYXDomain:
return "Name Exists when it should not"
case DNSResponseCodeYXRRSet:
return "RR Set Exists when it should not"
case DNSResponseCodeNXRRSet:
return "RR Set that should exist does not"
case DNSResponseCodeNotAuth:
return "Server Not Authoritative for zone"
case DNSResponseCodeNotZone:
return "Name not contained in zone"
case DNSResponseCodeBadVers:
return "Bad OPT Version"
case DNSResponseCodeBadKey:
return "Key not recognized"
case DNSResponseCodeBadTime:
return "Signature out of time window"
case DNSResponseCodeBadMode:
return "Bad TKEY Mode"
case DNSResponseCodeBadName:
return "Duplicate key name"
case DNSResponseCodeBadAlg:
return "Algorithm not supported"
case DNSResponseCodeBadTruc:
return "Bad Truncation"
}
}
// DNSOpCode defines a set of different operation types.
type DNSOpCode uint8
// DNSOpCode known values.
const (
DNSOpCodeQuery DNSOpCode = 0 // Query [RFC1035]
DNSOpCodeIQuery DNSOpCode = 1 // Inverse Query Obsolete [RFC3425]
DNSOpCodeStatus DNSOpCode = 2 // Status [RFC1035]
DNSOpCodeNotify DNSOpCode = 4 // Notify [RFC1996]
DNSOpCodeUpdate DNSOpCode = 5 // Update [RFC2136]
)
func (doc DNSOpCode) String() string {
switch doc {
default:
return "Unknown"
case DNSOpCodeQuery:
return "Query"
case DNSOpCodeIQuery:
return "Inverse Query"
case DNSOpCodeStatus:
return "Status"
case DNSOpCodeNotify:
return "Notify"
case DNSOpCodeUpdate:
return "Update"
}
}
// DNS is specified in RFC 1034 / RFC 1035
// +---------------------+
// | Header |
// +---------------------+
// | Question | the question for the name server
// +---------------------+
// | Answer | RRs answering the question
// +---------------------+
// | Authority | RRs pointing toward an authority
// +---------------------+
// | Additional | RRs holding additional information
// +---------------------+
//
// DNS Header
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ID |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | QDCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ANCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | NSCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | ARCOUNT |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// DNS contains data from a single Domain Name Service packet.
type DNS struct {
BaseLayer
// Header fields
ID uint16
QR bool
OpCode DNSOpCode
AA bool // Authoritative answer
TC bool // Truncated
RD bool // Recursion desired
RA bool // Recursion available
Z uint8 // Resrved for future use
ResponseCode DNSResponseCode
QDCount uint16 // Number of questions to expect
ANCount uint16 // Number of answers to expect
NSCount uint16 // Number of authorities to expect
ARCount uint16 // Number of additional records to expect
// Entries
Questions []DNSQuestion
Answers []DNSResourceRecord
Authorities []DNSResourceRecord
Additionals []DNSResourceRecord
// buffer for doing name decoding. We use a single reusable buffer to avoid
// name decoding on a single object via multiple DecodeFromBytes calls
// requiring constant allocation of small byte slices.
buffer []byte
}
// LayerType returns gopacket.LayerTypeDNS.
func (d *DNS) LayerType() gopacket.LayerType { return LayerTypeDNS }
// decodeDNS decodes the byte slice into a DNS type. It also
// setups the application Layer in PacketBuilder.
func decodeDNS(data []byte, p gopacket.PacketBuilder) error {
d := &DNS{}
err := d.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(d)
p.SetApplicationLayer(d)
return nil
}
// DecodeFromBytes decodes the slice into the DNS struct.
func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
d.buffer = d.buffer[:0]
if len(data) < 12 {
df.SetTruncated()
return errors.New("DNS packet too short")
}
// since there are no further layers, the baselayer's content is
// pointing to this layer
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
d.ID = binary.BigEndian.Uint16(data[:2])
d.QR = data[2]&0x80 != 0
d.OpCode = DNSOpCode(data[2]>>3) & 0x0F
d.AA = data[2]&0x04 != 0
d.TC = data[2]&0x02 != 0
d.RD = data[2]&0x01 != 0
d.RA = data[3]&0x80 != 0
d.Z = uint8(data[3]>>4) & 0x7
d.ResponseCode = DNSResponseCode(data[3] & 0xF)
d.QDCount = binary.BigEndian.Uint16(data[4:6])
d.ANCount = binary.BigEndian.Uint16(data[6:8])
d.NSCount = binary.BigEndian.Uint16(data[8:10])
d.ARCount = binary.BigEndian.Uint16(data[10:12])
d.Questions = d.Questions[:0]
d.Answers = d.Answers[:0]
d.Authorities = d.Authorities[:0]
d.Additionals = d.Additionals[:0]
offset := 12
var err error
for i := 0; i < int(d.QDCount); i++ {
var q DNSQuestion
if offset, err = q.decode(data, offset, df, &d.buffer); err != nil {
return err
}
d.Questions = append(d.Questions, q)
}
// For some horrible reason, if we do the obvious thing in this loop:
// var r DNSResourceRecord
// if blah := r.decode(blah); err != nil {
// return err
// }
// d.Foo = append(d.Foo, r)
// the Go compiler thinks that 'r' escapes to the heap, causing a malloc for
// every Answer, Authority, and Additional. To get around this, we do
// something really silly: we append an empty resource record to our slice,
// then use the last value in the slice to call decode. Since the value is
// already in the slice, there's no WAY it can escape... on the other hand our
// code is MUCH uglier :(
for i := 0; i < int(d.ANCount); i++ {
d.Answers = append(d.Answers, DNSResourceRecord{})
if offset, err = d.Answers[i].decode(data, offset, df, &d.buffer); err != nil {
d.Answers = d.Answers[:i] // strip off erroneous value
return err
}
}
for i := 0; i < int(d.NSCount); i++ {
d.Authorities = append(d.Authorities, DNSResourceRecord{})
if offset, err = d.Authorities[i].decode(data, offset, df, &d.buffer); err != nil {
d.Authorities = d.Authorities[:i] // strip off erroneous value
return err
}
}
for i := 0; i < int(d.ARCount); i++ {
d.Additionals = append(d.Additionals, DNSResourceRecord{})
if offset, err = d.Additionals[i].decode(data, offset, df, &d.buffer); err != nil {
d.Additionals = d.Additionals[:i] // strip off erroneous value
return err
}
}
if uint16(len(d.Questions)) != d.QDCount {
return errors.New("Invalid query decoding, not the right number of questions")
} else if uint16(len(d.Answers)) != d.ANCount {
return errors.New("Invalid query decoding, not the right number of answers")
} else if uint16(len(d.Authorities)) != d.NSCount {
return errors.New("Invalid query decoding, not the right number of authorities")
} else if uint16(len(d.Additionals)) != d.ARCount {
return errors.New("Invalid query decoding, not the right number of additionals info")
}
return nil
}
// CanDecode implements gopacket.DecodingLayer.
func (d *DNS) CanDecode() gopacket.LayerClass {
return LayerTypeDNS
}
// NextLayerType implements gopacket.DecodingLayer.
func (d *DNS) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// Payload returns nil.
func (d *DNS) Payload() []byte {
return nil
}
func b2i(b bool) int {
if b {
return 1
}
return 0
}
func recSize(rr *DNSResourceRecord) int {
switch rr.Type {
case DNSTypeA:
return 4
case DNSTypeAAAA:
return 16
case DNSTypeNS:
return len(rr.NS) + 2
case DNSTypeCNAME:
return len(rr.CNAME) + 2
case DNSTypePTR:
return len(rr.PTR) + 2
case DNSTypeSOA:
return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20
case DNSTypeMX:
return 2 + len(rr.MX.Name) + 2
case DNSTypeTXT:
l := len(rr.TXTs)
for _, txt := range rr.TXTs {
l += len(txt)
}
return l
case DNSTypeSRV:
return 6 + len(rr.SRV.Name) + 2
}
return 0
}
func computeSize(recs []DNSResourceRecord) int {
sz := 0
for _, rr := range recs {
sz += len(rr.Name) + 14
sz += recSize(&rr)
}
return sz
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
dsz := 0
for _, q := range d.Questions {
dsz += len(q.Name) + 6
}
dsz += computeSize(d.Answers)
dsz += computeSize(d.Authorities)
dsz += computeSize(d.Additionals)
bytes, err := b.PrependBytes(12 + dsz)
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes, d.ID)
bytes[2] = byte((b2i(d.QR) << 7) | (int(d.OpCode) << 3) | (b2i(d.AA) << 2) | (b2i(d.TC) << 1) | b2i(d.RD))
bytes[3] = byte((b2i(d.RA) << 7) | (int(d.Z) << 4) | int(d.ResponseCode))
if opts.FixLengths {
d.QDCount = uint16(len(d.Questions))
d.ANCount = uint16(len(d.Answers))
d.NSCount = uint16(len(d.Authorities))
d.ARCount = uint16(len(d.Additionals))
}
binary.BigEndian.PutUint16(bytes[4:], d.QDCount)
binary.BigEndian.PutUint16(bytes[6:], d.ANCount)
binary.BigEndian.PutUint16(bytes[8:], d.NSCount)
binary.BigEndian.PutUint16(bytes[10:], d.ARCount)
off := 12
for _, qd := range d.Questions {
n := qd.encode(bytes, off)
off += n
}
for i := range d.Answers {
// done this way so we can modify DNSResourceRecord to fix
// lengths if requested
qa := &d.Answers[i]
n, err := qa.encode(bytes, off, opts)
if err != nil {
return err
}
off += n
}
for i := range d.Authorities {
qa := &d.Authorities[i]
n, err := qa.encode(bytes, off, opts)
if err != nil {
return err
}
off += n
}
for i := range d.Additionals {
qa := &d.Additionals[i]
n, err := qa.encode(bytes, off, opts)
if err != nil {
return err
}
off += n
}
return nil
}
var errMaxRecursion = errors.New("max DNS recursion level hit")
const maxRecursionLevel = 255
func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) {
if level > maxRecursionLevel {
return nil, 0, errMaxRecursion
} else if offset >= len(data) {
return nil, 0, errors.New("dns name offset too high")
} else if offset < 0 {
return nil, 0, errors.New("dns name offset is negative")
}
start := len(*buffer)
index := offset
if data[index] == 0x00 {
return nil, index + 1, nil
}
loop:
for data[index] != 0x00 {
switch data[index] & 0xc0 {
default:
/* RFC 1035
A domain name represented as a sequence of labels, where
each label consists of a length octet followed by that
number of octets. The domain name terminates with the
zero length octet for the null label of the root. Note
that this field may be an odd number of octets; no
padding is used.
*/
index2 := index + int(data[index]) + 1
if index2-offset > 255 {
return nil, 0, errors.New("dns name is too long")
} else if index2 < index+1 || index2 > len(data) {
return nil, 0, errors.New("dns name uncomputable: invalid index")
}
*buffer = append(*buffer, '.')
*buffer = append(*buffer, data[index+1:index2]...)
index = index2
case 0xc0:
/* RFC 1035
The pointer takes the form of a two octet sequence.
The first two bits are ones. This allows a pointer to
be distinguished from a label, since the label must
begin with two zero bits because labels are restricted
to 63 octets or less. (The 10 and 01 combinations are
reserved for future use.) The OFFSET field specifies
an offset from the start of the message (i.e., the
first octet of the ID field in the domain header). A
zero offset specifies the first byte of the ID field,
etc.
The compression scheme allows a domain name in a message to be
represented as either:
- a sequence of labels ending in a zero octet
- a pointer
- a sequence of labels ending with a pointer
*/
if index+2 > len(data) {
return nil, 0, errors.New("dns offset pointer too high")
}
offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff)
if offsetp > len(data) {
return nil, 0, errors.New("dns offset pointer too high")
}
// This looks a little tricky, but actually isn't. Because of how
// decodeName is written, calling it appends the decoded name to the
// current buffer. We already have the start of the buffer, then, so
// once this call is done buffer[start:] will contain our full name.
_, _, err := decodeName(data, offsetp, buffer, level+1)
if err != nil {
return nil, 0, err
}
index++ // pointer is two bytes, so add an extra byte here.
break loop
/* EDNS, or other DNS option ? */
case 0x40: // RFC 2673
return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)",
data[index], index)
case 0x80:
return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)",
data[index], index)
}
if index >= len(data) {
return nil, 0, errors.New("dns index walked out of range")
}
}
if len(*buffer) <= start {
return nil, 0, errors.New("no dns data found for name")
}
return (*buffer)[start+1:], index + 1, nil
}
// DNSQuestion wraps a single request (question) within a DNS query.
type DNSQuestion struct {
Name []byte
Type DNSType
Class DNSClass
}
func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
name, endq, err := decodeName(data, offset, buffer, 1)
if err != nil {
return 0, err
}
q.Name = name
q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
return endq + 4, nil
}
func (q *DNSQuestion) encode(data []byte, offset int) int {
noff := encodeName(q.Name, data, offset)
binary.BigEndian.PutUint16(data[noff:], uint16(q.Type))
binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class))
return len(q.Name) + 6
}
// DNSResourceRecord
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | |
// / /
// / NAME /
// | |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | TYPE |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | CLASS |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | TTL |
// | |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | RDLENGTH |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
// / RDATA /
// / /
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// DNSResourceRecord wraps the data from a single DNS resource within a
// response.
type DNSResourceRecord struct {
// Header
Name []byte
Type DNSType
Class DNSClass
TTL uint32
// RDATA Raw Values
DataLength uint16
Data []byte
// RDATA Decoded Values
IP net.IP
NS, CNAME, PTR []byte
TXTs [][]byte
SOA DNSSOA
SRV DNSSRV
MX DNSMX
// Undecoded TXT for backward compatibility
TXT []byte
}
// decode decodes the resource record, returning the total length of the record.
func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
name, endq, err := decodeName(data, offset, buffer, 1)
if err != nil {
return 0, err
}
rr.Name = name
rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8])
rr.DataLength = binary.BigEndian.Uint16(data[endq+8 : endq+10])
end := endq + 10 + int(rr.DataLength)
if end > len(data) {
return 0, fmt.Errorf("resource record length exceeds data")
}
rr.Data = data[endq+10 : end]
if err = rr.decodeRData(data, endq+10, buffer); err != nil {
return 0, err
}
return endq + 10 + int(rr.DataLength), nil
}
func encodeName(name []byte, data []byte, offset int) int {
l := 0
for i := range name {
if name[i] == '.' {
data[offset+i-l] = byte(l)
l = 0
} else {
// skip one to write the length
data[offset+i+1] = name[i]
l++
}
}
// length for final portion
data[offset+len(name)-l] = byte(l)
data[offset+len(name)+1] = 0x00 // terminal
return offset + len(name) + 2
}
func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) {
noff := encodeName(rr.Name, data, offset)
binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type))
binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class))
binary.BigEndian.PutUint32(data[noff+4:], uint32(rr.TTL))
switch rr.Type {
case DNSTypeA:
copy(data[noff+10:], rr.IP.To4())
case DNSTypeAAAA:
copy(data[noff+10:], rr.IP)
case DNSTypeNS:
encodeName(rr.NS, data, noff+10)
case DNSTypeCNAME:
encodeName(rr.CNAME, data, noff+10)
case DNSTypePTR:
encodeName(rr.PTR, data, noff+10)
case DNSTypeSOA:
noff2 := encodeName(rr.SOA.MName, data, noff+10)
noff2 = encodeName(rr.SOA.RName, data, noff2)
binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial)
binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh)
binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry)
binary.BigEndian.PutUint32(data[noff2+12:], rr.SOA.Expire)
binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum)
case DNSTypeMX:
binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference)
encodeName(rr.MX.Name, data, noff+12)
case DNSTypeTXT:
noff2 := noff + 10
for _, txt := range rr.TXTs {
data[noff2] = byte(len(txt))
copy(data[noff2+1:], txt)
noff2 += 1 + len(txt)
}
case DNSTypeSRV:
binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority)
binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight)
binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port)
encodeName(rr.SRV.Name, data, noff+16)
default:
return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type)
}
// DataLength
dSz := recSize(rr)
binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz))
if opts.FixLengths {
rr.DataLength = uint16(dSz)
}
return len(rr.Name) + 1 + 11 + dSz, nil
}
func (rr *DNSResourceRecord) String() string {
if rr.Class == DNSClassIN {
switch rr.Type {
case DNSTypeA, DNSTypeAAAA:
return rr.IP.String()
case DNSTypeNS:
return "NS " + string(rr.NS)
case DNSTypeCNAME:
return "CNAME " + string(rr.CNAME)
case DNSTypePTR:
return "PTR " + string(rr.PTR)
case DNSTypeTXT:
return "TXT " + string(rr.TXT)
}
}
return fmt.Sprintf("<%v, %v>", rr.Class, rr.Type)
}
func decodeCharacterStrings(data []byte) ([][]byte, error) {
strings := make([][]byte, 0, 1)
end := len(data)
for index, index2 := 0, 0; index != end; index = index2 {
index2 = index + 1 + int(data[index]) // index increases by 1..256 and does not overflow
if index2 > end {
return nil, errors.New("Insufficient data for a <character-string>")
}
strings = append(strings, data[index+1:index2])
}
return strings, nil
}
func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error {
switch rr.Type {
case DNSTypeA:
rr.IP = rr.Data
case DNSTypeAAAA:
rr.IP = rr.Data
case DNSTypeTXT, DNSTypeHINFO:
rr.TXT = rr.Data
txts, err := decodeCharacterStrings(rr.Data)
if err != nil {
return err
}
rr.TXTs = txts
case DNSTypeNS:
name, _, err := decodeName(data, offset, buffer, 1)
if err != nil {
return err
}
rr.NS = name
case DNSTypeCNAME:
name, _, err := decodeName(data, offset, buffer, 1)
if err != nil {
return err
}
rr.CNAME = name
case DNSTypePTR:
name, _, err := decodeName(data, offset, buffer, 1)
if err != nil {
return err
}
rr.PTR = name
case DNSTypeSOA:
name, endq, err := decodeName(data, offset, buffer, 1)
if err != nil {
return err
}
rr.SOA.MName = name
name, endq, err = decodeName(data, endq, buffer, 1)
if err != nil {
return err
}
rr.SOA.RName = name
rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4])
rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8])
rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12])
rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16])
rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20])
case DNSTypeMX:
rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2])
name, _, err := decodeName(data, offset+2, buffer, 1)
if err != nil {
return err
}
rr.MX.Name = name
case DNSTypeSRV:
rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6])
name, _, err := decodeName(data, offset+6, buffer, 1)
if err != nil {
return err
}
rr.SRV.Name = name
}
return nil
}
// DNSSOA is a Start of Authority record. Each domain requires a SOA record at
// the cutover where a domain is delegated from its parent.
type DNSSOA struct {
MName, RName []byte
Serial, Refresh, Retry, Expire, Minimum uint32
}
// DNSSRV is a Service record, defining a location (hostname/port) of a
// server/service.
type DNSSRV struct {
Priority, Weight, Port uint16
Name []byte
}
// DNSMX is a mail exchange record, defining a mail server for a recipient's
// domain.
type DNSMX struct {
Preference uint16
Name []byte
}

@ -0,0 +1,61 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
/*
Package layers provides decoding layers for many common protocols.
The layers package contains decode implementations for a number of different
types of packet layers. Users of gopacket will almost always want to also use
layers to actually decode packet data into useful pieces. To see the set of
protocols that gopacket/layers is currently able to decode,
look at the set of LayerTypes defined in the Variables sections. The
layers package also defines endpoints for many of the common packet layers
that have source/destination addresses associated with them, for example IPv4/6
(IPs) and TCP/UDP (ports).
Finally, layers contains a number of useful enumerations (IPProtocol,
EthernetType, LinkType, PPPType, etc...). Many of these implement the
gopacket.Decoder interface, so they can be passed into gopacket as decoders.
Most common protocol layers are named using acronyms or other industry-common
names (IPv4, TCP, PPP). Some of the less common ones have their names expanded
(CiscoDiscoveryProtocol).
For certain protocols, sub-parts of the protocol are split out into their own
layers (SCTP, for example). This is done mostly in cases where portions of the
protocol may fulfill the capabilities of interesting layers (SCTPData implements
ApplicationLayer, while base SCTP implements TransportLayer), or possibly
because splitting a protocol into a few layers makes decoding easier.
This package is meant to be used with its parent,
http://github.com/google/gopacket.
Port Types
Instead of using raw uint16 or uint8 values for ports, we use a different port
type for every protocol, for example TCPPort and UDPPort. This allows us to
override string behavior for each port, which we do by setting up port name
maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with
their protocol names, and their String function displays these names:
p := TCPPort(80)
fmt.Printf("Number: %d String: %v", p, p)
// Prints: "Number: 80 String: 80(http)"
Modifying Decode Behavior
layers links together decoding through its enumerations. For example, after
decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder.
All enumerations that act as decoders, like EthernetType, can be modified by
users depending on their preferences. For example, if you have a spiffy new
IPv4 decoder that works way better than the one built into layers, you can do
this:
var mySpiffyIPv4Decoder gopacket.Decoder = ...
layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder
This will make all future ethernet packets use your new decoder to decode IPv4
packets, instead of the built-in decoder used by gopacket.
*/
package layers

File diff suppressed because it is too large Load Diff

@ -0,0 +1,71 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
// Dot1Q is the packet layer for 802.1Q VLAN headers.
type Dot1Q struct {
BaseLayer
Priority uint8
DropEligible bool
VLANIdentifier uint16
Type EthernetType
}
// LayerType returns gopacket.LayerTypeDot1Q
func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
d.Priority = (data[0] & 0xE0) >> 5
d.DropEligible = data[0]&0x10 != 0
d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4]))
d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (d *Dot1Q) CanDecode() gopacket.LayerClass {
return LayerTypeDot1Q
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (d *Dot1Q) NextLayerType() gopacket.LayerType {
return d.Type.LayerType()
}
func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error {
d := &Dot1Q{}
return decodingLayerDecoder(d, data, p)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
if d.VLANIdentifier > 0xFFF {
return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier)
}
firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier
if d.DropEligible {
firstBytes |= 0x1000
}
binary.BigEndian.PutUint16(bytes, firstBytes)
binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type))
return nil
}

@ -0,0 +1,106 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
type EAPCode uint8
type EAPType uint8
const (
EAPCodeRequest EAPCode = 1
EAPCodeResponse EAPCode = 2
EAPCodeSuccess EAPCode = 3
EAPCodeFailure EAPCode = 4
// EAPTypeNone means that this EAP layer has no Type or TypeData.
// Success and Failure EAPs will have this set.
EAPTypeNone EAPType = 0
EAPTypeIdentity EAPType = 1
EAPTypeNotification EAPType = 2
EAPTypeNACK EAPType = 3
EAPTypeOTP EAPType = 4
EAPTypeTokenCard EAPType = 5
)
// EAP defines an Extensible Authentication Protocol (rfc 3748) layer.
type EAP struct {
BaseLayer
Code EAPCode
Id uint8
Length uint16
Type EAPType
TypeData []byte
}
// LayerType returns LayerTypeEAP.
func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
// DecodeFromBytes decodes the given bytes into this layer.
func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
e.Code = EAPCode(data[0])
e.Id = data[1]
e.Length = binary.BigEndian.Uint16(data[2:4])
switch {
case e.Length > 4:
e.Type = EAPType(data[4])
e.TypeData = data[5:]
case e.Length == 4:
e.Type = 0
e.TypeData = nil
default:
return fmt.Errorf("invalid EAP length %d", e.Length)
}
e.BaseLayer.Contents = data[:e.Length]
e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if opts.FixLengths {
e.Length = uint16(len(e.TypeData) + 1)
}
size := len(e.TypeData) + 4
if size > 4 {
size++
}
bytes, err := b.PrependBytes(size)
if err != nil {
return err
}
bytes[0] = byte(e.Code)
bytes[1] = e.Id
binary.BigEndian.PutUint16(bytes[2:], e.Length)
if size > 4 {
bytes[4] = byte(e.Type)
copy(bytes[5:], e.TypeData)
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (e *EAP) CanDecode() gopacket.LayerClass {
return LayerTypeEAP
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (e *EAP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
func decodeEAP(data []byte, p gopacket.PacketBuilder) error {
e := &EAP{}
return decodingLayerDecoder(e, data, p)
}

@ -0,0 +1,57 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// EAPOL defines an EAP over LAN (802.1x) layer.
type EAPOL struct {
BaseLayer
Version uint8
Type EAPOLType
Length uint16
}
// LayerType returns LayerTypeEAPOL.
func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL }
// DecodeFromBytes decodes the given bytes into this layer.
func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
e.Version = data[0]
e.Type = EAPOLType(data[1])
e.Length = binary.BigEndian.Uint16(data[2:4])
e.BaseLayer = BaseLayer{data[:4], data[4:]}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer
func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, _ := b.PrependBytes(4)
bytes[0] = e.Version
bytes[1] = byte(e.Type)
binary.BigEndian.PutUint16(bytes[2:], e.Length)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (e *EAPOL) CanDecode() gopacket.LayerClass {
return LayerTypeEAPOL
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (e *EAPOL) NextLayerType() gopacket.LayerType {
return e.Type.LayerType()
}
func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error {
e := &EAPOL{}
return decodingLayerDecoder(e, data, p)
}

@ -0,0 +1,97 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
"net"
"strconv"
)
var (
// We use two different endpoint types for IPv4 vs IPv6 addresses, so that
// ordering with endpointA.LessThan(endpointB) sanely groups all IPv4
// addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses.
EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string {
return net.IP(b).String()
}})
EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string {
return net.IP(b).String()
}})
EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string {
return net.HardwareAddr(b).String()
}})
EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string {
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
}})
EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string {
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
}})
EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string {
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
}})
EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string {
return strconv.Itoa(int(b[0]))
}})
EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string {
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
}})
EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string {
return "point"
}})
)
// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address.
// It returns gopacket.InvalidEndpoint if the IP address is invalid.
func NewIPEndpoint(a net.IP) gopacket.Endpoint {
ipv4 := a.To4()
if ipv4 != nil {
return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4))
}
ipv6 := a.To16()
if ipv6 != nil {
return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6))
}
return gopacket.InvalidEndpoint
}
// NewMACEndpoint returns a new MAC address endpoint.
func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint {
return gopacket.NewEndpoint(EndpointMAC, []byte(a))
}
func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint {
return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)})
}
// NewTCPPortEndpoint returns an endpoint based on a TCP port.
func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint {
return newPortEndpoint(EndpointTCPPort, uint16(p))
}
// NewUDPPortEndpoint returns an endpoint based on a UDP port.
func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint {
return newPortEndpoint(EndpointUDPPort, uint16(p))
}
// NewSCTPPortEndpoint returns an endpoint based on a SCTP port.
func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint {
return newPortEndpoint(EndpointSCTPPort, uint16(p))
}
// NewRUDPPortEndpoint returns an endpoint based on a RUDP port.
func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint {
return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)})
}
// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port.
func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint {
return newPortEndpoint(EndpointUDPLitePort, uint16(p))
}

@ -0,0 +1,562 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"errors"
"fmt"
"github.com/google/gopacket"
)
// EnumMetadata keeps track of a set of metadata for each enumeration value
// for protocol enumerations.
type EnumMetadata struct {
// DecodeWith is the decoder to use to decode this protocol's data.
DecodeWith gopacket.Decoder
// Name is the name of the enumeration value.
Name string
// LayerType is the layer type implied by the given enum.
LayerType gopacket.LayerType
}
// errorFunc returns a decoder that spits out a specific error message.
func errorFunc(msg string) gopacket.Decoder {
var e = errors.New(msg)
return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error {
return e
})
}
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
// for any type it supports.
type EthernetType uint16
const (
// EthernetTypeLLC is not an actual ethernet type. It is instead a
// placeholder we use in Ethernet frames that use the 802.3 standard of
// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
EthernetTypeLLC EthernetType = 0
EthernetTypeIPv4 EthernetType = 0x0800
EthernetTypeARP EthernetType = 0x0806
EthernetTypeIPv6 EthernetType = 0x86DD
EthernetTypeCiscoDiscovery EthernetType = 0x2000
EthernetTypeNortelDiscovery EthernetType = 0x01a2
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
EthernetTypeDot1Q EthernetType = 0x8100
EthernetTypePPPoEDiscovery EthernetType = 0x8863
EthernetTypePPPoESession EthernetType = 0x8864
EthernetTypeMPLSUnicast EthernetType = 0x8847
EthernetTypeMPLSMulticast EthernetType = 0x8848
EthernetTypeEAPOL EthernetType = 0x888e
EthernetTypeQinQ EthernetType = 0x88a8
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
EthernetTypeEthernetCTP EthernetType = 0x9000
)
// IPProtocol is an enumeration of IP protocol values, and acts as a decoder
// for any type it supports.
type IPProtocol uint8
const (
IPProtocolIPv6HopByHop IPProtocol = 0
IPProtocolICMPv4 IPProtocol = 1
IPProtocolIGMP IPProtocol = 2
IPProtocolIPv4 IPProtocol = 4
IPProtocolTCP IPProtocol = 6
IPProtocolUDP IPProtocol = 17
IPProtocolRUDP IPProtocol = 27
IPProtocolIPv6 IPProtocol = 41
IPProtocolIPv6Routing IPProtocol = 43
IPProtocolIPv6Fragment IPProtocol = 44
IPProtocolGRE IPProtocol = 47
IPProtocolESP IPProtocol = 50
IPProtocolAH IPProtocol = 51
IPProtocolICMPv6 IPProtocol = 58
IPProtocolNoNextHeader IPProtocol = 59
IPProtocolIPv6Destination IPProtocol = 60
IPProtocolIPIP IPProtocol = 94
IPProtocolEtherIP IPProtocol = 97
IPProtocolVRRP IPProtocol = 112
IPProtocolSCTP IPProtocol = 132
IPProtocolUDPLite IPProtocol = 136
IPProtocolMPLSInIP IPProtocol = 137
)
// LinkType is an enumeration of link types, and acts as a decoder for any
// link type it supports.
type LinkType uint8
const (
// According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html
LinkTypeNull LinkType = 0
LinkTypeEthernet LinkType = 1
LinkTypeAX25 LinkType = 3
LinkTypeTokenRing LinkType = 6
LinkTypeArcNet LinkType = 7
LinkTypeSLIP LinkType = 8
LinkTypePPP LinkType = 9
LinkTypeFDDI LinkType = 10
LinkTypePPP_HDLC LinkType = 50
LinkTypePPPEthernet LinkType = 51
LinkTypeATM_RFC1483 LinkType = 100
LinkTypeRaw LinkType = 101
LinkTypeC_HDLC LinkType = 104
LinkTypeIEEE802_11 LinkType = 105
LinkTypeFRelay LinkType = 107
LinkTypeLoop LinkType = 108
LinkTypeLinuxSLL LinkType = 113
LinkTypeLTalk LinkType = 114
LinkTypePFLog LinkType = 117
LinkTypePrismHeader LinkType = 119
LinkTypeIPOverFC LinkType = 122
LinkTypeSunATM LinkType = 123
LinkTypeIEEE80211Radio LinkType = 127
LinkTypeARCNetLinux LinkType = 129
LinkTypeIPOver1394 LinkType = 138
LinkTypeMTP2Phdr LinkType = 139
LinkTypeMTP2 LinkType = 140
LinkTypeMTP3 LinkType = 141
LinkTypeSCCP LinkType = 142
LinkTypeDOCSIS LinkType = 143
LinkTypeLinuxIRDA LinkType = 144
LinkTypeLinuxLAPD LinkType = 177
LinkTypeLinuxUSB LinkType = 220
LinkTypeIPv4 LinkType = 228
LinkTypeIPv6 LinkType = 229
)
// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516
type PPPoECode uint8
const (
PPPoECodePADI PPPoECode = 0x09
PPPoECodePADO PPPoECode = 0x07
PPPoECodePADR PPPoECode = 0x19
PPPoECodePADS PPPoECode = 0x65
PPPoECodePADT PPPoECode = 0xA7
PPPoECodeSession PPPoECode = 0x00
)
// PPPType is an enumeration of PPP type values, and acts as a decoder for any
// type it supports.
type PPPType uint16
const (
PPPTypeIPv4 PPPType = 0x0021
PPPTypeIPv6 PPPType = 0x0057
PPPTypeMPLSUnicast PPPType = 0x0281
PPPTypeMPLSMulticast PPPType = 0x0283
)
// SCTPChunkType is an enumeration of chunk types inside SCTP packets.
type SCTPChunkType uint8
const (
SCTPChunkTypeData SCTPChunkType = 0
SCTPChunkTypeInit SCTPChunkType = 1
SCTPChunkTypeInitAck SCTPChunkType = 2
SCTPChunkTypeSack SCTPChunkType = 3
SCTPChunkTypeHeartbeat SCTPChunkType = 4
SCTPChunkTypeHeartbeatAck SCTPChunkType = 5
SCTPChunkTypeAbort SCTPChunkType = 6
SCTPChunkTypeShutdown SCTPChunkType = 7
SCTPChunkTypeShutdownAck SCTPChunkType = 8
SCTPChunkTypeError SCTPChunkType = 9
SCTPChunkTypeCookieEcho SCTPChunkType = 10
SCTPChunkTypeCookieAck SCTPChunkType = 11
SCTPChunkTypeShutdownComplete SCTPChunkType = 14
)
// FDDIFrameControl is an enumeration of FDDI frame control bytes.
type FDDIFrameControl uint8
const (
FDDIFrameControlLLC FDDIFrameControl = 0x50
)
// EAPOLType is an enumeration of EAPOL packet types.
type EAPOLType uint8
const (
EAPOLTypeEAP EAPOLType = 0
EAPOLTypeStart EAPOLType = 1
EAPOLTypeLogOff EAPOLType = 2
EAPOLTypeKey EAPOLType = 3
EAPOLTypeASFAlert EAPOLType = 4
)
// ProtocolFamily is the set of values defined as PF_* in sys/socket.h
type ProtocolFamily uint8
const (
ProtocolFamilyIPv4 ProtocolFamily = 2
// BSDs use different values for INET6... glory be. These values taken from
// tcpdump 4.3.0.
ProtocolFamilyIPv6BSD ProtocolFamily = 24
ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28
ProtocolFamilyIPv6Darwin ProtocolFamily = 30
ProtocolFamilyIPv6Linux ProtocolFamily = 10
)
// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields.
// By combining these two fields together into a single type, we're able to
// provide a String function that correctly displays the subtype given the
// top-level type.
//
// If you just care about the top-level type, use the MainType function.
type Dot11Type uint8
// MainType strips the subtype information from the given type,
// returning just the overarching type (Mgmt, Ctrl, Data, Reserved).
func (d Dot11Type) MainType() Dot11Type {
return d & dot11TypeMask
}
const (
Dot11TypeMgmt Dot11Type = 0x00
Dot11TypeCtrl Dot11Type = 0x01
Dot11TypeData Dot11Type = 0x02
Dot11TypeReserved Dot11Type = 0x03
dot11TypeMask = 0x03
// The following are type/subtype conglomerations.
// Management
Dot11TypeMgmtAssociationReq Dot11Type = 0x00
Dot11TypeMgmtAssociationResp Dot11Type = 0x04
Dot11TypeMgmtReassociationReq Dot11Type = 0x08
Dot11TypeMgmtReassociationResp Dot11Type = 0x0c
Dot11TypeMgmtProbeReq Dot11Type = 0x10
Dot11TypeMgmtProbeResp Dot11Type = 0x14
Dot11TypeMgmtMeasurementPilot Dot11Type = 0x18
Dot11TypeMgmtBeacon Dot11Type = 0x20
Dot11TypeMgmtATIM Dot11Type = 0x24
Dot11TypeMgmtDisassociation Dot11Type = 0x28
Dot11TypeMgmtAuthentication Dot11Type = 0x2c
Dot11TypeMgmtDeauthentication Dot11Type = 0x30
Dot11TypeMgmtAction Dot11Type = 0x34
Dot11TypeMgmtActionNoAck Dot11Type = 0x38
// Control
Dot11TypeCtrlWrapper Dot11Type = 0x1d
Dot11TypeCtrlBlockAckReq Dot11Type = 0x21
Dot11TypeCtrlBlockAck Dot11Type = 0x25
Dot11TypeCtrlPowersavePoll Dot11Type = 0x29
Dot11TypeCtrlRTS Dot11Type = 0x2d
Dot11TypeCtrlCTS Dot11Type = 0x31
Dot11TypeCtrlAck Dot11Type = 0x35
Dot11TypeCtrlCFEnd Dot11Type = 0x39
Dot11TypeCtrlCFEndAck Dot11Type = 0x3d
// Data
Dot11TypeDataCFAck Dot11Type = 0x06
Dot11TypeDataCFPoll Dot11Type = 0x0a
Dot11TypeDataCFAckPoll Dot11Type = 0x0e
Dot11TypeDataNull Dot11Type = 0x12
Dot11TypeDataCFAckNoData Dot11Type = 0x16
Dot11TypeDataCFPollNoData Dot11Type = 0x1a
Dot11TypeDataCFAckPollNoData Dot11Type = 0x1e
Dot11TypeDataQOSData Dot11Type = 0x22
Dot11TypeDataQOSDataCFAck Dot11Type = 0x26
Dot11TypeDataQOSDataCFPoll Dot11Type = 0x2a
Dot11TypeDataQOSDataCFAckPoll Dot11Type = 0x2e
Dot11TypeDataQOSNull Dot11Type = 0x32
Dot11TypeDataQOSCFPollNoData Dot11Type = 0x3a
Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e
)
var (
// Each of the following arrays contains mappings of how to handle enum
// values for various enum types in gopacket/layers.
//
// So, EthernetTypeMetadata[2] contains information on how to handle EthernetType
// 2, including which name to give it and which decoder to use to decode
// packet data of that type. These arrays are filled by default with all of the
// protocols gopacket/layers knows how to handle, but users of the library can
// add new decoders or override existing ones. For example, if you write a better
// TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith
// with your new decoder, and all gopacket/layers decoding will use your new
// decoder whenever they encounter that IPProtocol.
EthernetTypeMetadata [65536]EnumMetadata
IPProtocolMetadata [256]EnumMetadata
SCTPChunkTypeMetadata [256]EnumMetadata
PPPTypeMetadata [65536]EnumMetadata
PPPoECodeMetadata [256]EnumMetadata
LinkTypeMetadata [256]EnumMetadata
FDDIFrameControlMetadata [256]EnumMetadata
EAPOLTypeMetadata [256]EnumMetadata
ProtocolFamilyMetadata [256]EnumMetadata
Dot11TypeMetadata [256]EnumMetadata
USBTypeMetadata [256]EnumMetadata
)
func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
return EthernetTypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a EthernetType) String() string {
return EthernetTypeMetadata[a].Name
}
func (a EthernetType) LayerType() gopacket.LayerType {
return EthernetTypeMetadata[a].LayerType
}
func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
return IPProtocolMetadata[a].DecodeWith.Decode(data, p)
}
func (a IPProtocol) String() string {
return IPProtocolMetadata[a].Name
}
func (a IPProtocol) LayerType() gopacket.LayerType {
return IPProtocolMetadata[a].LayerType
}
func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a SCTPChunkType) String() string {
return SCTPChunkTypeMetadata[a].Name
}
func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
return PPPTypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a PPPType) String() string {
return PPPTypeMetadata[a].Name
}
func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a LinkType) String() string {
return LinkTypeMetadata[a].Name
}
func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
return PPPoECodeMetadata[a].DecodeWith.Decode(data, p)
}
func (a PPPoECode) String() string {
return PPPoECodeMetadata[a].Name
}
func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p)
}
func (a FDDIFrameControl) String() string {
return FDDIFrameControlMetadata[a].Name
}
func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a EAPOLType) String() string {
return EAPOLTypeMetadata[a].Name
}
func (a EAPOLType) LayerType() gopacket.LayerType {
return EAPOLTypeMetadata[a].LayerType
}
func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p)
}
func (a ProtocolFamily) String() string {
return ProtocolFamilyMetadata[a].Name
}
func (a ProtocolFamily) LayerType() gopacket.LayerType {
return ProtocolFamilyMetadata[a].LayerType
}
func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
return Dot11TypeMetadata[a].DecodeWith.Decode(data, p)
}
func (a Dot11Type) String() string {
return Dot11TypeMetadata[a].Name
}
func (a Dot11Type) LayerType() gopacket.LayerType {
return Dot11TypeMetadata[a].LayerType
}
// Decode a raw v4 or v6 IP packet.
func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error {
version := data[0] >> 4
switch version {
case 4:
return decodeIPv4(data, p)
case 6:
return decodeIPv6(data, p)
}
return fmt.Errorf("Invalid IP packet version %v", version)
}
func init() {
// Here we link up all enumerations with their respective names and decoders.
for i := 0; i < 65536; i++ {
EthernetTypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode ethernet type %d", i)),
Name: fmt.Sprintf("UnknownEthernetType(%d)", i),
}
PPPTypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPP type %d", i)),
Name: fmt.Sprintf("UnknownPPPType(%d)", i),
}
}
for i := 0; i < 256; i++ {
IPProtocolMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode IP protocol %d", i)),
Name: fmt.Sprintf("UnknownIPProtocol(%d)", i),
}
SCTPChunkTypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode SCTP chunk type %d", i)),
Name: fmt.Sprintf("UnknownSCTPChunkType(%d)", i),
}
PPPoECodeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPPoE code %d", i)),
Name: fmt.Sprintf("UnknownPPPoECode(%d)", i),
}
LinkTypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode link type %d", i)),
Name: fmt.Sprintf("UnknownLinkType(%d)", i),
}
FDDIFrameControlMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode FDDI frame control %d", i)),
Name: fmt.Sprintf("UnknownFDDIFrameControl(%d)", i),
}
EAPOLTypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode EAPOL type %d", i)),
Name: fmt.Sprintf("UnknownEAPOLType(%d)", i),
}
ProtocolFamilyMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode protocol family %d", i)),
Name: fmt.Sprintf("UnknownProtocolFamily(%d)", i),
}
Dot11TypeMetadata[i] = EnumMetadata{
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode Dot11 type %d", i)),
Name: fmt.Sprintf("UnknownDot11Type(%d)", i),
}
}
EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC}
EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP}
EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP}
EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery}
EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery}
EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery}
EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS}
EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS}
EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP}
IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4}
IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6}
IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP}
IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP}
IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP}
IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE}
IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop}
IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing}
IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment}
IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination}
IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH}
IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP}
IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite}
IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS}
IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload}
IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP}
IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP}
SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"}
SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"}
SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"}
SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"}
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"}
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"}
SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"}
SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"}
SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"}
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"}
SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"}
SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"}
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"}
PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"}
PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"}
PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"}
PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"}
PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"}
LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"}
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"}
LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"}
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"}
LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}
LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"}
LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"}
FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"}
EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP}
ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq}
Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp}
Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq}
Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp}
Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq}
Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp}
Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot}
Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon}
Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM}
Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation}
Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication}
Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication}
Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction}
Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck}
Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl}
Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl}
Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq}
Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck}
Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll}
Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS}
Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS}
Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck}
Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd}
Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck}
Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data}
Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck}
Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll}
Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll}
Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull}
Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData}
Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData}
Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData}
Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData}
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck}
Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll}
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll}
Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull}
Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData}
Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData}
USBTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
USBTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
USBTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
}

@ -0,0 +1,45 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
type EtherIP struct {
BaseLayer
Version uint8
Reserved uint16
}
// LayerType returns gopacket.LayerTypeEtherIP.
func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
// DecodeFromBytes decodes the given bytes into this layer.
func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
e.Version = data[0] >> 4
e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
e.BaseLayer = BaseLayer{data[:2], data[2:]}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (e *EtherIP) CanDecode() gopacket.LayerClass {
return LayerTypeEtherIP
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (e *EtherIP) NextLayerType() gopacket.LayerType {
return LayerTypeEthernet
}
func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
e := &EtherIP{}
return decodingLayerDecoder(e, data, p)
}

@ -0,0 +1,122 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
"net"
)
// EthernetBroadcast is the broadcast MAC address used by Ethernet.
var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
// Ethernet is the layer for Ethernet frame headers.
type Ethernet struct {
BaseLayer
SrcMAC, DstMAC net.HardwareAddr
EthernetType EthernetType
// Length is only set if a length field exists within this header. Ethernet
// headers follow two different standards, one that uses an EthernetType, the
// other which defines a length the follows with a LLC header (802.3). If the
// former is the case, we set EthernetType and Length stays 0. In the latter
// case, we set Length and EthernetType = EthernetTypeLLC.
Length uint16
}
// LayerType returns LayerTypeEthernet
func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet }
func (e *Ethernet) LinkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC)
}
func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 14 {
return errors.New("Ethernet packet too small")
}
eth.DstMAC = net.HardwareAddr(data[0:6])
eth.SrcMAC = net.HardwareAddr(data[6:12])
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
eth.BaseLayer = BaseLayer{data[:14], data[14:]}
if eth.EthernetType < 0x0600 {
eth.Length = uint16(eth.EthernetType)
eth.EthernetType = EthernetTypeLLC
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
df.SetTruncated()
} else if cmp > 0 {
// Strip off bytes at the end, since we have too many bytes
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
}
// fmt.Println(eth)
}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if len(eth.DstMAC) != 6 {
return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC)
}
if len(eth.SrcMAC) != 6 {
return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC)
}
payload := b.Bytes()
bytes, err := b.PrependBytes(14)
if err != nil {
return err
}
copy(bytes, eth.DstMAC)
copy(bytes[6:], eth.SrcMAC)
if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC {
if opts.FixLengths {
eth.Length = uint16(len(payload))
}
if eth.EthernetType != EthernetTypeLLC {
return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length)
} else if eth.Length > 0x0600 {
return fmt.Errorf("invalid ethernet length %v", eth.Length)
}
binary.BigEndian.PutUint16(bytes[12:], eth.Length)
} else {
binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType))
}
length := len(b.Bytes())
if length < 60 {
// Pad out to 60 bytes.
padding, err := b.AppendBytes(60 - length)
if err != nil {
return err
}
copy(padding, lotsOfZeros[:])
}
return nil
}
func (eth *Ethernet) CanDecode() gopacket.LayerClass {
return LayerTypeEthernet
}
func (eth *Ethernet) NextLayerType() gopacket.LayerType {
return eth.EthernetType.LayerType()
}
func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
eth := &Ethernet{}
err := eth.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(eth)
p.SetLinkLayer(eth)
return p.NextDecoder(eth.EthernetType)
}

@ -0,0 +1,41 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"github.com/google/gopacket"
"net"
)
// FDDI contains the header for FDDI frames.
type FDDI struct {
BaseLayer
FrameControl FDDIFrameControl
Priority uint8
SrcMAC, DstMAC net.HardwareAddr
}
// LayerType returns LayerTypeFDDI.
func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI }
// LinkFlow returns a new flow of type EndpointMAC.
func (f *FDDI) LinkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC)
}
func decodeFDDI(data []byte, p gopacket.PacketBuilder) error {
f := &FDDI{
FrameControl: FDDIFrameControl(data[0] & 0xF8),
Priority: data[0] & 0x07,
SrcMAC: net.HardwareAddr(data[1:7]),
DstMAC: net.HardwareAddr(data[7:13]),
BaseLayer: BaseLayer{data[:13], data[13:]},
}
p.SetLinkLayer(f)
p.AddLayer(f)
return p.NextDecoder(f.FrameControl)
}

@ -0,0 +1,109 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// +build ignore
// This binary pulls known ports from IANA, and uses them to populate
// iana_ports.go's TCPPortNames and UDPPortNames maps.
//
// go run gen.go | gofmt > iana_ports.go
package main
import (
"bytes"
"encoding/xml"
"flag"
"fmt"
"io/ioutil"
"net/http"
"os"
"strconv"
"time"
)
const fmtString = `// Copyright 2012 Google, Inc. All rights reserved.
package layers
// Created by gen.go, don't edit manually
// Generated at %s
// Fetched from %q
// TCPPortNames contains the port names for all TCP ports.
var TCPPortNames = tcpPortNames
// UDPPortNames contains the port names for all UDP ports.
var UDPPortNames = udpPortNames
// SCTPPortNames contains the port names for all SCTP ports.
var SCTPPortNames = sctpPortNames
var tcpPortNames = map[TCPPort]string{
%s}
var udpPortNames = map[UDPPort]string{
%s}
var sctpPortNames = map[SCTPPort]string{
%s}
`
var url = flag.String("url", "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml", "URL to grab port numbers from")
func main() {
fmt.Fprintf(os.Stderr, "Fetching ports from %q\n", *url)
resp, err := http.Get(*url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Fprintln(os.Stderr, "Parsing XML")
var registry struct {
Records []struct {
Protocol string `xml:"protocol"`
Number string `xml:"number"`
Name string `xml:"name"`
} `xml:"record"`
}
xml.Unmarshal(body, &registry)
var tcpPorts bytes.Buffer
var udpPorts bytes.Buffer
var sctpPorts bytes.Buffer
done := map[string]map[int]bool{
"tcp": map[int]bool{},
"udp": map[int]bool{},
"sctp": map[int]bool{},
}
for _, r := range registry.Records {
port, err := strconv.Atoi(r.Number)
if err != nil {
continue
}
if r.Name == "" {
continue
}
var b *bytes.Buffer
switch r.Protocol {
case "tcp":
b = &tcpPorts
case "udp":
b = &udpPorts
case "sctp":
b = &sctpPorts
default:
continue
}
if done[r.Protocol][port] {
continue
}
done[r.Protocol][port] = true
fmt.Fprintf(b, "\t%d: %q,\n", port, r.Name)
}
fmt.Fprintln(os.Stderr, "Writing results to stdout")
fmt.Printf(fmtString, time.Now(), *url, tcpPorts.String(), udpPorts.String(), sctpPorts.String())
}

@ -0,0 +1,3 @@
#!/bin/bash
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted

@ -0,0 +1,98 @@
// Copyright 2016 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
// Geneve Header:
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Virtual Network Identifier (VNI) | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Variable Length Options |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type Geneve struct {
BaseLayer
Version uint8 // 2 bits
OptionsLength uint8 // 6 bits
OAMPacket bool // 1 bits
CriticalOption bool // 1 bits
Protocol EthernetType // 16 bits
VNI uint32 // 24bits
Options []*GeneveOption
}
// Geneve Tunnel Options
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Option Class | Type |R|R|R| Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Variable Option Data |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type GeneveOption struct {
Class uint16 // 16 bits
Type uint8 // 8 bits
Flags uint8 // 3 bits
Length uint8 // 5 bits
Data []byte
}
// LayerType returns LayerTypeGeneve
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
opt := &GeneveOption{}
opt.Class = binary.BigEndian.Uint16(data[0:1])
opt.Type = data[2]
opt.Flags = data[3] >> 4
opt.Length = data[3] & 0xf
copy(opt.Data, data[4:opt.Length])
return opt, 4 + opt.Length
}
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
gn.Version = data[0] >> 7
gn.OptionsLength = data[0] & 0x3f
gn.OAMPacket = data[1]&0x80 > 0
gn.CriticalOption = data[1]&0x40 > 0
gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
var buf [4]byte
copy(buf[1:], data[4:7])
gn.VNI = binary.BigEndian.Uint32(buf[:])
offset, length := uint8(8), gn.OptionsLength
for length > 0 {
opt, len := decodeGeneveOption(data[offset:], gn)
gn.Options = append(gn.Options, opt)
length -= len
offset += len
}
gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}
return nil
}
func (gn *Geneve) NextLayerType() gopacket.LayerType {
return gn.Protocol.LayerType()
}
func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
gn := &Geneve{}
return decodingLayerDecoder(gn, data, p)
}

@ -0,0 +1,185 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// GRE is a Generic Routing Encapsulation header.
type GRE struct {
BaseLayer
ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute bool
RecursionControl, Flags, Version uint8
Protocol EthernetType
Checksum, Offset uint16
Key, Seq uint32
*GRERouting
}
// GRERouting is GRE routing information, present if the RoutingPresent flag is
// set.
type GRERouting struct {
AddressFamily uint16
SREOffset, SRELength uint8
RoutingInformation []byte
Next *GRERouting
}
// LayerType returns gopacket.LayerTypeGRE.
func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE }
// DecodeFromBytes decodes the given bytes into this layer.
func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
g.ChecksumPresent = data[0]&0x80 != 0
g.RoutingPresent = data[0]&0x40 != 0
g.KeyPresent = data[0]&0x20 != 0
g.SeqPresent = data[0]&0x10 != 0
g.StrictSourceRoute = data[0]&0x08 != 0
g.RecursionControl = data[0] & 0x7
g.Flags = data[1] >> 3
g.Version = data[1] & 0x7
g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
offset := 4
if g.ChecksumPresent || g.RoutingPresent {
g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2])
g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4])
offset += 4
}
if g.KeyPresent {
g.Key = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
if g.SeqPresent {
g.Seq = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
if g.RoutingPresent {
tail := &g.GRERouting
for {
sre := &GRERouting{
AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]),
SREOffset: data[offset+2],
SRELength: data[offset+3],
}
sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)]
offset += 4 + int(sre.SRELength)
if sre.AddressFamily == 0 && sre.SRELength == 0 {
break
}
(*tail) = sre
tail = &sre.Next
}
}
g.BaseLayer = BaseLayer{data[:offset], data[offset:]}
return nil
}
// SerializeTo writes the serialized form of this layer into the SerializationBuffer,
// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info.
func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
size := 4
if g.ChecksumPresent || g.RoutingPresent {
size += 4
}
if g.KeyPresent {
size += 4
}
if g.SeqPresent {
size += 4
}
if g.RoutingPresent {
r := g.GRERouting
for r != nil {
size += 4 + int(r.SRELength)
r = r.Next
}
size += 4
}
buf, err := b.PrependBytes(size)
if err != nil {
return err
}
// Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags.
buf[0] = 0
buf[1] = 0
if g.ChecksumPresent {
buf[0] |= 0x80
}
if g.RoutingPresent {
buf[0] |= 0x40
}
if g.KeyPresent {
buf[0] |= 0x20
}
if g.SeqPresent {
buf[0] |= 0x10
}
if g.StrictSourceRoute {
buf[0] |= 0x08
}
buf[0] |= g.RecursionControl
buf[1] |= g.Flags << 3
buf[1] |= g.Version
binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol))
offset := 4
if g.ChecksumPresent || g.RoutingPresent {
// Don't write the checksum value yet, as we may need to compute it,
// which requires the entire header be complete.
// Instead we zeroize the memory in case it is dirty.
buf[offset] = 0
buf[offset+1] = 0
binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset)
offset += 4
}
if g.KeyPresent {
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key)
offset += 4
}
if g.SeqPresent {
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq)
offset += 4
}
if g.RoutingPresent {
sre := g.GRERouting
for sre != nil {
binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily)
buf[offset+2] = sre.SREOffset
buf[offset+3] = sre.SRELength
copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation)
offset += 4 + int(sre.SRELength)
sre = sre.Next
}
// Terminate routing field with a "NULL" SRE.
binary.BigEndian.PutUint32(buf[offset:offset+4], 0)
}
if g.ChecksumPresent {
if opts.ComputeChecksums {
g.Checksum = tcpipChecksum(b.Bytes(), 0)
}
binary.BigEndian.PutUint16(buf[4:6], g.Checksum)
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (g *GRE) CanDecode() gopacket.LayerClass {
return LayerTypeGRE
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (g *GRE) NextLayerType() gopacket.LayerType {
return g.Protocol.LayerType()
}
func decodeGRE(data []byte, p gopacket.PacketBuilder) error {
g := &GRE{}
return decodingLayerDecoder(g, data, p)
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,267 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"reflect"
"github.com/google/gopacket"
)
const (
ICMPv4TypeEchoReply = 0
ICMPv4TypeDestinationUnreachable = 3
ICMPv4TypeSourceQuench = 4
ICMPv4TypeRedirect = 5
ICMPv4TypeEchoRequest = 8
ICMPv4TypeRouterAdvertisement = 9
ICMPv4TypeRouterSolicitation = 10
ICMPv4TypeTimeExceeded = 11
ICMPv4TypeParameterProblem = 12
ICMPv4TypeTimestampRequest = 13
ICMPv4TypeTimestampReply = 14
ICMPv4TypeInfoRequest = 15
ICMPv4TypeInfoReply = 16
ICMPv4TypeAddressMaskRequest = 17
ICMPv4TypeAddressMaskReply = 18
)
const (
// DestinationUnreachable
ICMPv4CodeNet = 0
ICMPv4CodeHost = 1
ICMPv4CodeProtocol = 2
ICMPv4CodePort = 3
ICMPv4CodeFragmentationNeeded = 4
ICMPv4CodeSourceRoutingFailed = 5
ICMPv4CodeNetUnknown = 6
ICMPv4CodeHostUnknown = 7
ICMPv4CodeSourceIsolated = 8
ICMPv4CodeNetAdminProhibited = 9
ICMPv4CodeHostAdminProhibited = 10
ICMPv4CodeNetTOS = 11
ICMPv4CodeHostTOS = 12
ICMPv4CodeCommAdminProhibited = 13
ICMPv4CodeHostPrecedence = 14
ICMPv4CodePrecedenceCutoff = 15
// TimeExceeded
ICMPv4CodeTTLExceeded = 0
ICMPv4CodeFragmentReassemblyTimeExceeded = 1
// ParameterProblem
ICMPv4CodePointerIndicatesError = 0
ICMPv4CodeMissingOption = 1
ICMPv4CodeBadLength = 2
// Redirect
// ICMPv4CodeNet = same as for DestinationUnreachable
// ICMPv4CodeHost = same as for DestinationUnreachable
ICMPv4CodeTOSNet = 2
ICMPv4CodeTOSHost = 3
)
type icmpv4TypeCodeInfoStruct struct {
typeStr string
codeStr *map[uint8]string
}
var (
icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
"DestinationUnreachable", &map[uint8]string{
ICMPv4CodeNet: "Net",
ICMPv4CodeHost: "Host",
ICMPv4CodeProtocol: "Protocol",
ICMPv4CodePort: "Port",
ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
ICMPv4CodeNetUnknown: "NetUnknown",
ICMPv4CodeHostUnknown: "HostUnknown",
ICMPv4CodeSourceIsolated: "SourceIsolated",
ICMPv4CodeNetAdminProhibited: "NetAdminProhibited",
ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
ICMPv4CodeNetTOS: "NetTOS",
ICMPv4CodeHostTOS: "HostTOS",
ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
ICMPv4CodeHostPrecedence: "HostPrecedence",
ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff",
},
},
ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
"TimeExceeded", &map[uint8]string{
ICMPv4CodeTTLExceeded: "TTLExceeded",
ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
},
},
ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
"ParameterProblem", &map[uint8]string{
ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
ICMPv4CodeMissingOption: "MissingOption",
ICMPv4CodeBadLength: "BadLength",
},
},
ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
"SourceQuench", nil,
},
ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
"Redirect", &map[uint8]string{
ICMPv4CodeNet: "Net",
ICMPv4CodeHost: "Host",
ICMPv4CodeTOSNet: "TOS+Net",
ICMPv4CodeTOSHost: "TOS+Host",
},
},
ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
"EchoRequest", nil,
},
ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
"EchoReply", nil,
},
ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
"TimestampRequest", nil,
},
ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
"TimestampReply", nil,
},
ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
"InfoRequest", nil,
},
ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
"InfoReply", nil,
},
ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
"RouterSolicitation", nil,
},
ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
"RouterAdvertisement", nil,
},
ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
"AddressMaskRequest", nil,
},
ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
"AddressMaskReply", nil,
},
}
)
type ICMPv4TypeCode uint16
// Type returns the ICMPv4 type field.
func (a ICMPv4TypeCode) Type() uint8 {
return uint8(a >> 8)
}
// Code returns the ICMPv4 code field.
func (a ICMPv4TypeCode) Code() uint8 {
return uint8(a)
}
func (a ICMPv4TypeCode) String() string {
t, c := a.Type(), a.Code()
strInfo, ok := icmpv4TypeCodeInfo[t]
if !ok {
// Unknown ICMPv4 type field
return fmt.Sprintf("%d(%d)", t, c)
}
typeStr := strInfo.typeStr
if strInfo.codeStr == nil && c == 0 {
// The ICMPv4 type does not make use of the code field
return fmt.Sprintf("%s", strInfo.typeStr)
}
if strInfo.codeStr == nil && c != 0 {
// The ICMPv4 type does not make use of the code field, but it is present anyway
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
}
codeStr, ok := (*strInfo.codeStr)[c]
if !ok {
// We don't know this ICMPv4 code; print the numerical value
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
}
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
}
func (a ICMPv4TypeCode) GoString() string {
t := reflect.TypeOf(a)
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
}
// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
binary.BigEndian.PutUint16(bytes, uint16(a))
}
// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
// gopacket type from the ICMPv4 type and code values.
func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
}
// ICMPv4 is the layer for IPv4 ICMP packet data.
type ICMPv4 struct {
BaseLayer
TypeCode ICMPv4TypeCode
Checksum uint16
Id uint16
Seq uint16
}
// LayerType returns LayerTypeICMPv4.
func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
}
i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.Id = binary.BigEndian.Uint16(data[4:6])
i.Seq = binary.BigEndian.Uint16(data[6:8])
i.BaseLayer = BaseLayer{data[:8], data[8:]}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
i.TypeCode.SerializeTo(bytes)
binary.BigEndian.PutUint16(bytes[4:], i.Id)
binary.BigEndian.PutUint16(bytes[6:], i.Seq)
if opts.ComputeChecksums {
bytes[2] = 0
bytes[3] = 0
i.Checksum = tcpipChecksum(b.Bytes(), 0)
}
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv4) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv4
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv4) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv4{}
return decodingLayerDecoder(i, data, p)
}

@ -0,0 +1,231 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"reflect"
"github.com/google/gopacket"
)
const (
// The following are from RFC 4443
ICMPv6TypeDestinationUnreachable = 1
ICMPv6TypePacketTooBig = 2
ICMPv6TypeTimeExceeded = 3
ICMPv6TypeParameterProblem = 4
ICMPv6TypeEchoRequest = 128
ICMPv6TypeEchoReply = 129
// The following are from RFC 4861
ICMPv6TypeRouterSolicitation = 133
ICMPv6TypeRouterAdvertisement = 134
ICMPv6TypeNeighborSolicitation = 135
ICMPv6TypeNeighborAdvertisement = 136
ICMPv6TypeRedirect = 137
)
const (
// DestinationUnreachable
ICMPv6CodeNoRouteToDst = 0
ICMPv6CodeAdminProhibited = 1
ICMPv6CodeBeyondScopeOfSrc = 2
ICMPv6CodeAddressUnreachable = 3
ICMPv6CodePortUnreachable = 4
ICMPv6CodeSrcAddressFailedPolicy = 5
ICMPv6CodeRejectRouteToDst = 6
// TimeExceeded
ICMPv6CodeHopLimitExceeded = 0
ICMPv6CodeFragmentReassemblyTimeExceeded = 1
// ParameterProblem
ICMPv6CodeErroneousHeaderField = 0
ICMPv6CodeUnrecognizedNextHeader = 1
ICMPv6CodeUnrecognizedIPv6Option = 2
)
type icmpv6TypeCodeInfoStruct struct {
typeStr string
codeStr *map[uint8]string
}
var (
icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{
ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{
"DestinationUnreachable", &map[uint8]string{
ICMPv6CodeNoRouteToDst: "NoRouteToDst",
ICMPv6CodeAdminProhibited: "AdminProhibited",
ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc",
ICMPv6CodeAddressUnreachable: "AddressUnreachable",
ICMPv6CodePortUnreachable: "PortUnreachable",
ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy",
ICMPv6CodeRejectRouteToDst: "RejectRouteToDst",
},
},
ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{
"PacketTooBig", nil,
},
ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{
"TimeExceeded", &map[uint8]string{
ICMPv6CodeHopLimitExceeded: "HopLimitExceeded",
ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
},
},
ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{
"ParameterProblem", &map[uint8]string{
ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField",
ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader",
ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option",
},
},
ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{
"EchoRequest", nil,
},
ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{
"EchoReply", nil,
},
ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{
"RouterSolicitation", nil,
},
ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{
"RouterAdvertisement", nil,
},
ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{
"NeighborSolicitation", nil,
},
ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{
"NeighborAdvertisement", nil,
},
ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{
"Redirect", nil,
},
}
)
type ICMPv6TypeCode uint16
// Type returns the ICMPv6 type field.
func (a ICMPv6TypeCode) Type() uint8 {
return uint8(a >> 8)
}
// Code returns the ICMPv6 code field.
func (a ICMPv6TypeCode) Code() uint8 {
return uint8(a)
}
func (a ICMPv6TypeCode) String() string {
t, c := a.Type(), a.Code()
strInfo, ok := icmpv6TypeCodeInfo[t]
if !ok {
// Unknown ICMPv6 type field
return fmt.Sprintf("%d(%d)", t, c)
}
typeStr := strInfo.typeStr
if strInfo.codeStr == nil && c == 0 {
// The ICMPv6 type does not make use of the code field
return fmt.Sprintf("%s", strInfo.typeStr)
}
if strInfo.codeStr == nil && c != 0 {
// The ICMPv6 type does not make use of the code field, but it is present anyway
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
}
codeStr, ok := (*strInfo.codeStr)[c]
if !ok {
// We don't know this ICMPv6 code; print the numerical value
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
}
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
}
func (a ICMPv6TypeCode) GoString() string {
t := reflect.TypeOf(a)
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
}
// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer.
func (a ICMPv6TypeCode) SerializeTo(bytes []byte) {
binary.BigEndian.PutUint16(bytes, uint16(a))
}
// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode
// gopacket type from the ICMPv6 type and code values.
func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode {
return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
}
// ICMPv6 is the layer for IPv6 ICMP packet data
type ICMPv6 struct {
BaseLayer
TypeCode ICMPv6TypeCode
Checksum uint16
TypeBytes []byte
tcpipchecksum
}
// LayerType returns LayerTypeICMPv6.
func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 }
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return errors.New("ICMP layer less then 8 bytes for ICMPv6 packet")
}
i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.TypeBytes = data[4:8]
i.BaseLayer = BaseLayer{data[:8], data[8:]}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if i.TypeBytes == nil {
i.TypeBytes = lotsOfZeros[:4]
} else if len(i.TypeBytes) != 4 {
return fmt.Errorf("invalid type bytes for ICMPv6 packet: %v", i.TypeBytes)
}
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
i.TypeCode.SerializeTo(bytes)
copy(bytes[4:8], i.TypeBytes)
if opts.ComputeChecksums {
bytes[2] = 0
bytes[3] = 0
csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6)
if err != nil {
return err
}
i.Checksum = csum
}
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6{}
return decodingLayerDecoder(i, data, p)
}

@ -0,0 +1,355 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"net"
"time"
"github.com/google/gopacket"
)
type IGMPType uint8
const (
IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
IGMPLeaveGroup IGMPType = 0x17 // Leave Group
IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
)
// String conversions for IGMP message types
func (i IGMPType) String() string {
switch i {
case IGMPMembershipQuery:
return "IGMP Membership Query"
case IGMPMembershipReportV1:
return "IGMPv1 Membership Report"
case IGMPMembershipReportV2:
return "IGMPv2 Membership Report"
case IGMPMembershipReportV3:
return "IGMPv3 Membership Report"
case IGMPLeaveGroup:
return "Leave Group"
default:
return ""
}
}
type IGMPv3GroupRecordType uint8
const (
IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
)
func (i IGMPv3GroupRecordType) String() string {
switch i {
case IGMPIsIn:
return "MODE_IS_INCLUDE"
case IGMPIsEx:
return "MODE_IS_EXCLUDE"
case IGMPToIn:
return "CHANGE_TO_INCLUDE_MODE"
case IGMPToEx:
return "CHANGE_TO_EXCLUDE_MODE"
case IGMPAllow:
return "ALLOW_NEW_SOURCES"
case IGMPBlock:
return "BLOCK_OLD_SOURCES"
default:
return ""
}
}
// IGMP represents an IGMPv3 message.
type IGMP struct {
BaseLayer
Type IGMPType
MaxResponseTime time.Duration
Checksum uint16
GroupAddress net.IP
SupressRouterProcessing bool
RobustnessValue uint8
IntervalTime time.Duration
SourceAddresses []net.IP
NumberOfGroupRecords uint16
NumberOfSources uint16
GroupRecords []IGMPv3GroupRecord
Version uint8 // IGMP protocol version
}
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Max Resp Time | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type IGMPv1or2 struct {
BaseLayer
Type IGMPType // IGMP message type
MaxResponseTime time.Duration // meaningful only in Membership Query messages
Checksum uint16 // 16-bit checksum of entire ip payload
GroupAddress net.IP // either 0 or an IP multicast address
Version uint8
}
// decodeResponse dissects IGMPv1 or IGMPv2 packet.
func (i *IGMPv1or2) decodeResponse(data []byte) error {
if len(data) < 8 {
return errors.New("IGMP packet too small")
}
i.MaxResponseTime = igmpTimeDecode(data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.GroupAddress = net.IP(data[4:8])
return nil
}
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type = 0x22 | Reserved | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Reserved | Number of Group Records (M) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . Group Record [1] .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . Group Record [2] .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . Group Record [M] .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Record Type | Aux Data Len | Number of Sources (N) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Multicast Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Source Address [1] |
// +- -+
// | Source Address [2] |
// +- -+
// | Source Address [N] |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . Auxiliary Data .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
type IGMPv3GroupRecord struct {
Type IGMPv3GroupRecordType
AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
NumberOfSources uint16
MulticastAddress net.IP
SourceAddresses []net.IP
AuxData uint32 // NOT USED
}
func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
if len(data) < 8 {
return errors.New("IGMPv3 Membership Report too small #1")
}
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
recordOffset := 8
for j := 0; j < int(i.NumberOfGroupRecords); j++ {
if len(data) < recordOffset+8 {
return errors.New("IGMPv3 Membership Report too small #2")
}
var gr IGMPv3GroupRecord
gr.Type = IGMPv3GroupRecordType(data[recordOffset])
gr.AuxDataLen = data[recordOffset+1]
gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
return errors.New("IGMPv3 Membership Report too small #3")
}
// append source address records.
for i := 0; i < int(gr.NumberOfSources); i++ {
sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
}
i.GroupRecords = append(i.GroupRecords, gr)
recordOffset += 8 + 4*int(gr.NumberOfSources)
}
return nil
}
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type = 0x11 | Max Resp Code | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Group Address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Resv |S| QRV | QQIC | Number of Sources (N) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Source Address [1] |
// +- -+
// | Source Address [2] |
// +- . -+
// | Source Address [N] |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
if len(data) < 12 {
return errors.New("IGMPv3 Membership Query too small #1")
}
i.MaxResponseTime = igmpTimeDecode(data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.SupressRouterProcessing = data[8]&0x8 != 0
i.GroupAddress = net.IP(data[4:8])
i.RobustnessValue = data[8] & 0x7
i.IntervalTime = igmpTimeDecode(data[9])
i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
if len(data) < 12+int(i.NumberOfSources)*4 {
return errors.New("IGMPv3 Membership Query too small #2")
}
for j := 0; j < int(i.NumberOfSources); j++ {
i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
}
return nil
}
// igmpTimeDecode decodes the duration created by the given byte, using the
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
func igmpTimeDecode(t uint8) time.Duration {
if t&0x80 == 0 {
return time.Millisecond * 100 * time.Duration(t)
}
mant := (t & 0x70) >> 4
exp := t & 0x0F
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
}
// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
return errors.New("IGMP Packet too small")
}
i.Type = IGMPType(data[0])
i.MaxResponseTime = igmpTimeDecode(data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.GroupAddress = net.IP(data[4:8])
return nil
}
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
return LayerTypeIGMP
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 1 {
return errors.New("IGMP packet is too small")
}
// common IGMP header values between versions 1..3 of IGMP specification..
i.Type = IGMPType(data[0])
switch i.Type {
case IGMPMembershipQuery:
i.decodeIGMPv3MembershipQuery(data)
case IGMPMembershipReportV3:
i.decodeIGMPv3MembershipReport(data)
default:
return errors.New("unsupported IGMP type")
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *IGMP) CanDecode() gopacket.LayerClass {
return LayerTypeIGMP
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *IGMP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
// IGMP type are performed against byte[0], logic then iniitalizes and
// passes the appropriate struct (IGMP or IGMPv1or2) to
// decodingLayerDecoder.
func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
if len(data) < 1 {
return errors.New("IGMP packet is too small")
}
// byte 0 contains IGMP message type.
switch IGMPType(data[0]) {
case IGMPMembershipQuery:
// IGMPv3 Membership Query payload is >= 12
if len(data) >= 12 {
i := &IGMP{Version: 3}
return decodingLayerDecoder(i, data, p)
} else if len(data) == 8 {
i := &IGMPv1or2{}
if data[1] == 0x00 {
i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
} else {
i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
}
return decodingLayerDecoder(i, data, p)
}
case IGMPMembershipReportV3:
i := &IGMP{Version: 3}
return decodingLayerDecoder(i, data, p)
case IGMPMembershipReportV1:
i := &IGMPv1or2{Version: 1}
return decodingLayerDecoder(i, data, p)
case IGMPLeaveGroup, IGMPMembershipReportV2:
// leave group and Query Report v2 used in IGMPv2 only.
i := &IGMPv1or2{Version: 2}
return decodingLayerDecoder(i, data, p)
default:
}
return errors.New("Unable to determine IGMP type.")
}

@ -0,0 +1,311 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"strings"
"github.com/google/gopacket"
)
type IPv4Flag uint8
const (
IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;)
IPv4DontFragment IPv4Flag = 1 << 1
IPv4MoreFragments IPv4Flag = 1 << 0
)
func (f IPv4Flag) String() string {
var s []string
if f&IPv4EvilBit != 0 {
s = append(s, "Evil")
}
if f&IPv4DontFragment != 0 {
s = append(s, "DF")
}
if f&IPv4MoreFragments != 0 {
s = append(s, "MF")
}
return strings.Join(s, "|")
}
// IPv4 is the header of an IP packet.
type IPv4 struct {
BaseLayer
Version uint8
IHL uint8
TOS uint8
Length uint16
Id uint16
Flags IPv4Flag
FragOffset uint16
TTL uint8
Protocol IPProtocol
Checksum uint16
SrcIP net.IP
DstIP net.IP
Options []IPv4Option
Padding []byte
}
// LayerType returns LayerTypeIPv4
func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 }
func (i *IPv4) NetworkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP)
}
type IPv4Option struct {
OptionType uint8
OptionLength uint8
OptionData []byte
}
func (i IPv4Option) String() string {
return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData)
}
// for the current ipv4 options, return the number of bytes (including
// padding that the options used)
func (ip *IPv4) getIPv4OptionSize() uint8 {
optionSize := uint8(0)
for _, opt := range ip.Options {
switch opt.OptionType {
case 0:
// this is the end of option lists
optionSize++
case 1:
// this is the padding
optionSize++
default:
optionSize += opt.OptionLength
}
}
// make sure the options are aligned to 32 bit boundary
if (optionSize % 4) != 0 {
optionSize += 4 - (optionSize % 4)
}
return optionSize
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
optionLength := ip.getIPv4OptionSize()
bytes, err := b.PrependBytes(20 + int(optionLength))
if err != nil {
return err
}
if opts.FixLengths {
ip.IHL = 5 + (optionLength / 4)
ip.Length = uint16(len(b.Bytes()))
}
bytes[0] = (ip.Version << 4) | ip.IHL
bytes[1] = ip.TOS
binary.BigEndian.PutUint16(bytes[2:], ip.Length)
binary.BigEndian.PutUint16(bytes[4:], ip.Id)
binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags())
bytes[8] = ip.TTL
bytes[9] = byte(ip.Protocol)
if err := ip.AddressTo4(); err != nil {
return err
}
copy(bytes[12:16], ip.SrcIP)
copy(bytes[16:20], ip.DstIP)
curLocation := 20
// Now, we will encode the options
for _, opt := range ip.Options {
switch opt.OptionType {
case 0:
// this is the end of option lists
bytes[curLocation] = 0
curLocation++
case 1:
// this is the padding
bytes[curLocation] = 1
curLocation++
default:
bytes[curLocation] = opt.OptionType
bytes[curLocation+1] = opt.OptionLength
// sanity checking to protect us from buffer overrun
if len(opt.OptionData) > int(opt.OptionLength-2) {
return errors.New("option length is smaller than length of option data")
}
copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData)
curLocation += int(opt.OptionLength)
}
}
if opts.ComputeChecksums {
ip.Checksum = checksum(bytes)
}
binary.BigEndian.PutUint16(bytes[10:], ip.Checksum)
return nil
}
func checksum(bytes []byte) uint16 {
// Clear checksum bytes
bytes[10] = 0
bytes[11] = 0
// Compute checksum
var csum uint32
for i := 0; i < len(bytes); i += 2 {
csum += uint32(bytes[i]) << 8
csum += uint32(bytes[i+1])
}
for {
// Break when sum is less or equals to 0xFFFF
if csum <= 65535 {
break
}
// Add carry to the sum
csum = (csum >> 16) + uint32(uint16(csum))
}
// Flip all the bits
return ^uint16(csum)
}
func (ip *IPv4) flagsfrags() (ff uint16) {
ff |= uint16(ip.Flags) << 13
ff |= ip.FragOffset
return
}
// DecodeFromBytes decodes the given bytes into this layer.
func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
flagsfrags := binary.BigEndian.Uint16(data[6:8])
ip.Version = uint8(data[0]) >> 4
ip.IHL = uint8(data[0]) & 0x0F
ip.TOS = data[1]
ip.Length = binary.BigEndian.Uint16(data[2:4])
ip.Id = binary.BigEndian.Uint16(data[4:6])
ip.Flags = IPv4Flag(flagsfrags >> 13)
ip.FragOffset = flagsfrags & 0x1FFF
ip.TTL = data[8]
ip.Protocol = IPProtocol(data[9])
ip.Checksum = binary.BigEndian.Uint16(data[10:12])
ip.SrcIP = data[12:16]
ip.DstIP = data[16:20]
ip.Options = ip.Options[:0]
// Set up an initial guess for contents/payload... we'll reset these soon.
ip.BaseLayer = BaseLayer{Contents: data}
// This code is added for the following enviroment:
// * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver )
if ip.Length == 0 {
// If using TSO(TCP Segmentation Offload), length is zero.
// The actual packet length is the length of data.
ip.Length = uint16(len(data))
}
if ip.Length < 20 {
return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length)
} else if ip.IHL < 5 {
return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL)
} else if int(ip.IHL*4) > int(ip.Length) {
return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length)
}
if cmp := len(data) - int(ip.Length); cmp > 0 {
data = data[:ip.Length]
} else if cmp < 0 {
df.SetTruncated()
if int(ip.IHL)*4 > len(data) {
return errors.New("Not all IP header bytes available")
}
}
ip.Contents = data[:ip.IHL*4]
ip.Payload = data[ip.IHL*4:]
// From here on, data contains the header options.
data = data[20 : ip.IHL*4]
// Pull out IP options
for len(data) > 0 {
if ip.Options == nil {
// Pre-allocate to avoid growing the slice too much.
ip.Options = make([]IPv4Option, 0, 4)
}
opt := IPv4Option{OptionType: data[0]}
switch opt.OptionType {
case 0: // End of options
opt.OptionLength = 1
ip.Options = append(ip.Options, opt)
ip.Padding = data[1:]
break
case 1: // 1 byte padding
opt.OptionLength = 1
default:
opt.OptionLength = data[1]
opt.OptionData = data[2:opt.OptionLength]
}
if len(data) >= int(opt.OptionLength) {
data = data[opt.OptionLength:]
} else {
return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
}
ip.Options = append(ip.Options, opt)
}
return nil
}
func (i *IPv4) CanDecode() gopacket.LayerClass {
return LayerTypeIPv4
}
func (i *IPv4) NextLayerType() gopacket.LayerType {
if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 {
return gopacket.LayerTypeFragment
}
return i.Protocol.LayerType()
}
func decodeIPv4(data []byte, p gopacket.PacketBuilder) error {
ip := &IPv4{}
err := ip.DecodeFromBytes(data, p)
p.AddLayer(ip)
p.SetNetworkLayer(ip)
if err != nil {
return err
}
return p.NextDecoder(ip.NextLayerType())
}
func checkIPv4Address(addr net.IP) (net.IP, error) {
if c := addr.To4(); c != nil {
return c, nil
}
if len(addr) == net.IPv6len {
return nil, errors.New("address is IPv6")
}
return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len)
}
func (ip *IPv4) AddressTo4() error {
var src, dst net.IP
if addr, err := checkIPv4Address(ip.SrcIP); err != nil {
return fmt.Errorf("Invalid source IPv4 address (%s)", err)
} else {
src = addr
}
if addr, err := checkIPv4Address(ip.DstIP); err != nil {
return fmt.Errorf("Invalid destination IPv4 address (%s)", err)
} else {
dst = addr
}
ip.SrcIP = src
ip.DstIP = dst
return nil
}

@ -0,0 +1,650 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
const (
IPv6HopByHopOptionJumbogram = 0xC2 // RFC 2675
)
const (
ipv6MaxPayloadLength = 65535
)
// IPv6 is the layer for the IPv6 header.
type IPv6 struct {
// http://www.networksorcery.com/enp/protocol/ipv6.htm
BaseLayer
Version uint8
TrafficClass uint8
FlowLabel uint32
Length uint16
NextHeader IPProtocol
HopLimit uint8
SrcIP net.IP
DstIP net.IP
HopByHop *IPv6HopByHop
// hbh will be pointed to by HopByHop if that layer exists.
hbh IPv6HopByHop
}
// LayerType returns LayerTypeIPv6
func (i *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 }
func (i *IPv6) NetworkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointIPv6, i.SrcIP, i.DstIP)
}
// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found
func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) {
var tlv *IPv6HopByHopOption
for _, t := range hopopts.Options {
if t.OptionType == IPv6HopByHopOptionJumbogram {
tlv = t
break
}
}
if tlv == nil {
// Not found
return 0, false, nil
}
if len(tlv.OptionData) != 4 {
return 0, false, errors.New("Jumbo length TLV data must have length 4")
}
l := binary.BigEndian.Uint32(tlv.OptionData)
if l <= ipv6MaxPayloadLength {
return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1)
}
// Found
return l, true, nil
}
// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist
// (if necessary add hop-by-hop header)
func addIPv6JumboOption(ip6 *IPv6) {
var tlv *IPv6HopByHopOption
if ip6.HopByHop == nil {
// Add IPv6 HopByHop
ip6.HopByHop = &IPv6HopByHop{}
ip6.HopByHop.NextHeader = ip6.NextHeader
ip6.HopByHop.HeaderLength = 0
ip6.NextHeader = IPProtocolIPv6HopByHop
}
for _, t := range ip6.HopByHop.Options {
if t.OptionType == IPv6HopByHopOptionJumbogram {
tlv = t
break
}
}
if tlv == nil {
// Add Jumbo TLV
tlv = &IPv6HopByHopOption{}
ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv)
}
tlv.SetJumboLength(0)
}
// Set jumbo length in serialized IPv6 payload (starting with HopByHop header)
func setIPv6PayloadJumboLength(hbh []byte) error {
pLen := len(hbh)
if pLen < 8 {
//HopByHop is minimum 8 bytes
return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen)
}
hbhLen := int((hbh[1] + 1) * 8)
if hbhLen > pLen {
return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen)
}
offset := 2 //start with options
for offset < hbhLen {
opt := hbh[offset]
if opt == 0 {
//Pad1
offset += 1
continue
}
optLen := int(hbh[offset+1])
if opt == IPv6HopByHopOptionJumbogram {
if optLen == 4 {
binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen))
return nil
}
return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen)
}
offset += 2 + optLen
}
return errors.New("Jumbo TLV not found")
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (ip6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var jumbo bool
var err error
payload := b.Bytes()
pLen := len(payload)
if pLen > ipv6MaxPayloadLength {
jumbo = true
if opts.FixLengths {
// We need to set the length later because the hop-by-hop header may
// not exist or else need padding, so pLen may yet change
addIPv6JumboOption(ip6)
} else if ip6.HopByHop == nil {
return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen)
} else {
_, ok, err := getIPv6HopByHopJumboLength(ip6.HopByHop)
if err != nil {
return err
}
if !ok {
return errors.New("Missing jumbo length hop-by-hop option")
}
}
}
if ip6.HopByHop != nil {
if ip6.NextHeader != IPProtocolIPv6HopByHop {
// Just fix it instead of throwing an error
ip6.NextHeader = IPProtocolIPv6HopByHop
}
err = ip6.HopByHop.SerializeTo(b, opts)
if err != nil {
return err
}
payload = b.Bytes()
pLen = len(payload)
if opts.FixLengths && jumbo {
err := setIPv6PayloadJumboLength(payload)
if err != nil {
return err
}
}
}
if !jumbo && pLen > ipv6MaxPayloadLength {
return errors.New("Cannot fit payload into IPv6 header")
}
bytes, err := b.PrependBytes(40)
if err != nil {
return err
}
bytes[0] = (ip6.Version << 4) | (ip6.TrafficClass >> 4)
bytes[1] = (ip6.TrafficClass << 4) | uint8(ip6.FlowLabel>>16)
binary.BigEndian.PutUint16(bytes[2:], uint16(ip6.FlowLabel))
if opts.FixLengths {
if jumbo {
ip6.Length = 0
} else {
ip6.Length = uint16(pLen)
}
}
binary.BigEndian.PutUint16(bytes[4:], ip6.Length)
bytes[6] = byte(ip6.NextHeader)
bytes[7] = byte(ip6.HopLimit)
if err := ip6.AddressTo16(); err != nil {
return err
}
copy(bytes[8:], ip6.SrcIP)
copy(bytes[24:], ip6.DstIP)
return nil
}
func (ip6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
ip6.Version = uint8(data[0]) >> 4
ip6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
ip6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
ip6.Length = binary.BigEndian.Uint16(data[4:6])
ip6.NextHeader = IPProtocol(data[6])
ip6.HopLimit = data[7]
ip6.SrcIP = data[8:24]
ip6.DstIP = data[24:40]
ip6.HopByHop = nil
ip6.BaseLayer = BaseLayer{data[:40], data[40:]}
// We treat a HopByHop IPv6 option as part of the IPv6 packet, since its
// options are crucial for understanding what's actually happening per packet.
if ip6.NextHeader == IPProtocolIPv6HopByHop {
err := ip6.hbh.DecodeFromBytes(ip6.Payload, df)
if err != nil {
return err
}
ip6.HopByHop = &ip6.hbh
pEnd, jumbo, err := getIPv6HopByHopJumboLength(ip6.HopByHop)
if err != nil {
return err
}
if jumbo && ip6.Length == 0 {
pEnd := int(pEnd)
if pEnd > len(ip6.Payload) {
df.SetTruncated()
pEnd = len(ip6.Payload)
}
ip6.Payload = ip6.Payload[:pEnd]
return nil
} else if jumbo && ip6.Length != 0 {
return errors.New("IPv6 has jumbo length and IPv6 length is not 0")
} else if !jumbo && ip6.Length == 0 {
return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option")
}
}
if ip6.Length == 0 {
return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ip6.NextHeader)
} else {
pEnd := int(ip6.Length)
if pEnd > len(ip6.Payload) {
df.SetTruncated()
pEnd = len(ip6.Payload)
}
ip6.Payload = ip6.Payload[:pEnd]
}
return nil
}
func (i *IPv6) CanDecode() gopacket.LayerClass {
return LayerTypeIPv6
}
func (i *IPv6) NextLayerType() gopacket.LayerType {
if i.HopByHop != nil {
return i.HopByHop.NextHeader.LayerType()
}
return i.NextHeader.LayerType()
}
func decodeIPv6(data []byte, p gopacket.PacketBuilder) error {
ip6 := &IPv6{}
err := ip6.DecodeFromBytes(data, p)
p.AddLayer(ip6)
p.SetNetworkLayer(ip6)
if ip6.HopByHop != nil {
p.AddLayer(ip6.HopByHop)
}
if err != nil {
return err
}
return p.NextDecoder(ip6.NextLayerType())
}
type ipv6HeaderTLVOption struct {
OptionType, OptionLength uint8
ActualLength int
OptionData []byte
OptionAlignment [2]uint8 // Xn+Y = [2]uint8{X, Y}
}
func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int {
if fixLengths {
h.OptionLength = uint8(len(h.OptionData))
}
length := int(h.OptionLength) + 2
if !dryrun {
data[0] = h.OptionType
data[1] = h.OptionLength
copy(data[2:], h.OptionData)
}
return length
}
func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
h = &ipv6HeaderTLVOption{}
if data[0] == 0 {
h.ActualLength = 1
return
}
h.OptionType = data[0]
h.OptionLength = data[1]
h.ActualLength = int(h.OptionLength) + 2
h.OptionData = data[2:h.ActualLength]
return
}
func serializeTLVOptionPadding(data []byte, padLength int) {
if padLength <= 0 {
return
}
if padLength == 1 {
data[0] = 0x0
return
}
tlvLength := uint8(padLength) - 2
data[0] = 0x1
data[1] = tlvLength
if tlvLength != 0 {
for k := range data[2:] {
data[k+2] = 0x0
}
}
return
}
// If buf is 'nil' do a serialize dry run
func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int {
var l int
dryrun := buf == nil
length := 2
for _, opt := range options {
if fixLengths {
x := int(opt.OptionAlignment[0])
y := int(opt.OptionAlignment[1])
if x != 0 {
n := length / x
offset := x*n + y
if offset < length {
offset += x
}
if length != offset {
pad := offset - length
if !dryrun {
serializeTLVOptionPadding(buf[length-2:], pad)
}
length += pad
}
}
}
if dryrun {
l = opt.serializeTo(nil, fixLengths, true)
} else {
l = opt.serializeTo(buf[length-2:], fixLengths, false)
}
length += l
}
if fixLengths {
pad := length % 8
if pad != 0 {
if !dryrun {
serializeTLVOptionPadding(buf[length-2:], pad)
}
length += pad
}
}
return length - 2
}
type ipv6ExtensionBase struct {
BaseLayer
NextHeader IPProtocol
HeaderLength uint8
ActualLength int
}
func decodeIPv6ExtensionBase(data []byte) (i ipv6ExtensionBase) {
i.NextHeader = IPProtocol(data[0])
i.HeaderLength = data[1]
i.ActualLength = int(i.HeaderLength)*8 + 8
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
return
}
// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6
// extensions. You can use it with a DecodingLayerParser to handle IPv6 stacks
// which may or may not have extensions.
type IPv6ExtensionSkipper struct {
NextHeader IPProtocol
BaseLayer
}
func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
extension := decodeIPv6ExtensionBase(data)
i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
i.NextHeader = extension.NextHeader
return nil
}
func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass {
return LayerClassIPv6Extension
}
func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType {
return i.NextHeader.LayerType()
}
// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension.
type IPv6HopByHopOption ipv6HeaderTLVOption
// IPv6HopByHop is the IPv6 hop-by-hop extension.
type IPv6HopByHop struct {
ipv6ExtensionBase
Options []*IPv6HopByHopOption
}
// LayerType returns LayerTypeIPv6HopByHop.
func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop }
func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var bytes []byte
var err error
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
for _, v := range i.Options {
o = append(o, (*ipv6HeaderTLVOption)(v))
}
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
bytes, err = b.PrependBytes(l)
if err != nil {
return err
}
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
length := len(bytes) + 2
if length%8 != 0 {
return errors.New("IPv6HopByHop actual length must be multiple of 8")
}
bytes, err = b.PrependBytes(2)
if err != nil {
return err
}
bytes[0] = uint8(i.NextHeader)
if opts.FixLengths {
i.HeaderLength = uint8((length / 8) - 1)
}
bytes[1] = uint8(i.HeaderLength)
return nil
}
func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
offset += opt.ActualLength
}
return nil
}
func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6HopByHop{}
err := i.DecodeFromBytes(data, p)
p.AddLayer(i)
if err != nil {
return err
}
return p.NextDecoder(i.NextHeader)
}
func (o *IPv6HopByHopOption) SetJumboLength(len uint32) {
o.OptionType = IPv6HopByHopOptionJumbogram
o.OptionLength = 4
o.ActualLength = 6
if o.OptionData == nil {
o.OptionData = make([]byte, 4)
}
binary.BigEndian.PutUint32(o.OptionData, len)
o.OptionAlignment = [2]uint8{4, 2}
}
// IPv6Routing is the IPv6 routing extension.
type IPv6Routing struct {
ipv6ExtensionBase
RoutingType uint8
SegmentsLeft uint8
// This segment is supposed to be zero according to RFC2460, the second set of
// 4 bytes in the extension.
Reserved []byte
// SourceRoutingIPs is the set of IPv6 addresses requested for source routing,
// set only if RoutingType == 0.
SourceRoutingIPs []net.IP
}
// LayerType returns LayerTypeIPv6Routing.
func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }
func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6Routing{
ipv6ExtensionBase: decodeIPv6ExtensionBase(data),
RoutingType: data[2],
SegmentsLeft: data[3],
Reserved: data[4:8],
}
switch i.RoutingType {
case 0: // Source routing
if (i.ActualLength-8)%16 != 0 {
return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)
}
for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
}
default:
return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType)
}
p.AddLayer(i)
return p.NextDecoder(i.NextHeader)
}
// IPv6Fragment is the IPv6 fragment header, used for packet
// fragmentation/defragmentation.
type IPv6Fragment struct {
BaseLayer
NextHeader IPProtocol
// Reserved1 is bits [8-16), from least to most significant, 0-indexed
Reserved1 uint8
FragmentOffset uint16
// Reserved2 is bits [29-31), from least to most significant, 0-indexed
Reserved2 uint8
MoreFragments bool
Identification uint32
}
// LayerType returns LayerTypeIPv6Fragment.
func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment }
func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6Fragment{
BaseLayer: BaseLayer{data[:8], data[8:]},
NextHeader: IPProtocol(data[0]),
Reserved1: data[1],
FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3,
Reserved2: data[3] & 0x6 >> 1,
MoreFragments: data[3]&0x1 != 0,
Identification: binary.BigEndian.Uint32(data[4:8]),
}
p.AddLayer(i)
return p.NextDecoder(gopacket.DecodeFragment)
}
// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension.
type IPv6DestinationOption ipv6HeaderTLVOption
// IPv6Destination is the IPv6 destination options header.
type IPv6Destination struct {
ipv6ExtensionBase
Options []*IPv6DestinationOption
}
// LayerType returns LayerTypeIPv6Destination.
func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination }
func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
offset += opt.ActualLength
}
return nil
}
func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6Destination{}
err := i.DecodeFromBytes(data, p)
p.AddLayer(i)
if err != nil {
return err
}
return p.NextDecoder(i.NextHeader)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var bytes []byte
var err error
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
for _, v := range i.Options {
o = append(o, (*ipv6HeaderTLVOption)(v))
}
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
bytes, err = b.PrependBytes(l)
if err != nil {
return err
}
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
length := len(bytes) + 2
if length%8 != 0 {
return errors.New("IPv6Destination actual length must be multiple of 8")
}
bytes, err = b.PrependBytes(2)
if err != nil {
return err
}
bytes[0] = uint8(i.NextHeader)
if opts.FixLengths {
i.HeaderLength = uint8((length / 8) - 1)
}
bytes[1] = uint8(i.HeaderLength)
return nil
}
func checkIPv6Address(addr net.IP) error {
if len(addr) == net.IPv6len {
return nil
}
if len(addr) == net.IPv4len {
return errors.New("address is IPv4")
}
return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len)
}
func (ip *IPv6) AddressTo16() error {
if err := checkIPv6Address(ip.SrcIP); err != nil {
return fmt.Errorf("Invalid source IPv6 address (%s)", err)
}
if err := checkIPv6Address(ip.DstIP); err != nil {
return fmt.Errorf("Invalid destination IPv6 address (%s)", err)
}
return nil
}

@ -0,0 +1,68 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// IPSecAH is the authentication header for IPv4/6 defined in
// http://tools.ietf.org/html/rfc2402
type IPSecAH struct {
// While the auth header can be used for both IPv4 and v6, its format is that of
// an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase
// to build it.
ipv6ExtensionBase
Reserved uint16
SPI, Seq uint32
AuthenticationData []byte
}
// LayerType returns LayerTypeIPSecAH.
func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
i := &IPSecAH{
ipv6ExtensionBase: ipv6ExtensionBase{
NextHeader: IPProtocol(data[0]),
HeaderLength: data[1],
},
Reserved: binary.BigEndian.Uint16(data[2:4]),
SPI: binary.BigEndian.Uint32(data[4:8]),
Seq: binary.BigEndian.Uint32(data[8:12]),
}
i.ActualLength = (int(i.HeaderLength) + 2) * 4
i.AuthenticationData = data[12:i.ActualLength]
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
p.AddLayer(i)
return p.NextDecoder(i.NextHeader)
}
// IPSecESP is the encapsulating security payload defined in
// http://tools.ietf.org/html/rfc2406
type IPSecESP struct {
BaseLayer
SPI, Seq uint32
// Encrypted contains the encrypted set of bytes sent in an ESP
Encrypted []byte
}
// LayerType returns LayerTypeIPSecESP.
func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP }
func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error {
i := &IPSecESP{
BaseLayer: BaseLayer{data, nil},
SPI: binary.BigEndian.Uint32(data[:4]),
Seq: binary.BigEndian.Uint32(data[4:8]),
Encrypted: data[8:],
}
p.AddLayer(i)
return nil
}

@ -0,0 +1,174 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"github.com/google/gopacket"
)
var (
LayerTypeARP = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)})
LayerTypeCiscoDiscovery = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)})
LayerTypeEthernetCTP = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)})
LayerTypeEthernetCTPForwardData = gopacket.RegisterLayerType(13, gopacket.LayerTypeMetadata{Name: "EthernetCTPForwardData", Decoder: nil})
LayerTypeEthernetCTPReply = gopacket.RegisterLayerType(14, gopacket.LayerTypeMetadata{Name: "EthernetCTPReply", Decoder: nil})
LayerTypeDot1Q = gopacket.RegisterLayerType(15, gopacket.LayerTypeMetadata{Name: "Dot1Q", Decoder: gopacket.DecodeFunc(decodeDot1Q)})
LayerTypeEtherIP = gopacket.RegisterLayerType(16, gopacket.LayerTypeMetadata{Name: "EtherIP", Decoder: gopacket.DecodeFunc(decodeEtherIP)})
LayerTypeEthernet = gopacket.RegisterLayerType(17, gopacket.LayerTypeMetadata{Name: "Ethernet", Decoder: gopacket.DecodeFunc(decodeEthernet)})
LayerTypeGRE = gopacket.RegisterLayerType(18, gopacket.LayerTypeMetadata{Name: "GRE", Decoder: gopacket.DecodeFunc(decodeGRE)})
LayerTypeICMPv4 = gopacket.RegisterLayerType(19, gopacket.LayerTypeMetadata{Name: "ICMPv4", Decoder: gopacket.DecodeFunc(decodeICMPv4)})
LayerTypeIPv4 = gopacket.RegisterLayerType(20, gopacket.LayerTypeMetadata{Name: "IPv4", Decoder: gopacket.DecodeFunc(decodeIPv4)})
LayerTypeIPv6 = gopacket.RegisterLayerType(21, gopacket.LayerTypeMetadata{Name: "IPv6", Decoder: gopacket.DecodeFunc(decodeIPv6)})
LayerTypeLLC = gopacket.RegisterLayerType(22, gopacket.LayerTypeMetadata{Name: "LLC", Decoder: gopacket.DecodeFunc(decodeLLC)})
LayerTypeSNAP = gopacket.RegisterLayerType(23, gopacket.LayerTypeMetadata{Name: "SNAP", Decoder: gopacket.DecodeFunc(decodeSNAP)})
LayerTypeMPLS = gopacket.RegisterLayerType(24, gopacket.LayerTypeMetadata{Name: "MPLS", Decoder: gopacket.DecodeFunc(decodeMPLS)})
LayerTypePPP = gopacket.RegisterLayerType(25, gopacket.LayerTypeMetadata{Name: "PPP", Decoder: gopacket.DecodeFunc(decodePPP)})
LayerTypePPPoE = gopacket.RegisterLayerType(26, gopacket.LayerTypeMetadata{Name: "PPPoE", Decoder: gopacket.DecodeFunc(decodePPPoE)})
LayerTypeRUDP = gopacket.RegisterLayerType(27, gopacket.LayerTypeMetadata{Name: "RUDP", Decoder: gopacket.DecodeFunc(decodeRUDP)})
LayerTypeSCTP = gopacket.RegisterLayerType(28, gopacket.LayerTypeMetadata{Name: "SCTP", Decoder: gopacket.DecodeFunc(decodeSCTP)})
LayerTypeSCTPUnknownChunkType = gopacket.RegisterLayerType(29, gopacket.LayerTypeMetadata{Name: "SCTPUnknownChunkType", Decoder: nil})
LayerTypeSCTPData = gopacket.RegisterLayerType(30, gopacket.LayerTypeMetadata{Name: "SCTPData", Decoder: nil})
LayerTypeSCTPInit = gopacket.RegisterLayerType(31, gopacket.LayerTypeMetadata{Name: "SCTPInit", Decoder: nil})
LayerTypeSCTPSack = gopacket.RegisterLayerType(32, gopacket.LayerTypeMetadata{Name: "SCTPSack", Decoder: nil})
LayerTypeSCTPHeartbeat = gopacket.RegisterLayerType(33, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeat", Decoder: nil})
LayerTypeSCTPError = gopacket.RegisterLayerType(34, gopacket.LayerTypeMetadata{Name: "SCTPError", Decoder: nil})
LayerTypeSCTPShutdown = gopacket.RegisterLayerType(35, gopacket.LayerTypeMetadata{Name: "SCTPShutdown", Decoder: nil})
LayerTypeSCTPShutdownAck = gopacket.RegisterLayerType(36, gopacket.LayerTypeMetadata{Name: "SCTPShutdownAck", Decoder: nil})
LayerTypeSCTPCookieEcho = gopacket.RegisterLayerType(37, gopacket.LayerTypeMetadata{Name: "SCTPCookieEcho", Decoder: nil})
LayerTypeSCTPEmptyLayer = gopacket.RegisterLayerType(38, gopacket.LayerTypeMetadata{Name: "SCTPEmptyLayer", Decoder: nil})
LayerTypeSCTPInitAck = gopacket.RegisterLayerType(39, gopacket.LayerTypeMetadata{Name: "SCTPInitAck", Decoder: nil})
LayerTypeSCTPHeartbeatAck = gopacket.RegisterLayerType(40, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeatAck", Decoder: nil})
LayerTypeSCTPAbort = gopacket.RegisterLayerType(41, gopacket.LayerTypeMetadata{Name: "SCTPAbort", Decoder: nil})
LayerTypeSCTPShutdownComplete = gopacket.RegisterLayerType(42, gopacket.LayerTypeMetadata{Name: "SCTPShutdownComplete", Decoder: nil})
LayerTypeSCTPCookieAck = gopacket.RegisterLayerType(43, gopacket.LayerTypeMetadata{Name: "SCTPCookieAck", Decoder: nil})
LayerTypeTCP = gopacket.RegisterLayerType(44, gopacket.LayerTypeMetadata{Name: "TCP", Decoder: gopacket.DecodeFunc(decodeTCP)})
LayerTypeUDP = gopacket.RegisterLayerType(45, gopacket.LayerTypeMetadata{Name: "UDP", Decoder: gopacket.DecodeFunc(decodeUDP)})
LayerTypeIPv6HopByHop = gopacket.RegisterLayerType(46, gopacket.LayerTypeMetadata{Name: "IPv6HopByHop", Decoder: gopacket.DecodeFunc(decodeIPv6HopByHop)})
LayerTypeIPv6Routing = gopacket.RegisterLayerType(47, gopacket.LayerTypeMetadata{Name: "IPv6Routing", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)})
LayerTypeIPv6Fragment = gopacket.RegisterLayerType(48, gopacket.LayerTypeMetadata{Name: "IPv6Fragment", Decoder: gopacket.DecodeFunc(decodeIPv6Fragment)})
LayerTypeIPv6Destination = gopacket.RegisterLayerType(49, gopacket.LayerTypeMetadata{Name: "IPv6Destination", Decoder: gopacket.DecodeFunc(decodeIPv6Destination)})
LayerTypeIPSecAH = gopacket.RegisterLayerType(50, gopacket.LayerTypeMetadata{Name: "IPSecAH", Decoder: gopacket.DecodeFunc(decodeIPSecAH)})
LayerTypeIPSecESP = gopacket.RegisterLayerType(51, gopacket.LayerTypeMetadata{Name: "IPSecESP", Decoder: gopacket.DecodeFunc(decodeIPSecESP)})
LayerTypeUDPLite = gopacket.RegisterLayerType(52, gopacket.LayerTypeMetadata{Name: "UDPLite", Decoder: gopacket.DecodeFunc(decodeUDPLite)})
LayerTypeFDDI = gopacket.RegisterLayerType(53, gopacket.LayerTypeMetadata{Name: "FDDI", Decoder: gopacket.DecodeFunc(decodeFDDI)})
LayerTypeLoopback = gopacket.RegisterLayerType(54, gopacket.LayerTypeMetadata{Name: "Loopback", Decoder: gopacket.DecodeFunc(decodeLoopback)})
LayerTypeEAP = gopacket.RegisterLayerType(55, gopacket.LayerTypeMetadata{Name: "EAP", Decoder: gopacket.DecodeFunc(decodeEAP)})
LayerTypeEAPOL = gopacket.RegisterLayerType(56, gopacket.LayerTypeMetadata{Name: "EAPOL", Decoder: gopacket.DecodeFunc(decodeEAPOL)})
LayerTypeICMPv6 = gopacket.RegisterLayerType(57, gopacket.LayerTypeMetadata{Name: "ICMPv6", Decoder: gopacket.DecodeFunc(decodeICMPv6)})
LayerTypeLinkLayerDiscovery = gopacket.RegisterLayerType(58, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscovery", Decoder: gopacket.DecodeFunc(decodeLinkLayerDiscovery)})
LayerTypeCiscoDiscoveryInfo = gopacket.RegisterLayerType(59, gopacket.LayerTypeMetadata{Name: "CiscoDiscoveryInfo", Decoder: gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)})
LayerTypeLinkLayerDiscoveryInfo = gopacket.RegisterLayerType(60, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscoveryInfo", Decoder: nil})
LayerTypeNortelDiscovery = gopacket.RegisterLayerType(61, gopacket.LayerTypeMetadata{Name: "NortelDiscovery", Decoder: gopacket.DecodeFunc(decodeNortelDiscovery)})
LayerTypeIGMP = gopacket.RegisterLayerType(62, gopacket.LayerTypeMetadata{Name: "IGMP", Decoder: gopacket.DecodeFunc(decodeIGMP)})
LayerTypePFLog = gopacket.RegisterLayerType(63, gopacket.LayerTypeMetadata{Name: "PFLog", Decoder: gopacket.DecodeFunc(decodePFLog)})
LayerTypeRadioTap = gopacket.RegisterLayerType(64, gopacket.LayerTypeMetadata{Name: "RadioTap", Decoder: gopacket.DecodeFunc(decodeRadioTap)})
LayerTypeDot11 = gopacket.RegisterLayerType(65, gopacket.LayerTypeMetadata{Name: "Dot11", Decoder: gopacket.DecodeFunc(decodeDot11)})
LayerTypeDot11Ctrl = gopacket.RegisterLayerType(66, gopacket.LayerTypeMetadata{Name: "Dot11Ctrl", Decoder: gopacket.DecodeFunc(decodeDot11Ctrl)})
LayerTypeDot11Data = gopacket.RegisterLayerType(67, gopacket.LayerTypeMetadata{Name: "Dot11Data", Decoder: gopacket.DecodeFunc(decodeDot11Data)})
LayerTypeDot11DataCFAck = gopacket.RegisterLayerType(68, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
LayerTypeDot11DataCFPoll = gopacket.RegisterLayerType(69, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
LayerTypeDot11DataCFAckPoll = gopacket.RegisterLayerType(70, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
LayerTypeDot11DataNull = gopacket.RegisterLayerType(71, gopacket.LayerTypeMetadata{Name: "Dot11DataNull", Decoder: gopacket.DecodeFunc(decodeDot11DataNull)})
LayerTypeDot11DataCFAckNoData = gopacket.RegisterLayerType(72, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
LayerTypeDot11DataCFPollNoData = gopacket.RegisterLayerType(73, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
LayerTypeDot11DataCFAckPollNoData = gopacket.RegisterLayerType(74, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
LayerTypeDot11DataQOSData = gopacket.RegisterLayerType(75, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSData", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSData)})
LayerTypeDot11DataQOSDataCFAck = gopacket.RegisterLayerType(76, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck)})
LayerTypeDot11DataQOSDataCFPoll = gopacket.RegisterLayerType(77, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll)})
LayerTypeDot11DataQOSDataCFAckPoll = gopacket.RegisterLayerType(78, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll)})
LayerTypeDot11DataQOSNull = gopacket.RegisterLayerType(79, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSNull", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSNull)})
LayerTypeDot11DataQOSCFPollNoData = gopacket.RegisterLayerType(80, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData)})
LayerTypeDot11DataQOSCFAckPollNoData = gopacket.RegisterLayerType(81, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData)})
LayerTypeDot11InformationElement = gopacket.RegisterLayerType(82, gopacket.LayerTypeMetadata{Name: "Dot11InformationElement", Decoder: gopacket.DecodeFunc(decodeDot11InformationElement)})
LayerTypeDot11CtrlCTS = gopacket.RegisterLayerType(83, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCTS)})
LayerTypeDot11CtrlRTS = gopacket.RegisterLayerType(84, gopacket.LayerTypeMetadata{Name: "Dot11CtrlRTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlRTS)})
LayerTypeDot11CtrlBlockAckReq = gopacket.RegisterLayerType(85, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAckReq", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq)})
LayerTypeDot11CtrlBlockAck = gopacket.RegisterLayerType(86, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAck)})
LayerTypeDot11CtrlPowersavePoll = gopacket.RegisterLayerType(87, gopacket.LayerTypeMetadata{Name: "Dot11CtrlPowersavePoll", Decoder: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll)})
LayerTypeDot11CtrlAck = gopacket.RegisterLayerType(88, gopacket.LayerTypeMetadata{Name: "Dot11CtrlAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlAck)})
LayerTypeDot11CtrlCFEnd = gopacket.RegisterLayerType(89, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEnd", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEnd)})
LayerTypeDot11CtrlCFEndAck = gopacket.RegisterLayerType(90, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEndAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck)})
LayerTypeDot11MgmtAssociationReq = gopacket.RegisterLayerType(91, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq)})
LayerTypeDot11MgmtAssociationResp = gopacket.RegisterLayerType(92, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp)})
LayerTypeDot11MgmtReassociationReq = gopacket.RegisterLayerType(93, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq)})
LayerTypeDot11MgmtReassociationResp = gopacket.RegisterLayerType(94, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp)})
LayerTypeDot11MgmtProbeReq = gopacket.RegisterLayerType(95, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeReq)})
LayerTypeDot11MgmtProbeResp = gopacket.RegisterLayerType(96, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeResp)})
LayerTypeDot11MgmtMeasurementPilot = gopacket.RegisterLayerType(97, gopacket.LayerTypeMetadata{Name: "Dot11MgmtMeasurementPilot", Decoder: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot)})
LayerTypeDot11MgmtBeacon = gopacket.RegisterLayerType(98, gopacket.LayerTypeMetadata{Name: "Dot11MgmtBeacon", Decoder: gopacket.DecodeFunc(decodeDot11MgmtBeacon)})
LayerTypeDot11MgmtATIM = gopacket.RegisterLayerType(99, gopacket.LayerTypeMetadata{Name: "Dot11MgmtATIM", Decoder: gopacket.DecodeFunc(decodeDot11MgmtATIM)})
LayerTypeDot11MgmtDisassociation = gopacket.RegisterLayerType(100, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDisassociation", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDisassociation)})
LayerTypeDot11MgmtAuthentication = gopacket.RegisterLayerType(101, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAuthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAuthentication)})
LayerTypeDot11MgmtDeauthentication = gopacket.RegisterLayerType(102, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDeauthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication)})
LayerTypeDot11MgmtAction = gopacket.RegisterLayerType(103, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAction", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAction)})
LayerTypeDot11MgmtActionNoAck = gopacket.RegisterLayerType(104, gopacket.LayerTypeMetadata{Name: "Dot11MgmtActionNoAck", Decoder: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck)})
LayerTypeDot11MgmtArubaWLAN = gopacket.RegisterLayerType(105, gopacket.LayerTypeMetadata{Name: "Dot11MgmtArubaWLAN", Decoder: gopacket.DecodeFunc(decodeDot11MgmtArubaWLAN)})
LayerTypeDot11WEP = gopacket.RegisterLayerType(106, gopacket.LayerTypeMetadata{Name: "Dot11WEP", Decoder: gopacket.DecodeFunc(decodeDot11WEP)})
LayerTypeDNS = gopacket.RegisterLayerType(107, gopacket.LayerTypeMetadata{Name: "DNS", Decoder: gopacket.DecodeFunc(decodeDNS)})
LayerTypeUSB = gopacket.RegisterLayerType(108, gopacket.LayerTypeMetadata{Name: "USB", Decoder: gopacket.DecodeFunc(decodeUSB)})
LayerTypeUSBRequestBlockSetup = gopacket.RegisterLayerType(109, gopacket.LayerTypeMetadata{Name: "USBRequestBlockSetup", Decoder: gopacket.DecodeFunc(decodeUSBRequestBlockSetup)})
LayerTypeUSBControl = gopacket.RegisterLayerType(110, gopacket.LayerTypeMetadata{Name: "USBControl", Decoder: gopacket.DecodeFunc(decodeUSBControl)})
LayerTypeUSBInterrupt = gopacket.RegisterLayerType(111, gopacket.LayerTypeMetadata{Name: "USBInterrupt", Decoder: gopacket.DecodeFunc(decodeUSBInterrupt)})
LayerTypeUSBBulk = gopacket.RegisterLayerType(112, gopacket.LayerTypeMetadata{Name: "USBBulk", Decoder: gopacket.DecodeFunc(decodeUSBBulk)})
LayerTypeLinuxSLL = gopacket.RegisterLayerType(113, gopacket.LayerTypeMetadata{Name: "Linux SLL", Decoder: gopacket.DecodeFunc(decodeLinuxSLL)})
LayerTypeSFlow = gopacket.RegisterLayerType(114, gopacket.LayerTypeMetadata{Name: "SFlow", Decoder: gopacket.DecodeFunc(decodeSFlow)})
LayerTypePrismHeader = gopacket.RegisterLayerType(115, gopacket.LayerTypeMetadata{Name: "Prism monitor mode header", Decoder: gopacket.DecodeFunc(decodePrismHeader)})
LayerTypeVXLAN = gopacket.RegisterLayerType(116, gopacket.LayerTypeMetadata{Name: "VXLAN", Decoder: gopacket.DecodeFunc(decodeVXLAN)})
LayerTypeNTP = gopacket.RegisterLayerType(117, gopacket.LayerTypeMetadata{Name: "NTP", Decoder: gopacket.DecodeFunc(decodeNTP)})
LayerTypeDHCPv4 = gopacket.RegisterLayerType(118, gopacket.LayerTypeMetadata{Name: "DHCPv4", Decoder: gopacket.DecodeFunc(decodeDHCPv4)})
LayerTypeVRRP = gopacket.RegisterLayerType(119, gopacket.LayerTypeMetadata{Name: "VRRP", Decoder: gopacket.DecodeFunc(decodeVRRP)})
LayerTypeGeneve = gopacket.RegisterLayerType(120, gopacket.LayerTypeMetadata{Name: "Geneve", Decoder: gopacket.DecodeFunc(decodeGeneve)})
)
var (
// LayerClassIPNetwork contains TCP/IP network layer types.
LayerClassIPNetwork = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPv4,
LayerTypeIPv6,
})
// LayerClassIPTransport contains TCP/IP transport layer types.
LayerClassIPTransport = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeTCP,
LayerTypeUDP,
LayerTypeSCTP,
})
// LayerClassIPControl contains TCP/IP control protocols.
LayerClassIPControl = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeICMPv4,
LayerTypeICMPv6,
})
// LayerClassSCTPChunk contains SCTP chunk types (not the top-level SCTP
// layer).
LayerClassSCTPChunk = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeSCTPUnknownChunkType,
LayerTypeSCTPData,
LayerTypeSCTPInit,
LayerTypeSCTPSack,
LayerTypeSCTPHeartbeat,
LayerTypeSCTPError,
LayerTypeSCTPShutdown,
LayerTypeSCTPShutdownAck,
LayerTypeSCTPCookieEcho,
LayerTypeSCTPEmptyLayer,
LayerTypeSCTPInitAck,
LayerTypeSCTPHeartbeatAck,
LayerTypeSCTPAbort,
LayerTypeSCTPShutdownComplete,
LayerTypeSCTPCookieAck,
})
// LayerClassIPv6Extension contains IPv6 extension headers.
LayerClassIPv6Extension = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPv6HopByHop,
LayerTypeIPv6Routing,
LayerTypeIPv6Fragment,
LayerTypeIPv6Destination,
})
LayerClassIPSec = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPSecAH,
LayerTypeIPSecESP,
})
)

@ -0,0 +1,96 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/google/gopacket"
)
type LinuxSLLPacketType uint16
const (
LinuxSLLPacketTypeHost LinuxSLLPacketType = 0 // To us
LinuxSLLPacketTypeBroadcast LinuxSLLPacketType = 1 // To all
LinuxSLLPacketTypeMulticast LinuxSLLPacketType = 2 // To group
LinuxSLLPacketTypeOtherhost LinuxSLLPacketType = 3 // To someone else
LinuxSLLPacketTypeOutgoing LinuxSLLPacketType = 4 // Outgoing of any type
// These ones are invisible by user level
LinuxSLLPacketTypeLoopback LinuxSLLPacketType = 5 // MC/BRD frame looped back
LinuxSLLPacketTypeFastroute LinuxSLLPacketType = 6 // Fastrouted frame
)
func (l LinuxSLLPacketType) String() string {
switch l {
case LinuxSLLPacketTypeHost:
return "host"
case LinuxSLLPacketTypeBroadcast:
return "broadcast"
case LinuxSLLPacketTypeMulticast:
return "multicast"
case LinuxSLLPacketTypeOtherhost:
return "otherhost"
case LinuxSLLPacketTypeOutgoing:
return "outgoing"
case LinuxSLLPacketTypeLoopback:
return "loopback"
case LinuxSLLPacketTypeFastroute:
return "fastroute"
}
return fmt.Sprintf("Unknown(%d)", int(l))
}
type LinuxSLL struct {
BaseLayer
PacketType LinuxSLLPacketType
AddrLen uint16
Addr net.HardwareAddr
EthernetType EthernetType
}
// LayerType returns LayerTypeLinuxSLL.
func (sll *LinuxSLL) LayerType() gopacket.LayerType { return LayerTypeLinuxSLL }
func (sll *LinuxSLL) CanDecode() gopacket.LayerClass {
return LayerTypeLinuxSLL
}
func (sll *LinuxSLL) LinkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointMAC, sll.Addr, nil)
}
func (sll *LinuxSLL) NextLayerType() gopacket.LayerType {
return sll.EthernetType.LayerType()
}
func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 16 {
return errors.New("Linux SLL packet too small")
}
sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2]))
sll.AddrLen = binary.BigEndian.Uint16(data[4:6])
sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6])
sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16]))
sll.BaseLayer = BaseLayer{data[:16], data[16:]}
return nil
}
func decodeLinuxSLL(data []byte, p gopacket.PacketBuilder) error {
sll := &LinuxSLL{}
if err := sll.DecodeFromBytes(data, p); err != nil {
return err
}
p.AddLayer(sll)
p.SetLinkLayer(sll)
return p.NextDecoder(sll.EthernetType)
}

@ -0,0 +1,143 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
// LLC is the layer used for 802.2 Logical Link Control headers.
// See http://standards.ieee.org/getieee802/download/802.2-1998.pdf
type LLC struct {
BaseLayer
DSAP uint8
IG bool // true means group, false means individual
SSAP uint8
CR bool // true means response, false means command
Control uint16
}
// LayerType returns gopacket.LayerTypeLLC.
func (l *LLC) LayerType() gopacket.LayerType { return LayerTypeLLC }
// SNAP is used inside LLC. See
// http://standards.ieee.org/getieee802/download/802-2001.pdf.
// From http://en.wikipedia.org/wiki/Subnetwork_Access_Protocol:
// "[T]he Subnetwork Access Protocol (SNAP) is a mechanism for multiplexing,
// on networks using IEEE 802.2 LLC, more protocols than can be distinguished
// by the 8-bit 802.2 Service Access Point (SAP) fields."
type SNAP struct {
BaseLayer
OrganizationalCode []byte
Type EthernetType
}
// LayerType returns gopacket.LayerTypeSNAP.
func (s *SNAP) LayerType() gopacket.LayerType { return LayerTypeSNAP }
func decodeLLC(data []byte, p gopacket.PacketBuilder) error {
l := &LLC{
DSAP: data[0] & 0xFE,
IG: data[0]&0x1 != 0,
SSAP: data[1] & 0xFE,
CR: data[1]&0x1 != 0,
Control: uint16(data[2]),
}
if l.Control&0x1 == 0 || l.Control&0x3 == 0x1 {
l.Control = l.Control<<8 | uint16(data[3])
l.Contents = data[:4]
l.Payload = data[4:]
} else {
l.Contents = data[:3]
l.Payload = data[3:]
}
p.AddLayer(l)
if l.DSAP == 0xAA && l.SSAP == 0xAA {
return p.NextDecoder(LayerTypeSNAP)
}
return p.NextDecoder(gopacket.DecodeUnknown)
}
func decodeSNAP(data []byte, p gopacket.PacketBuilder) error {
s := &SNAP{
OrganizationalCode: data[:3],
Type: EthernetType(binary.BigEndian.Uint16(data[3:5])),
BaseLayer: BaseLayer{data[:5], data[5:]},
}
p.AddLayer(s)
// BUG(gconnell): When decoding SNAP, we treat the SNAP type as an Ethernet
// type. This may not actually be an ethernet type in all cases,
// depending on the organizational code. Right now, we don't check.
return p.NextDecoder(s.Type)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (l *LLC) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var ig_flag, cr_flag byte
var length int
if l.Control&0xFF00 != 0 {
length = 4
} else {
length = 3
}
if l.DSAP&0x1 != 0 {
return errors.New("DSAP value invalid, should not include IG flag bit")
}
if l.SSAP&0x1 != 0 {
return errors.New("SSAP value invalid, should not include CR flag bit")
}
if buf, err := b.PrependBytes(length); err != nil {
return err
} else {
ig_flag = 0
if l.IG {
ig_flag = 0x1
}
cr_flag = 0
if l.CR {
cr_flag = 0x1
}
buf[0] = l.DSAP + ig_flag
buf[1] = l.SSAP + cr_flag
if length == 4 {
buf[2] = uint8(l.Control >> 8)
buf[3] = uint8(l.Control)
} else {
buf[2] = uint8(l.Control)
}
}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (s *SNAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if buf, err := b.PrependBytes(5); err != nil {
return err
} else {
buf[0] = s.OrganizationalCode[0]
buf[1] = s.OrganizationalCode[1]
buf[2] = s.OrganizationalCode[2]
binary.BigEndian.PutUint16(buf[3:5], uint16(s.Type))
}
return nil
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,80 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
// Loopback contains the header for loopback encapsulation. This header is
// used by both BSD and OpenBSD style loopback decoding (pcap's DLT_NULL
// and DLT_LOOP, respectively).
type Loopback struct {
BaseLayer
Family ProtocolFamily
}
// LayerType returns LayerTypeLoopback.
func (l *Loopback) LayerType() gopacket.LayerType { return LayerTypeLoopback }
// DecodeFromBytes decodes the given bytes into this layer.
func (l *Loopback) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
return errors.New("Loopback packet too small")
}
// The protocol could be either big-endian or little-endian, we're
// not sure. But we're PRETTY sure that the value is less than
// 256, so we can check the first two bytes.
var prot uint32
if data[0] == 0 && data[1] == 0 {
prot = binary.BigEndian.Uint32(data[:4])
} else {
prot = binary.LittleEndian.Uint32(data[:4])
}
if prot > 0xFF {
return fmt.Errorf("Invalid loopback protocol %q", data[:4])
}
l.Family = ProtocolFamily(prot)
l.BaseLayer = BaseLayer{data[:4], data[4:]}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (l *Loopback) CanDecode() gopacket.LayerClass {
return LayerTypeLoopback
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (l *Loopback) NextLayerType() gopacket.LayerType {
return l.Family.LayerType()
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
func (l *Loopback) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
binary.LittleEndian.PutUint32(bytes, uint32(l.Family))
return nil
}
func decodeLoopback(data []byte, p gopacket.PacketBuilder) error {
l := Loopback{}
if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil {
return err
}
p.AddLayer(&l)
return p.NextDecoder(l.Family)
}

@ -0,0 +1,87 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
// MPLS is the MPLS packet header.
type MPLS struct {
BaseLayer
Label uint32
TrafficClass uint8
StackBottom bool
TTL uint8
}
// LayerType returns gopacket.LayerTypeMPLS.
func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS }
// ProtocolGuessingDecoder attempts to guess the protocol of the bytes it's
// given, then decode the packet accordingly. Its algorithm for guessing is:
// If the packet starts with byte 0x45-0x4F: IPv4
// If the packet starts with byte 0x60-0x6F: IPv6
// Otherwise: Error
// See draft-hsmit-isis-aal5mux-00.txt for more detail on this approach.
type ProtocolGuessingDecoder struct{}
func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error {
switch data[0] {
// 0x40 | header_len, where header_len is at least 5.
case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f:
return decodeIPv4(data, p)
// IPv6 can start with any byte whose first 4 bits are 0x6.
case 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f:
return decodeIPv6(data, p)
}
return errors.New("Unable to guess protocol of packet data")
}
// MPLSPayloadDecoder is the decoder used to data encapsulated by each MPLS
// layer. MPLS contains no type information, so we have to explicitly decide
// which decoder to use. This is initially set to ProtocolGuessingDecoder, our
// simple attempt at guessing protocols based on the first few bytes of data
// available to us. However, if you know that in your environment MPLS always
// encapsulates a specific protocol, you may reset this.
var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{}
func decodeMPLS(data []byte, p gopacket.PacketBuilder) error {
decoded := binary.BigEndian.Uint32(data[:4])
mpls := &MPLS{
Label: decoded >> 12,
TrafficClass: uint8(decoded>>9) & 0x7,
StackBottom: decoded&0x100 != 0,
TTL: uint8(decoded),
BaseLayer: BaseLayer{data[:4], data[4:]},
}
p.AddLayer(mpls)
if mpls.StackBottom {
return p.NextDecoder(MPLSPayloadDecoder)
}
return p.NextDecoder(gopacket.DecodeFunc(decodeMPLS))
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (m *MPLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
encoded := m.Label << 12
encoded |= uint32(m.TrafficClass) << 9
encoded |= uint32(m.TTL)
if m.StackBottom {
encoded |= 0x100
}
binary.BigEndian.PutUint32(bytes, encoded)
return nil
}

@ -0,0 +1,611 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// Enum types courtesy of...
// http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-ndp.c
package layers
import (
"fmt"
"github.com/google/gopacket"
"net"
)
type NDPChassisType uint8
// Nortel Chassis Types
const (
NDPChassisother NDPChassisType = 1
NDPChassis3000 NDPChassisType = 2
NDPChassis3030 NDPChassisType = 3
NDPChassis2310 NDPChassisType = 4
NDPChassis2810 NDPChassisType = 5
NDPChassis2912 NDPChassisType = 6
NDPChassis2914 NDPChassisType = 7
NDPChassis271x NDPChassisType = 8
NDPChassis2813 NDPChassisType = 9
NDPChassis2814 NDPChassisType = 10
NDPChassis2915 NDPChassisType = 11
NDPChassis5000 NDPChassisType = 12
NDPChassis2813SA NDPChassisType = 13
NDPChassis2814SA NDPChassisType = 14
NDPChassis810M NDPChassisType = 15
NDPChassisEthercell NDPChassisType = 16
NDPChassis5005 NDPChassisType = 17
NDPChassisAlcatelEWC NDPChassisType = 18
NDPChassis2715SA NDPChassisType = 20
NDPChassis2486 NDPChassisType = 21
NDPChassis28000series NDPChassisType = 22
NDPChassis23000series NDPChassisType = 23
NDPChassis5DN00xseries NDPChassisType = 24
NDPChassisBayStackEthernet NDPChassisType = 25
NDPChassis23100series NDPChassisType = 26
NDPChassis100BaseTHub NDPChassisType = 27
NDPChassis3000FastEthernet NDPChassisType = 28
NDPChassisOrionSwitch NDPChassisType = 29
NDPChassisDDS NDPChassisType = 31
NDPChassisCentillion6slot NDPChassisType = 32
NDPChassisCentillion12slot NDPChassisType = 33
NDPChassisCentillion1slot NDPChassisType = 34
NDPChassisBayStack301 NDPChassisType = 35
NDPChassisBayStackTokenRingHub NDPChassisType = 36
NDPChassisFVCMultimediaSwitch NDPChassisType = 37
NDPChassisSwitchNode NDPChassisType = 38
NDPChassisBayStack302Switch NDPChassisType = 39
NDPChassisBayStack350Switch NDPChassisType = 40
NDPChassisBayStack150EthernetHub NDPChassisType = 41
NDPChassisCentillion50NSwitch NDPChassisType = 42
NDPChassisCentillion50TSwitch NDPChassisType = 43
NDPChassisBayStack303304Switches NDPChassisType = 44
NDPChassisBayStack200EthernetHub NDPChassisType = 45
NDPChassisBayStack25010100EthernetHub NDPChassisType = 46
NDPChassisBayStack450101001000Switches NDPChassisType = 48
NDPChassisBayStack41010100Switches NDPChassisType = 49
NDPChassisPassport1200L3Switch NDPChassisType = 50
NDPChassisPassport1250L3Switch NDPChassisType = 51
NDPChassisPassport1100L3Switch NDPChassisType = 52
NDPChassisPassport1150L3Switch NDPChassisType = 53
NDPChassisPassport1050L3Switch NDPChassisType = 54
NDPChassisPassport1051L3Switch NDPChassisType = 55
NDPChassisPassport8610L3Switch NDPChassisType = 56
NDPChassisPassport8606L3Switch NDPChassisType = 57
NDPChassisPassport8010 NDPChassisType = 58
NDPChassisPassport8006 NDPChassisType = 59
NDPChassisBayStack670wirelessaccesspoint NDPChassisType = 60
NDPChassisPassport740 NDPChassisType = 61
NDPChassisPassport750 NDPChassisType = 62
NDPChassisPassport790 NDPChassisType = 63
NDPChassisBusinessPolicySwitch200010100Switches NDPChassisType = 64
NDPChassisPassport8110L2Switch NDPChassisType = 65
NDPChassisPassport8106L2Switch NDPChassisType = 66
NDPChassisBayStack3580GigSwitch NDPChassisType = 67
NDPChassisBayStack10PowerSupplyUnit NDPChassisType = 68
NDPChassisBayStack42010100Switch NDPChassisType = 69
NDPChassisOPTeraMetro1200EthernetServiceModule NDPChassisType = 70
NDPChassisOPTera8010co NDPChassisType = 71
NDPChassisOPTera8610coL3Switch NDPChassisType = 72
NDPChassisOPTera8110coL2Switch NDPChassisType = 73
NDPChassisOPTera8003 NDPChassisType = 74
NDPChassisOPTera8603L3Switch NDPChassisType = 75
NDPChassisOPTera8103L2Switch NDPChassisType = 76
NDPChassisBayStack380101001000Switch NDPChassisType = 77
NDPChassisEthernetSwitch47048T NDPChassisType = 78
NDPChassisOPTeraMetro1450EthernetServiceModule NDPChassisType = 79
NDPChassisOPTeraMetro1400EthernetServiceModule NDPChassisType = 80
NDPChassisAlteonSwitchFamily NDPChassisType = 81
NDPChassisEthernetSwitch46024TPWR NDPChassisType = 82
NDPChassisOPTeraMetro8010OPML2Switch NDPChassisType = 83
NDPChassisOPTeraMetro8010coOPML2Switch NDPChassisType = 84
NDPChassisOPTeraMetro8006OPML2Switch NDPChassisType = 85
NDPChassisOPTeraMetro8003OPML2Switch NDPChassisType = 86
NDPChassisAlteon180e NDPChassisType = 87
NDPChassisAlteonAD3 NDPChassisType = 88
NDPChassisAlteon184 NDPChassisType = 89
NDPChassisAlteonAD4 NDPChassisType = 90
NDPChassisPassport1424L3Switch NDPChassisType = 91
NDPChassisPassport1648L3Switch NDPChassisType = 92
NDPChassisPassport1612L3Switch NDPChassisType = 93
NDPChassisPassport1624L3Switch NDPChassisType = 94
NDPChassisBayStack38024FFiber1000Switch NDPChassisType = 95
NDPChassisEthernetRoutingSwitch551024T NDPChassisType = 96
NDPChassisEthernetRoutingSwitch551048T NDPChassisType = 97
NDPChassisEthernetSwitch47024T NDPChassisType = 98
NDPChassisNortelNetworksWirelessLANAccessPoint2220 NDPChassisType = 99
NDPChassisPassportRBS2402L3Switch NDPChassisType = 100
NDPChassisAlteonApplicationSwitch2424 NDPChassisType = 101
NDPChassisAlteonApplicationSwitch2224 NDPChassisType = 102
NDPChassisAlteonApplicationSwitch2208 NDPChassisType = 103
NDPChassisAlteonApplicationSwitch2216 NDPChassisType = 104
NDPChassisAlteonApplicationSwitch3408 NDPChassisType = 105
NDPChassisAlteonApplicationSwitch3416 NDPChassisType = 106
NDPChassisNortelNetworksWirelessLANSecuritySwitch2250 NDPChassisType = 107
NDPChassisEthernetSwitch42548T NDPChassisType = 108
NDPChassisEthernetSwitch42524T NDPChassisType = 109
NDPChassisNortelNetworksWirelessLANAccessPoint2221 NDPChassisType = 110
NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch NDPChassisType = 111
NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch NDPChassisType = 112
NDPChassisPassport830010slotchassis NDPChassisType = 113
NDPChassisPassport83006slotchassis NDPChassisType = 114
NDPChassisEthernetRoutingSwitch552024TPWR NDPChassisType = 115
NDPChassisEthernetRoutingSwitch552048TPWR NDPChassisType = 116
NDPChassisNortelNetworksVPNGateway3050 NDPChassisType = 117
NDPChassisAlteonSSL31010100 NDPChassisType = 118
NDPChassisAlteonSSL31010100Fiber NDPChassisType = 119
NDPChassisAlteonSSL31010100FIPS NDPChassisType = 120
NDPChassisAlteonSSL410101001000 NDPChassisType = 121
NDPChassisAlteonSSL410101001000Fiber NDPChassisType = 122
NDPChassisAlteonApplicationSwitch2424SSL NDPChassisType = 123
NDPChassisEthernetSwitch32524T NDPChassisType = 124
NDPChassisEthernetSwitch32524G NDPChassisType = 125
NDPChassisNortelNetworksWirelessLANAccessPoint2225 NDPChassisType = 126
NDPChassisNortelNetworksWirelessLANSecuritySwitch2270 NDPChassisType = 127
NDPChassis24portEthernetSwitch47024TPWR NDPChassisType = 128
NDPChassis48portEthernetSwitch47048TPWR NDPChassisType = 129
NDPChassisEthernetRoutingSwitch553024TFD NDPChassisType = 130
NDPChassisEthernetSwitch351024T NDPChassisType = 131
NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch NDPChassisType = 132
NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch NDPChassisType = 133
NDPChassisNortelSecureAccessSwitch NDPChassisType = 134
NDPChassisNortelNetworksVPNGateway3070 NDPChassisType = 135
NDPChassisOPTeraMetro3500 NDPChassisType = 136
NDPChassisSMBBES101024T NDPChassisType = 137
NDPChassisSMBBES101048T NDPChassisType = 138
NDPChassisSMBBES102024TPWR NDPChassisType = 139
NDPChassisSMBBES102048TPWR NDPChassisType = 140
NDPChassisSMBBES201024T NDPChassisType = 141
NDPChassisSMBBES201048T NDPChassisType = 142
NDPChassisSMBBES202024TPWR NDPChassisType = 143
NDPChassisSMBBES202048TPWR NDPChassisType = 144
NDPChassisSMBBES11024T NDPChassisType = 145
NDPChassisSMBBES11048T NDPChassisType = 146
NDPChassisSMBBES12024TPWR NDPChassisType = 147
NDPChassisSMBBES12048TPWR NDPChassisType = 148
NDPChassisSMBBES21024T NDPChassisType = 149
NDPChassisSMBBES21048T NDPChassisType = 150
NDPChassisSMBBES22024TPWR NDPChassisType = 151
NDPChassisSMBBES22048TPWR NDPChassisType = 152
NDPChassisOME6500 NDPChassisType = 153
NDPChassisEthernetRoutingSwitch4548GT NDPChassisType = 154
NDPChassisEthernetRoutingSwitch4548GTPWR NDPChassisType = 155
NDPChassisEthernetRoutingSwitch4550T NDPChassisType = 156
NDPChassisEthernetRoutingSwitch4550TPWR NDPChassisType = 157
NDPChassisEthernetRoutingSwitch4526FX NDPChassisType = 158
NDPChassisEthernetRoutingSwitch250026T NDPChassisType = 159
NDPChassisEthernetRoutingSwitch250026TPWR NDPChassisType = 160
NDPChassisEthernetRoutingSwitch250050T NDPChassisType = 161
NDPChassisEthernetRoutingSwitch250050TPWR NDPChassisType = 162
)
type NDPBackplaneType uint8
// Nortel Backplane Types
const (
NDPBackplaneOther NDPBackplaneType = 1
NDPBackplaneEthernet NDPBackplaneType = 2
NDPBackplaneEthernetTokenring NDPBackplaneType = 3
NDPBackplaneEthernetFDDI NDPBackplaneType = 4
NDPBackplaneEthernetTokenringFDDI NDPBackplaneType = 5
NDPBackplaneEthernetTokenringRedundantPower NDPBackplaneType = 6
NDPBackplaneEthernetTokenringFDDIRedundantPower NDPBackplaneType = 7
NDPBackplaneTokenRing NDPBackplaneType = 8
NDPBackplaneEthernetTokenringFastEthernet NDPBackplaneType = 9
NDPBackplaneEthernetFastEthernet NDPBackplaneType = 10
NDPBackplaneEthernetTokenringFastEthernetRedundantPower NDPBackplaneType = 11
NDPBackplaneEthernetFastEthernetGigabitEthernet NDPBackplaneType = 12
)
type NDPState uint8
// Device State
const (
NDPStateTopology NDPState = 1
NDPStateHeartbeat NDPState = 2
NDPStateNew NDPState = 3
)
// NortelDiscovery is a packet layer containing the Nortel Discovery Protocol.
type NortelDiscovery struct {
BaseLayer
IPAddress net.IP
SegmentID []byte
Chassis NDPChassisType
Backplane NDPBackplaneType
State NDPState
NumLinks uint8
}
// LayerType returns gopacket.LayerTypeNortelDiscovery.
func (c *NortelDiscovery) LayerType() gopacket.LayerType {
return LayerTypeNortelDiscovery
}
func decodeNortelDiscovery(data []byte, p gopacket.PacketBuilder) error {
c := &NortelDiscovery{}
if len(data) < 11 {
return fmt.Errorf("Invalid NortelDiscovery packet length %d", len(data))
}
c.IPAddress = data[0:4]
c.SegmentID = data[4:7]
c.Chassis = NDPChassisType(data[7])
c.Backplane = NDPBackplaneType(data[8])
c.State = NDPState(data[9])
c.NumLinks = uint8(data[10])
p.AddLayer(c)
return nil
}
func (t NDPChassisType) String() (s string) {
switch t {
case NDPChassisother:
s = "other"
case NDPChassis3000:
s = "3000"
case NDPChassis3030:
s = "3030"
case NDPChassis2310:
s = "2310"
case NDPChassis2810:
s = "2810"
case NDPChassis2912:
s = "2912"
case NDPChassis2914:
s = "2914"
case NDPChassis271x:
s = "271x"
case NDPChassis2813:
s = "2813"
case NDPChassis2814:
s = "2814"
case NDPChassis2915:
s = "2915"
case NDPChassis5000:
s = "5000"
case NDPChassis2813SA:
s = "2813SA"
case NDPChassis2814SA:
s = "2814SA"
case NDPChassis810M:
s = "810M"
case NDPChassisEthercell:
s = "Ethercell"
case NDPChassis5005:
s = "5005"
case NDPChassisAlcatelEWC:
s = "Alcatel Ethernet workgroup conc."
case NDPChassis2715SA:
s = "2715SA"
case NDPChassis2486:
s = "2486"
case NDPChassis28000series:
s = "28000 series"
case NDPChassis23000series:
s = "23000 series"
case NDPChassis5DN00xseries:
s = "5DN00x series"
case NDPChassisBayStackEthernet:
s = "BayStack Ethernet"
case NDPChassis23100series:
s = "23100 series"
case NDPChassis100BaseTHub:
s = "100Base-T Hub"
case NDPChassis3000FastEthernet:
s = "3000 Fast Ethernet"
case NDPChassisOrionSwitch:
s = "Orion switch"
case NDPChassisDDS:
s = "DDS"
case NDPChassisCentillion6slot:
s = "Centillion (6 slot)"
case NDPChassisCentillion12slot:
s = "Centillion (12 slot)"
case NDPChassisCentillion1slot:
s = "Centillion (1 slot)"
case NDPChassisBayStack301:
s = "BayStack 301"
case NDPChassisBayStackTokenRingHub:
s = "BayStack TokenRing Hub"
case NDPChassisFVCMultimediaSwitch:
s = "FVC Multimedia Switch"
case NDPChassisSwitchNode:
s = "Switch Node"
case NDPChassisBayStack302Switch:
s = "BayStack 302 Switch"
case NDPChassisBayStack350Switch:
s = "BayStack 350 Switch"
case NDPChassisBayStack150EthernetHub:
s = "BayStack 150 Ethernet Hub"
case NDPChassisCentillion50NSwitch:
s = "Centillion 50N switch"
case NDPChassisCentillion50TSwitch:
s = "Centillion 50T switch"
case NDPChassisBayStack303304Switches:
s = "BayStack 303 and 304 Switches"
case NDPChassisBayStack200EthernetHub:
s = "BayStack 200 Ethernet Hub"
case NDPChassisBayStack25010100EthernetHub:
s = "BayStack 250 10/100 Ethernet Hub"
case NDPChassisBayStack450101001000Switches:
s = "BayStack 450 10/100/1000 Switches"
case NDPChassisBayStack41010100Switches:
s = "BayStack 410 10/100 Switches"
case NDPChassisPassport1200L3Switch:
s = "Passport 1200 L3 Switch"
case NDPChassisPassport1250L3Switch:
s = "Passport 1250 L3 Switch"
case NDPChassisPassport1100L3Switch:
s = "Passport 1100 L3 Switch"
case NDPChassisPassport1150L3Switch:
s = "Passport 1150 L3 Switch"
case NDPChassisPassport1050L3Switch:
s = "Passport 1050 L3 Switch"
case NDPChassisPassport1051L3Switch:
s = "Passport 1051 L3 Switch"
case NDPChassisPassport8610L3Switch:
s = "Passport 8610 L3 Switch"
case NDPChassisPassport8606L3Switch:
s = "Passport 8606 L3 Switch"
case NDPChassisPassport8010:
s = "Passport 8010"
case NDPChassisPassport8006:
s = "Passport 8006"
case NDPChassisBayStack670wirelessaccesspoint:
s = "BayStack 670 wireless access point"
case NDPChassisPassport740:
s = "Passport 740"
case NDPChassisPassport750:
s = "Passport 750"
case NDPChassisPassport790:
s = "Passport 790"
case NDPChassisBusinessPolicySwitch200010100Switches:
s = "Business Policy Switch 2000 10/100 Switches"
case NDPChassisPassport8110L2Switch:
s = "Passport 8110 L2 Switch"
case NDPChassisPassport8106L2Switch:
s = "Passport 8106 L2 Switch"
case NDPChassisBayStack3580GigSwitch:
s = "BayStack 3580 Gig Switch"
case NDPChassisBayStack10PowerSupplyUnit:
s = "BayStack 10 Power Supply Unit"
case NDPChassisBayStack42010100Switch:
s = "BayStack 420 10/100 Switch"
case NDPChassisOPTeraMetro1200EthernetServiceModule:
s = "OPTera Metro 1200 Ethernet Service Module"
case NDPChassisOPTera8010co:
s = "OPTera 8010co"
case NDPChassisOPTera8610coL3Switch:
s = "OPTera 8610co L3 switch"
case NDPChassisOPTera8110coL2Switch:
s = "OPTera 8110co L2 switch"
case NDPChassisOPTera8003:
s = "OPTera 8003"
case NDPChassisOPTera8603L3Switch:
s = "OPTera 8603 L3 switch"
case NDPChassisOPTera8103L2Switch:
s = "OPTera 8103 L2 switch"
case NDPChassisBayStack380101001000Switch:
s = "BayStack 380 10/100/1000 Switch"
case NDPChassisEthernetSwitch47048T:
s = "Ethernet Switch 470-48T"
case NDPChassisOPTeraMetro1450EthernetServiceModule:
s = "OPTera Metro 1450 Ethernet Service Module"
case NDPChassisOPTeraMetro1400EthernetServiceModule:
s = "OPTera Metro 1400 Ethernet Service Module"
case NDPChassisAlteonSwitchFamily:
s = "Alteon Switch Family"
case NDPChassisEthernetSwitch46024TPWR:
s = "Ethernet Switch 460-24T-PWR"
case NDPChassisOPTeraMetro8010OPML2Switch:
s = "OPTera Metro 8010 OPM L2 Switch"
case NDPChassisOPTeraMetro8010coOPML2Switch:
s = "OPTera Metro 8010co OPM L2 Switch"
case NDPChassisOPTeraMetro8006OPML2Switch:
s = "OPTera Metro 8006 OPM L2 Switch"
case NDPChassisOPTeraMetro8003OPML2Switch:
s = "OPTera Metro 8003 OPM L2 Switch"
case NDPChassisAlteon180e:
s = "Alteon 180e"
case NDPChassisAlteonAD3:
s = "Alteon AD3"
case NDPChassisAlteon184:
s = "Alteon 184"
case NDPChassisAlteonAD4:
s = "Alteon AD4"
case NDPChassisPassport1424L3Switch:
s = "Passport 1424 L3 switch"
case NDPChassisPassport1648L3Switch:
s = "Passport 1648 L3 switch"
case NDPChassisPassport1612L3Switch:
s = "Passport 1612 L3 switch"
case NDPChassisPassport1624L3Switch:
s = "Passport 1624 L3 switch"
case NDPChassisBayStack38024FFiber1000Switch:
s = "BayStack 380-24F Fiber 1000 Switch"
case NDPChassisEthernetRoutingSwitch551024T:
s = "Ethernet Routing Switch 5510-24T"
case NDPChassisEthernetRoutingSwitch551048T:
s = "Ethernet Routing Switch 5510-48T"
case NDPChassisEthernetSwitch47024T:
s = "Ethernet Switch 470-24T"
case NDPChassisNortelNetworksWirelessLANAccessPoint2220:
s = "Nortel Networks Wireless LAN Access Point 2220"
case NDPChassisPassportRBS2402L3Switch:
s = "Passport RBS 2402 L3 switch"
case NDPChassisAlteonApplicationSwitch2424:
s = "Alteon Application Switch 2424"
case NDPChassisAlteonApplicationSwitch2224:
s = "Alteon Application Switch 2224"
case NDPChassisAlteonApplicationSwitch2208:
s = "Alteon Application Switch 2208"
case NDPChassisAlteonApplicationSwitch2216:
s = "Alteon Application Switch 2216"
case NDPChassisAlteonApplicationSwitch3408:
s = "Alteon Application Switch 3408"
case NDPChassisAlteonApplicationSwitch3416:
s = "Alteon Application Switch 3416"
case NDPChassisNortelNetworksWirelessLANSecuritySwitch2250:
s = "Nortel Networks Wireless LAN SecuritySwitch 2250"
case NDPChassisEthernetSwitch42548T:
s = "Ethernet Switch 425-48T"
case NDPChassisEthernetSwitch42524T:
s = "Ethernet Switch 425-24T"
case NDPChassisNortelNetworksWirelessLANAccessPoint2221:
s = "Nortel Networks Wireless LAN Access Point 2221"
case NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch:
s = "Nortel Metro Ethernet Service Unit 24-T SPF switch"
case NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch:
s = " Nortel Metro Ethernet Service Unit 24-T LX DC switch"
case NDPChassisPassport830010slotchassis:
s = "Passport 8300 10-slot chassis"
case NDPChassisPassport83006slotchassis:
s = "Passport 8300 6-slot chassis"
case NDPChassisEthernetRoutingSwitch552024TPWR:
s = "Ethernet Routing Switch 5520-24T-PWR"
case NDPChassisEthernetRoutingSwitch552048TPWR:
s = "Ethernet Routing Switch 5520-48T-PWR"
case NDPChassisNortelNetworksVPNGateway3050:
s = "Nortel Networks VPN Gateway 3050"
case NDPChassisAlteonSSL31010100:
s = "Alteon SSL 310 10/100"
case NDPChassisAlteonSSL31010100Fiber:
s = "Alteon SSL 310 10/100 Fiber"
case NDPChassisAlteonSSL31010100FIPS:
s = "Alteon SSL 310 10/100 FIPS"
case NDPChassisAlteonSSL410101001000:
s = "Alteon SSL 410 10/100/1000"
case NDPChassisAlteonSSL410101001000Fiber:
s = "Alteon SSL 410 10/100/1000 Fiber"
case NDPChassisAlteonApplicationSwitch2424SSL:
s = "Alteon Application Switch 2424-SSL"
case NDPChassisEthernetSwitch32524T:
s = "Ethernet Switch 325-24T"
case NDPChassisEthernetSwitch32524G:
s = "Ethernet Switch 325-24G"
case NDPChassisNortelNetworksWirelessLANAccessPoint2225:
s = "Nortel Networks Wireless LAN Access Point 2225"
case NDPChassisNortelNetworksWirelessLANSecuritySwitch2270:
s = "Nortel Networks Wireless LAN SecuritySwitch 2270"
case NDPChassis24portEthernetSwitch47024TPWR:
s = "24-port Ethernet Switch 470-24T-PWR"
case NDPChassis48portEthernetSwitch47048TPWR:
s = "48-port Ethernet Switch 470-48T-PWR"
case NDPChassisEthernetRoutingSwitch553024TFD:
s = "Ethernet Routing Switch 5530-24TFD"
case NDPChassisEthernetSwitch351024T:
s = "Ethernet Switch 3510-24T"
case NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch:
s = "Nortel Metro Ethernet Service Unit 12G AC L3 switch"
case NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch:
s = "Nortel Metro Ethernet Service Unit 12G DC L3 switch"
case NDPChassisNortelSecureAccessSwitch:
s = "Nortel Secure Access Switch"
case NDPChassisNortelNetworksVPNGateway3070:
s = "Nortel Networks VPN Gateway 3070"
case NDPChassisOPTeraMetro3500:
s = "OPTera Metro 3500"
case NDPChassisSMBBES101024T:
s = "SMB BES 1010 24T"
case NDPChassisSMBBES101048T:
s = "SMB BES 1010 48T"
case NDPChassisSMBBES102024TPWR:
s = "SMB BES 1020 24T PWR"
case NDPChassisSMBBES102048TPWR:
s = "SMB BES 1020 48T PWR"
case NDPChassisSMBBES201024T:
s = "SMB BES 2010 24T"
case NDPChassisSMBBES201048T:
s = "SMB BES 2010 48T"
case NDPChassisSMBBES202024TPWR:
s = "SMB BES 2020 24T PWR"
case NDPChassisSMBBES202048TPWR:
s = "SMB BES 2020 48T PWR"
case NDPChassisSMBBES11024T:
s = "SMB BES 110 24T"
case NDPChassisSMBBES11048T:
s = "SMB BES 110 48T"
case NDPChassisSMBBES12024TPWR:
s = "SMB BES 120 24T PWR"
case NDPChassisSMBBES12048TPWR:
s = "SMB BES 120 48T PWR"
case NDPChassisSMBBES21024T:
s = "SMB BES 210 24T"
case NDPChassisSMBBES21048T:
s = "SMB BES 210 48T"
case NDPChassisSMBBES22024TPWR:
s = "SMB BES 220 24T PWR"
case NDPChassisSMBBES22048TPWR:
s = "SMB BES 220 48T PWR"
case NDPChassisOME6500:
s = "OME 6500"
case NDPChassisEthernetRoutingSwitch4548GT:
s = "Ethernet Routing Switch 4548GT"
case NDPChassisEthernetRoutingSwitch4548GTPWR:
s = "Ethernet Routing Switch 4548GT-PWR"
case NDPChassisEthernetRoutingSwitch4550T:
s = "Ethernet Routing Switch 4550T"
case NDPChassisEthernetRoutingSwitch4550TPWR:
s = "Ethernet Routing Switch 4550T-PWR"
case NDPChassisEthernetRoutingSwitch4526FX:
s = "Ethernet Routing Switch 4526FX"
case NDPChassisEthernetRoutingSwitch250026T:
s = "Ethernet Routing Switch 2500-26T"
case NDPChassisEthernetRoutingSwitch250026TPWR:
s = "Ethernet Routing Switch 2500-26T-PWR"
case NDPChassisEthernetRoutingSwitch250050T:
s = "Ethernet Routing Switch 2500-50T"
case NDPChassisEthernetRoutingSwitch250050TPWR:
s = "Ethernet Routing Switch 2500-50T-PWR"
default:
s = "Unknown"
}
return
}
func (t NDPBackplaneType) String() (s string) {
switch t {
case NDPBackplaneOther:
s = "Other"
case NDPBackplaneEthernet:
s = "Ethernet"
case NDPBackplaneEthernetTokenring:
s = "Ethernet and Tokenring"
case NDPBackplaneEthernetFDDI:
s = "Ethernet and FDDI"
case NDPBackplaneEthernetTokenringFDDI:
s = "Ethernet, Tokenring and FDDI"
case NDPBackplaneEthernetTokenringRedundantPower:
s = "Ethernet and Tokenring with redundant power"
case NDPBackplaneEthernetTokenringFDDIRedundantPower:
s = "Ethernet, Tokenring, FDDI with redundant power"
case NDPBackplaneTokenRing:
s = "Token Ring"
case NDPBackplaneEthernetTokenringFastEthernet:
s = "Ethernet, Tokenring and Fast Ethernet"
case NDPBackplaneEthernetFastEthernet:
s = "Ethernet and Fast Ethernet"
case NDPBackplaneEthernetTokenringFastEthernetRedundantPower:
s = "Ethernet, Tokenring, Fast Ethernet with redundant power"
case NDPBackplaneEthernetFastEthernetGigabitEthernet:
s = "Ethernet, Fast Ethernet and Gigabit Ethernet"
default:
s = "Unknown"
}
return
}
func (t NDPState) String() (s string) {
switch t {
case NDPStateTopology:
s = "Topology Change"
case NDPStateHeartbeat:
s = "Heartbeat"
case NDPStateNew:
s = "New"
default:
s = "Unknown"
}
return
}

@ -0,0 +1,416 @@
// Copyright 2016 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
//
//******************************************************************************
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
//******************************************************************************
//
// Network Time Protocol (NTP) Decoding Layer
// ------------------------------------------
// This file provides a GoPacket decoding layer for NTP.
//
//******************************************************************************
//
// About The Network Time Protocol (NTP)
// -------------------------------------
// NTP is a protocol that enables computers on the internet to set their
// clocks to the correct time (or to a time that is acceptably close to the
// correct time). NTP runs on top of UDP.
//
// There have been a series of versions of the NTP protocol. The latest
// version is V4 and is specified in RFC 5905:
// http://www.ietf.org/rfc/rfc5905.txt
//
//******************************************************************************
//
// References
// ----------
//
// Wikipedia's NTP entry:
// https://en.wikipedia.org/wiki/Network_Time_Protocol
// This is the best place to get an overview of NTP.
//
// Network Time Protocol Home Website:
// http://www.ntp.org/
// This appears to be the official website of NTP.
//
// List of current NTP Protocol RFCs:
// http://www.ntp.org/rfc.html
//
// RFC 958: "Network Time Protocol (NTP)" (1985)
// https://tools.ietf.org/html/rfc958
// This is the original NTP specification.
//
// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992)
// https://tools.ietf.org/html/rfc1305
// The protocol was updated in 1992 yielding NTP V3.
//
// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010)
// https://www.ietf.org/rfc/rfc5905.txt
// The protocol was updated in 2010 yielding NTP V4.
// V4 is backwards compatible with all previous versions of NTP.
//
// RFC 5906: "Network Time Protocol Version 4: Autokey Specification"
// https://tools.ietf.org/html/rfc5906
// This document addresses the security of the NTP protocol
// and is probably not relevant to this package.
//
// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)"
// https://tools.ietf.org/html/rfc5907
// This document addresses the management of NTP servers and
// is probably not relevant to this package.
//
// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6"
// https://tools.ietf.org/html/rfc5908
// This document addresses the use of NTP in DHCPv6 and is
// probably not relevant to this package.
//
// "Let's make a NTP Client in C"
// https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html
// This web page contains useful information about the details of NTP,
// including an NTP record struture in C, and C code.
//
// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)"
// http://what-when-how.com/computer-network-time-synchronization/
// ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/
// This web page contains useful information on the details of NTP.
//
// "Technical information - NTP Data Packet"
// https://www.meinbergglobal.com/english/info/ntp-packet.htm
// This page has a helpful diagram of an NTP V4 packet.
//
//******************************************************************************
//
// Obsolete References
// -------------------
//
// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989)
// https://tools.ietf.org/html/rfc1119
// Version 2 was drafted in 1989.
// It is unclear whether V2 was ever implememented or whether the
// ideas ended up in V3 (which was implemented in 1992).
//
// RFC 1361: "Simple Network Time Protocol (SNTP)"
// https://tools.ietf.org/html/rfc1361
// This document is obsoleted by RFC 1769 and is included only for completeness.
//
// RFC 1769: "Simple Network Time Protocol (SNTP)"
// https://tools.ietf.org/html/rfc1769
// This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness.
//
// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
// https://tools.ietf.org/html/rfc2030
// This document is obsoleted by RFC 4330 and is included only for completeness.
//
// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
// https://tools.ietf.org/html/rfc4330
// This document is obsoleted by RFC 5905 and is included only for completeness.
//
//******************************************************************************
//
// Endian And Bit Numbering Issues
// -------------------------------
//
// Endian and bit numbering issues can be confusing. Here is some
// clarification:
//
// ENDIAN: Values are sent big endian.
// https://en.wikipedia.org/wiki/Endianness
//
// BIT NUMBERING: Bits are numbered 0 upwards from the most significant
// bit to the least significant bit. This means that if there is a 32-bit
// value, the most significant bit is called bit 0 and the least
// significant bit is called bit 31.
//
// See RFC 791 Appendix B for more discussion.
//
//******************************************************************************
//
// NTP V3 and V4 Packet Format
// ---------------------------
// NTP packets are UDP packets whose payload contains an NTP record.
//
// The NTP RFC defines the format of the NTP record.
//
// There have been four versions of the protocol:
//
// V1 in 1985
// V2 in 1989
// V3 in 1992
// V4 in 2010
//
// It is clear that V1 and V2 are obsolete, and there is no need to
// cater for these formats.
//
// V3 and V4 essentially use the same format, with V4 adding some optional
// fields on the end. So this package supports the V3 and V4 formats.
//
// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains
// the following diagram for the NTP record format:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |LI | VN |Mode | Stratum | Poll | Precision |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Root Delay |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Root Dispersion |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Reference ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// + Reference Timestamp (64) +
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// + Origin Timestamp (64) +
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// + Receive Timestamp (64) +
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// + Transmit Timestamp (64) +
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . .
// . Extension Field 1 (variable) .
// . .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . .
// . Extension Field 2 (variable) .
// . .
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Key Identifier |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | dgst (128) |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// From http://www.ietf.org/rfc/rfc5905.txt
//
// The fields "Extension Field 1 (variable)" and later are optional fields,
// and so we can set a minimum NTP record size of 48 bytes.
//
const ntpMinimumRecordSizeInBytes int = 48
//******************************************************************************
// NTP Type
// --------
// Type NTP implements the DecodingLayer interface. Each NTP object
// represents in a structured form the NTP record present as the UDP
// payload in an NTP UDP packet.
//
type NTPLeapIndicator uint8
type NTPVersion uint8
type NTPMode uint8
type NTPStratum uint8
type NTPLog2Seconds int8
type NTPFixed16Seconds uint32
type NTPReferenceID uint32
type NTPTimestamp uint64
type NTP struct {
BaseLayer // Stores the packet bytes and payload bytes.
LeapIndicator NTPLeapIndicator // [0,3]. Indicates whether leap second(s) is to be added.
Version NTPVersion // [0,7]. Version of the NTP protocol.
Mode NTPMode // [0,7]. Mode.
Stratum NTPStratum // [0,255]. Stratum of time server in the server tree.
Poll NTPLog2Seconds // [-128,127]. The maximum interval between successive messages, in log2 seconds.
Precision NTPLog2Seconds // [-128,127]. The precision of the system clock, in log2 seconds.
RootDelay NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16.
RootDispersion NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16.
ReferenceID NTPReferenceID // ID code of reference clock [0,2^32-1].
ReferenceTimestamp NTPTimestamp // Most recent timestamp from the reference clock.
OriginTimestamp NTPTimestamp // Local time when request was sent from local host.
ReceiveTimestamp NTPTimestamp // Local time (on server) that request arrived at server host.
TransmitTimestamp NTPTimestamp // Local time (on server) that request departed server host.
// FIX: This package should analyse the extension fields and represent the extension fields too.
ExtensionBytes []byte // Just put extensions in a byte slice.
}
//******************************************************************************
// LayerType returns the layer type of the NTP object, which is LayerTypeNTP.
func (d *NTP) LayerType() gopacket.LayerType {
return LayerTypeNTP
}
//******************************************************************************
// decodeNTP analyses a byte slice and attempts to decode it as an NTP
// record of a UDP packet.
//
// If it succeeds, it loads p with information about the packet and returns nil.
// If it fails, it returns an error (non nil).
//
// This function is employed in layertypes.go to register the NTP layer.
func decodeNTP(data []byte, p gopacket.PacketBuilder) error {
// Attempt to decode the byte slice.
d := &NTP{}
err := d.DecodeFromBytes(data, p)
if err != nil {
return err
}
// If the decoding worked, add the layer to the packet and set it
// as the application layer too, if there isn't already one.
p.AddLayer(d)
p.SetApplicationLayer(d)
return nil
}
//******************************************************************************
// DecodeFromBytes analyses a byte slice and attempts to decode it as an NTP
// record of a UDP packet.
//
// Upon succeeds, it loads the NTP object with information about the packet
// and returns nil.
// Upon failure, it returns an error (non nil).
func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
// If the data block is too short to be a NTP record, then return an error.
if len(data) < ntpMinimumRecordSizeInBytes {
df.SetTruncated()
return errors.New("NTP packet too short")
}
// RFC 5905 does not appear to define a maximum NTP record length.
// The protocol allows "extension fields" to be included in the record,
// and states about these fields:"
//
// "While the minimum field length containing required fields is
// four words (16 octets), a maximum field length remains to be
// established."
//
// For this reason, the packet length is not checked here for being too long.
// NTP type embeds type BaseLayer which contains two fields:
// Contents is supposed to contain the bytes of the data at this level.
// Payload is supposed to contain the payload of this level.
// Here we set the baselayer to be the bytes of the NTP record.
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
// Extract the fields from the block of bytes.
// To make sense of this, refer to the packet diagram
// above and the section on endian conventions.
// The first few fields are all packed into the first 32 bits. Unpack them.
f := data[0]
d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6)
d.Version = NTPVersion((f & 0x38) >> 3)
d.Mode = NTPMode(f & 0x07)
d.Stratum = NTPStratum(data[1])
d.Poll = NTPLog2Seconds(data[2])
d.Precision = NTPLog2Seconds(data[3])
// The remaining fields can just be copied in big endian order.
d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8]))
d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12]))
d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16]))
d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24]))
d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32]))
d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40]))
d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48]))
// This layer does not attempt to analyse the extension bytes.
// But if there are any, we'd like the user to know. So we just
// place them all in an ExtensionBytes field.
d.ExtensionBytes = data[48:]
// Return no error.
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (d *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes)
if err != nil {
return err
}
// Pack the first few fields into the first 32 bits.
h := uint8(0)
h |= (uint8(d.LeapIndicator) << 6) & 0xC0
h |= (uint8(d.Version) << 3) & 0x38
h |= (uint8(d.Mode)) & 0x07
data[0] = byte(h)
data[1] = byte(d.Stratum)
data[2] = byte(d.Poll)
data[3] = byte(d.Precision)
// The remaining fields can just be copied in big endian order.
binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay))
binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion))
binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID))
binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp))
binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp))
binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp))
binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp))
ex, err := b.AppendBytes(len(d.ExtensionBytes))
if err != nil {
return err
}
copy(ex, d.ExtensionBytes)
return nil
}
//******************************************************************************
// CanDecode returns a set of layers that NTP objects can decode.
// As NTP objects can only decide the NTP layer, we can return just that layer.
// Apparently a single layer type implements LayerClass.
func (d *NTP) CanDecode() gopacket.LayerClass {
return LayerTypeNTP
}
//******************************************************************************
// NextLayerType specifies the next layer that GoPacket should attempt to
// analyse after this (NTP) layer. As NTP packets do not contain any payload
// bytes, there are no further layers to analyse.
func (d *NTP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
//******************************************************************************
// NTP packets do not carry any data payload, so the empty byte slice is retured.
// In Go, a nil slice is functionally identical to an empty slice, so we
// return nil to avoid a heap allocation.
func (d *NTP) Payload() []byte {
return nil
}
//******************************************************************************
//* End Of NTP File *
//******************************************************************************

@ -0,0 +1,76 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
type PFDirection uint8
const (
PFDirectionInOut PFDirection = 0
PFDirectionIn PFDirection = 1
PFDirectionOut PFDirection = 2
)
// PFLog provides the layer for 'pf' packet-filter logging, as described at
// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4
type PFLog struct {
BaseLayer
Length uint8
Family ProtocolFamily
Action, Reason uint8
IFName, Ruleset []byte
RuleNum, SubruleNum uint32
UID uint32
PID int32
RuleUID uint32
RulePID int32
Direction PFDirection
// The remainder is padding
}
func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
pf.Length = data[0]
pf.Family = ProtocolFamily(data[1])
pf.Action = data[2]
pf.Reason = data[3]
pf.IFName = data[4:20]
pf.Ruleset = data[20:36]
pf.RuleNum = binary.BigEndian.Uint32(data[36:40])
pf.SubruleNum = binary.BigEndian.Uint32(data[40:44])
pf.UID = binary.BigEndian.Uint32(data[44:48])
pf.PID = int32(binary.BigEndian.Uint32(data[48:52]))
pf.RuleUID = binary.BigEndian.Uint32(data[52:56])
pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60]))
pf.Direction = PFDirection(data[60])
if pf.Length%4 != 1 {
return errors.New("PFLog header length should be 3 less than multiple of 4")
}
actualLength := int(pf.Length) + 3
pf.Contents = data[:actualLength]
pf.Payload = data[actualLength:]
return nil
}
// LayerType returns layers.LayerTypePFLog
func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog }
func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog }
func (pf *PFLog) NextLayerType() gopacket.LayerType {
return pf.Family.LayerType()
}
func decodePFLog(data []byte, p gopacket.PacketBuilder) error {
pf := &PFLog{}
return decodingLayerDecoder(pf, data, p)
}

@ -0,0 +1,139 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"fmt"
"strconv"
"github.com/google/gopacket"
)
// TCPPort is a port in a TCP layer.
type TCPPort uint16
// UDPPort is a port in a UDP layer.
type UDPPort uint16
// RUDPPort is a port in a RUDP layer.
type RUDPPort uint8
// SCTPPort is a port in a SCTP layer.
type SCTPPort uint16
// UDPLitePort is a port in a UDPLite layer.
type UDPLitePort uint16
// RUDPPortNames contains the string names for all RUDP ports.
var RUDPPortNames = map[RUDPPort]string{}
// UDPLitePortNames contains the string names for all UDPLite ports.
var UDPLitePortNames = map[UDPLitePort]string{}
// {TCP,UDP,SCTP}PortNames can be found in iana_ports.go
// String returns the port as "number(name)" if there's a well-known port name,
// or just "number" if there isn't. Well-known names are stored in
// TCPPortNames.
func (a TCPPort) String() string {
if name, ok := TCPPortNames[a]; ok {
return fmt.Sprintf("%d(%s)", a, name)
}
return strconv.Itoa(int(a))
}
// LayerType returns a LayerType that would be able to decode the
// application payload. It uses some well-known ports such as 53 for
// DNS.
//
// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
func (a TCPPort) LayerType() gopacket.LayerType {
lt := tcpPortLayerType[uint16(a)]
if lt != 0 {
return lt
}
return gopacket.LayerTypePayload
}
var tcpPortLayerType = [65536]gopacket.LayerType{
53: LayerTypeDNS,
}
// RegisterTCPPortLayerType creates a new mapping between a TCPPort
// and an underlaying LayerType.
func RegisterTCPPortLayerType(port TCPPort, layerType gopacket.LayerType) {
tcpPortLayerType[port] = layerType
}
// String returns the port as "number(name)" if there's a well-known port name,
// or just "number" if there isn't. Well-known names are stored in
// UDPPortNames.
func (a UDPPort) String() string {
if name, ok := UDPPortNames[a]; ok {
return fmt.Sprintf("%d(%s)", a, name)
}
return strconv.Itoa(int(a))
}
// LayerType returns a LayerType that would be able to decode the
// application payload. It uses some well-known ports such as 53 for
// DNS.
//
// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
func (a UDPPort) LayerType() gopacket.LayerType {
lt := udpPortLayerType[uint16(a)]
if lt != 0 {
return lt
}
return gopacket.LayerTypePayload
}
var udpPortLayerType = [65536]gopacket.LayerType{
53: LayerTypeDNS,
123: LayerTypeNTP,
4789: LayerTypeVXLAN,
67: LayerTypeDHCPv4,
68: LayerTypeDHCPv4,
6343: LayerTypeSFlow,
6081: LayerTypeGeneve,
}
// RegisterUDPPortLayerType creates a new mapping between a UDPPort
// and an underlaying LayerType.
func RegisterUDPPortLayerType(port UDPPort, layerType gopacket.LayerType) {
udpPortLayerType[port] = layerType
}
// String returns the port as "number(name)" if there's a well-known port name,
// or just "number" if there isn't. Well-known names are stored in
// RUDPPortNames.
func (a RUDPPort) String() string {
if name, ok := RUDPPortNames[a]; ok {
return fmt.Sprintf("%d(%s)", a, name)
}
return strconv.Itoa(int(a))
}
// String returns the port as "number(name)" if there's a well-known port name,
// or just "number" if there isn't. Well-known names are stored in
// SCTPPortNames.
func (a SCTPPort) String() string {
if name, ok := SCTPPortNames[a]; ok {
return fmt.Sprintf("%d(%s)", a, name)
}
return strconv.Itoa(int(a))
}
// String returns the port as "number(name)" if there's a well-known port name,
// or just "number" if there isn't. Well-known names are stored in
// UDPLitePortNames.
func (a UDPLitePort) String() string {
if name, ok := UDPLitePortNames[a]; ok {
return fmt.Sprintf("%d(%s)", a, name)
}
return strconv.Itoa(int(a))
}

@ -0,0 +1,74 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
// PPP is the layer for PPP encapsulation headers.
type PPP struct {
BaseLayer
PPPType PPPType
}
// PPPEndpoint is a singleton endpoint for PPP. Since there is no actual
// addressing for the two ends of a PPP connection, we use a singleton value
// named 'point' for each endpoint.
var PPPEndpoint = gopacket.NewEndpoint(EndpointPPP, nil)
// PPPFlow is a singleton flow for PPP. Since there is no actual addressing for
// the two ends of a PPP connection, we use a singleton value to represent the
// flow for all PPP connections.
var PPPFlow = gopacket.NewFlow(EndpointPPP, nil, nil)
// LayerType returns LayerTypePPP
func (p *PPP) LayerType() gopacket.LayerType { return LayerTypePPP }
// LinkFlow returns PPPFlow.
func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow }
func decodePPP(data []byte, p gopacket.PacketBuilder) error {
ppp := &PPP{}
if data[0]&0x1 == 0 {
if data[1]&0x1 == 0 {
return errors.New("PPP has invalid type")
}
ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[:2]))
ppp.Contents = data[:2]
ppp.Payload = data[2:]
} else {
ppp.PPPType = PPPType(data[0])
ppp.Contents = data[:1]
ppp.Payload = data[1:]
}
p.AddLayer(ppp)
p.SetLinkLayer(ppp)
return p.NextDecoder(ppp.PPPType)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if p.PPPType&0x100 == 0 {
bytes, err := b.PrependBytes(2)
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes, uint16(p.PPPType))
} else {
bytes, err := b.PrependBytes(1)
if err != nil {
return err
}
bytes[0] = uint8(p.PPPType)
}
return nil
}

@ -0,0 +1,60 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// PPPoE is the layer for PPPoE encapsulation headers.
type PPPoE struct {
BaseLayer
Version uint8
Type uint8
Code PPPoECode
SessionId uint16
Length uint16
}
// LayerType returns gopacket.LayerTypePPPoE.
func (p *PPPoE) LayerType() gopacket.LayerType {
return LayerTypePPPoE
}
// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516).
func decodePPPoE(data []byte, p gopacket.PacketBuilder) error {
pppoe := &PPPoE{
Version: data[0] >> 4,
Type: data[0] & 0x0F,
Code: PPPoECode(data[1]),
SessionId: binary.BigEndian.Uint16(data[2:4]),
Length: binary.BigEndian.Uint16(data[4:6]),
}
pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]}
p.AddLayer(pppoe)
return p.NextDecoder(pppoe.Code)
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
payload := b.Bytes()
bytes, err := b.PrependBytes(6)
if err != nil {
return err
}
bytes[0] = (p.Version << 4) | p.Type
bytes[1] = byte(p.Code)
binary.BigEndian.PutUint16(bytes[2:], p.SessionId)
if opts.FixLengths {
p.Length = uint16(len(payload))
}
binary.BigEndian.PutUint16(bytes[4:], p.Length)
return nil
}

@ -0,0 +1,146 @@
// Copyright 2015 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
// http://www.tcpdump.org/linktypes/LINKTYPE_IEEE802_11_PRISM.html
package layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
func decodePrismValue(data []byte, pv *PrismValue) {
pv.DID = PrismDID(binary.LittleEndian.Uint32(data[0:4]))
pv.Status = binary.LittleEndian.Uint16(data[4:6])
pv.Length = binary.LittleEndian.Uint16(data[6:8])
pv.Data = data[8 : 8+pv.Length]
}
type PrismDID uint32
const (
PrismDIDType1HostTime PrismDID = 0x10044
PrismDIDType2HostTime PrismDID = 0x01041
PrismDIDType1MACTime PrismDID = 0x20044
PrismDIDType2MACTime PrismDID = 0x02041
PrismDIDType1Channel PrismDID = 0x30044
PrismDIDType2Channel PrismDID = 0x03041
PrismDIDType1RSSI PrismDID = 0x40044
PrismDIDType2RSSI PrismDID = 0x04041
PrismDIDType1SignalQuality PrismDID = 0x50044
PrismDIDType2SignalQuality PrismDID = 0x05041
PrismDIDType1Signal PrismDID = 0x60044
PrismDIDType2Signal PrismDID = 0x06041
PrismDIDType1Noise PrismDID = 0x70044
PrismDIDType2Noise PrismDID = 0x07041
PrismDIDType1Rate PrismDID = 0x80044
PrismDIDType2Rate PrismDID = 0x08041
PrismDIDType1TransmittedFrameIndicator PrismDID = 0x90044
PrismDIDType2TransmittedFrameIndicator PrismDID = 0x09041
PrismDIDType1FrameLength PrismDID = 0xA0044
PrismDIDType2FrameLength PrismDID = 0x0A041
)
const (
PrismType1MessageCode uint16 = 0x00000044
PrismType2MessageCode uint16 = 0x00000041
)
func (p PrismDID) String() string {
dids := map[PrismDID]string{
PrismDIDType1HostTime: "Host Time",
PrismDIDType2HostTime: "Host Time",
PrismDIDType1MACTime: "MAC Time",
PrismDIDType2MACTime: "MAC Time",
PrismDIDType1Channel: "Channel",
PrismDIDType2Channel: "Channel",
PrismDIDType1RSSI: "RSSI",
PrismDIDType2RSSI: "RSSI",
PrismDIDType1SignalQuality: "Signal Quality",
PrismDIDType2SignalQuality: "Signal Quality",
PrismDIDType1Signal: "Signal",
PrismDIDType2Signal: "Signal",
PrismDIDType1Noise: "Noise",
PrismDIDType2Noise: "Noise",
PrismDIDType1Rate: "Rate",
PrismDIDType2Rate: "Rate",
PrismDIDType1TransmittedFrameIndicator: "Transmitted Frame Indicator",
PrismDIDType2TransmittedFrameIndicator: "Transmitted Frame Indicator",
PrismDIDType1FrameLength: "Frame Length",
PrismDIDType2FrameLength: "Frame Length",
}
if str, ok := dids[p]; ok {
return str
}
return "Unknown DID"
}
type PrismValue struct {
DID PrismDID
Status uint16
Length uint16
Data []byte
}
func (pv *PrismValue) IsSupplied() bool {
return pv.Status == 1
}
var ErrPrismExpectedMoreData = errors.New("Expected more data.")
var ErrPrismInvalidCode = errors.New("Invalid header code.")
func decodePrismHeader(data []byte, p gopacket.PacketBuilder) error {
d := &PrismHeader{}
return decodingLayerDecoder(d, data, p)
}
type PrismHeader struct {
BaseLayer
Code uint16
Length uint16
DeviceName string
Values []PrismValue
}
func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader }
func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.Code = binary.LittleEndian.Uint16(data[0:4])
m.Length = binary.LittleEndian.Uint16(data[4:8])
m.DeviceName = string(data[8:24])
m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]}
switch m.Code {
case PrismType1MessageCode:
fallthrough
case PrismType2MessageCode:
// valid message code
default:
return ErrPrismInvalidCode
}
offset := uint16(24)
m.Values = make([]PrismValue, (m.Length-offset)/12)
for i := 0; i < len(m.Values); i++ {
decodePrismValue(data[offset:offset+12], &m.Values[i])
offset += 12
}
if offset != m.Length {
return ErrPrismExpectedMoreData
}
return nil
}
func (m *PrismHeader) CanDecode() gopacket.LayerClass { return LayerTypePrismHeader }
func (m *PrismHeader) NextLayerType() gopacket.LayerType { return LayerTypeDot11 }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,93 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
type RUDP struct {
BaseLayer
SYN, ACK, EACK, RST, NUL bool
Version uint8
HeaderLength uint8
SrcPort, DstPort RUDPPort
DataLength uint16
Seq, Ack, Checksum uint32
VariableHeaderArea []byte
// RUDPHeaderSyn contains SYN information for the RUDP packet,
// if the SYN flag is set
*RUDPHeaderSYN
// RUDPHeaderEack contains EACK information for the RUDP packet,
// if the EACK flag is set.
*RUDPHeaderEACK
}
type RUDPHeaderSYN struct {
MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16
}
type RUDPHeaderEACK struct {
SeqsReceivedOK []uint32
}
// LayerType returns gopacket.LayerTypeRUDP.
func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP }
func decodeRUDP(data []byte, p gopacket.PacketBuilder) error {
r := &RUDP{
SYN: data[0]&0x80 != 0,
ACK: data[0]&0x40 != 0,
EACK: data[0]&0x20 != 0,
RST: data[0]&0x10 != 0,
NUL: data[0]&0x08 != 0,
Version: data[0] & 0x3,
HeaderLength: data[1],
SrcPort: RUDPPort(data[2]),
DstPort: RUDPPort(data[3]),
DataLength: binary.BigEndian.Uint16(data[4:6]),
Seq: binary.BigEndian.Uint32(data[6:10]),
Ack: binary.BigEndian.Uint32(data[10:14]),
Checksum: binary.BigEndian.Uint32(data[14:18]),
}
if r.HeaderLength < 9 {
return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength)
}
hlen := int(r.HeaderLength) * 2
r.Contents = data[:hlen]
r.Payload = data[hlen : hlen+int(r.DataLength)]
r.VariableHeaderArea = data[18:hlen]
headerData := r.VariableHeaderArea
switch {
case r.SYN:
if len(headerData) != 6 {
return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData))
}
r.RUDPHeaderSYN = &RUDPHeaderSYN{
MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]),
MaxSegmentSize: binary.BigEndian.Uint16(headerData[2:4]),
OptionFlags: binary.BigEndian.Uint16(headerData[4:6]),
}
case r.EACK:
if len(headerData)%4 != 0 {
return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData))
}
r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)}
for i := 0; i < len(headerData); i += 4 {
r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4])
}
}
p.AddLayer(r)
p.SetTransportLayer(r)
return p.NextDecoder(gopacket.LayerTypePayload)
}
func (r *RUDP) TransportFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)})
}

@ -0,0 +1,746 @@
// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"github.com/google/gopacket"
)
// SCTP contains information on the top level of an SCTP packet.
type SCTP struct {
BaseLayer
SrcPort, DstPort SCTPPort
VerificationTag uint32
Checksum uint32
sPort, dPort []byte
}
// LayerType returns gopacket.LayerTypeSCTP
func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP }
func decodeSCTP(data []byte, p gopacket.PacketBuilder) error {
sctp := &SCTP{}
err := sctp.DecodeFromBytes(data, p)
p.AddLayer(sctp)
p.SetTransportLayer(sctp)
if err != nil {
return err
}
return p.NextDecoder(sctpChunkTypePrefixDecoder)
}
var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)
// TransportFlow returns a flow based on the source and destination SCTP port.
func (s *SCTP) TransportFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort)
}
func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error {
chunkType := SCTPChunkType(data[0])
return chunkType.Decode(data, p)
}
// SerializeTo is for gopacket.SerializableLayer.
func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(12)
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort))
binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort))
binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag)
if opts.ComputeChecksums {
// Note: MakeTable(Castagnoli) actually only creates the table once, then
// passes back a singleton on every other call, so this shouldn't cause
// excessive memory allocation.
binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli)))
}
return nil
}
func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 12 {
return errors.New("Invalid SCTP common header length")
}
sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2]))
sctp.sPort = data[:2]
sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4]))
sctp.dPort = data[2:4]
sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8])
sctp.Checksum = binary.BigEndian.Uint32(data[8:12])
sctp.BaseLayer = BaseLayer{data[:12], data[12:]}
return nil
}
func (t *SCTP) CanDecode() gopacket.LayerClass {
return LayerTypeSCTP
}
func (t *SCTP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// SCTPChunk contains the common fields in all SCTP chunks.
type SCTPChunk struct {
BaseLayer
Type SCTPChunkType
Flags uint8
Length uint16
// ActualLength is the total length of an SCTP chunk, including padding.
// SCTP chunks start and end on 4-byte boundaries. So if a chunk has a length
// of 18, it means that it has data up to and including byte 18, then padding
// up to the next 4-byte boundary, 20. In this case, Length would be 18, and
// ActualLength would be 20.
ActualLength int
}
func roundUpToNearest4(i int) int {
if i%4 == 0 {
return i
}
return i + 4 - (i % 4)
}
func decodeSCTPChunk(data []byte) (SCTPChunk, error) {
length := binary.BigEndian.Uint16(data[2:4])
if length < 4 {
return SCTPChunk{}, errors.New("invalid SCTP chunk length")
}
actual := roundUpToNearest4(int(length))
ct := SCTPChunkType(data[0])
// For SCTP Data, use a separate layer for the payload
delta := 0
if ct == SCTPChunkTypeData {
delta = int(actual) - int(length)
actual = 16
}
return SCTPChunk{
Type: ct,
Flags: data[1],
Length: length,
ActualLength: actual,
BaseLayer: BaseLayer{data[:actual], data[actual : len(data)-delta]},
}, nil
}
// SCTPParameter is a TLV parameter inside a SCTPChunk.
type SCTPParameter struct {
Type uint16
Length uint16
ActualLength int
Value []byte
}
func decodeSCTPParameter(data []byte) SCTPParameter {
length := binary.BigEndian.Uint16(data[2:4])
return SCTPParameter{
Type: binary.BigEndian.Uint16(data[0:2]),
Length: length,
Value: data[4:length],
ActualLength: roundUpToNearest4(int(length)),
}
}
func (p SCTPParameter) Bytes() []byte {
length := 4 + len(p.Value)
data := make([]byte, roundUpToNearest4(length))
binary.BigEndian.PutUint16(data[0:2], p.Type)
binary.BigEndian.PutUint16(data[2:4], uint16(length))
copy(data[4:], p.Value)
return data
}
// SCTPUnknownChunkType is the layer type returned when we don't recognize the
// chunk type. Since there's a length in a known location, we can skip over
// it even if we don't know what it is, and continue parsing the rest of the
// chunks. This chunk is stored as an ErrorLayer in the packet.
type SCTPUnknownChunkType struct {
SCTPChunk
bytes []byte
}
func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPUnknownChunkType{SCTPChunk: chunk}
sc.bytes = data[:sc.ActualLength]
p.AddLayer(sc)
p.SetErrorLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(s.ActualLength)
if err != nil {
return err
}
copy(bytes, s.bytes)
return nil
}
// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType.
func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType }
// Payload returns all bytes in this header, including the decoded Type, Length,
// and Flags.
func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes }
// Error implements ErrorLayer.
func (s *SCTPUnknownChunkType) Error() error {
return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type)
}
// SCTPData is the SCTP Data chunk layer.
type SCTPData struct {
SCTPChunk
Unordered, BeginFragment, EndFragment bool
TSN uint32
StreamId uint16
StreamSequence uint16
PayloadProtocol SCTPPayloadProtocol
}
// LayerType returns gopacket.LayerTypeSCTPData.
func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData }
// SCTPPayloadProtocol represents a payload protocol
type SCTPPayloadProtocol uint32
// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml
const (
SCTPProtocolReserved SCTPPayloadProtocol = 0
SCTPPayloadUIA = 1
SCTPPayloadM2UA = 2
SCTPPayloadM3UA = 3
SCTPPayloadSUA = 4
SCTPPayloadM2PA = 5
SCTPPayloadV5UA = 6
SCTPPayloadH248 = 7
SCTPPayloadBICC = 8
SCTPPayloadTALI = 9
SCTPPayloadDUA = 10
SCTPPayloadASAP = 11
SCTPPayloadENRP = 12
SCTPPayloadH323 = 13
SCTPPayloadQIPC = 14
SCTPPayloadSIMCO = 15
SCTPPayloadDDPSegment = 16
SCTPPayloadDDPStream = 17
SCTPPayloadS1AP = 18
)
func (p SCTPPayloadProtocol) String() string {
switch p {
case SCTPProtocolReserved:
return "Reserved"
case SCTPPayloadUIA:
return "UIA"
case SCTPPayloadM2UA:
return "M2UA"
case SCTPPayloadM3UA:
return "M3UA"
case SCTPPayloadSUA:
return "SUA"
case SCTPPayloadM2PA:
return "M2PA"
case SCTPPayloadV5UA:
return "V5UA"
case SCTPPayloadH248:
return "H.248"
case SCTPPayloadBICC:
return "BICC"
case SCTPPayloadTALI:
return "TALI"
case SCTPPayloadDUA:
return "DUA"
case SCTPPayloadASAP:
return "ASAP"
case SCTPPayloadENRP:
return "ENRP"
case SCTPPayloadH323:
return "H.323"
case SCTPPayloadQIPC:
return "QIPC"
case SCTPPayloadSIMCO:
return "SIMCO"
case SCTPPayloadDDPSegment:
return "DDPSegment"
case SCTPPayloadDDPStream:
return "DDPStream"
case SCTPPayloadS1AP:
return "S1AP"
}
return fmt.Sprintf("Unknown(%d)", p)
}
func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPData{
SCTPChunk: chunk,
Unordered: data[1]&0x4 != 0,
BeginFragment: data[1]&0x2 != 0,
EndFragment: data[1]&0x1 != 0,
TSN: binary.BigEndian.Uint32(data[4:8]),
StreamId: binary.BigEndian.Uint16(data[8:10]),
StreamSequence: binary.BigEndian.Uint16(data[10:12]),
PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])),
}
// Length is the length in bytes of the data, INCLUDING the 16-byte header.
p.AddLayer(sc)
return p.NextDecoder(gopacket.LayerTypePayload)
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
payload := b.Bytes()
// Pad the payload to a 32 bit boundary
if rem := len(payload) % 4; rem != 0 {
b.AppendBytes(4 - rem)
}
length := 16
bytes, err := b.PrependBytes(length)
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
flags := uint8(0)
if sc.Unordered {
flags |= 0x4
}
if sc.BeginFragment {
flags |= 0x2
}
if sc.EndFragment {
flags |= 0x1
}
bytes[1] = flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload)))
binary.BigEndian.PutUint32(bytes[4:8], sc.TSN)
binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId)
binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence)
binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol))
return nil
}
// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet.
type SCTPInitParameter SCTPParameter
// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck
// messages.
type SCTPInit struct {
SCTPChunk
InitiateTag uint32
AdvertisedReceiverWindowCredit uint32
OutboundStreams, InboundStreams uint16
InitialTSN uint32
Parameters []SCTPInitParameter
}
// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck.
func (sc *SCTPInit) LayerType() gopacket.LayerType {
if sc.Type == SCTPChunkTypeInitAck {
return LayerTypeSCTPInitAck
}
// sc.Type == SCTPChunkTypeInit
return LayerTypeSCTPInit
}
func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPInit{
SCTPChunk: chunk,
InitiateTag: binary.BigEndian.Uint32(data[4:8]),
AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
OutboundStreams: binary.BigEndian.Uint16(data[12:14]),
InboundStreams: binary.BigEndian.Uint16(data[14:16]),
InitialTSN: binary.BigEndian.Uint32(data[16:20]),
}
paramData := data[20:sc.ActualLength]
for len(paramData) > 0 {
p := SCTPInitParameter(decodeSCTPParameter(paramData))
paramData = paramData[p.ActualLength:]
sc.Parameters = append(sc.Parameters, p)
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var payload []byte
for _, param := range sc.Parameters {
payload = append(payload, SCTPParameter(param).Bytes()...)
}
length := 20 + len(payload)
bytes, err := b.PrependBytes(roundUpToNearest4(length))
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag)
binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams)
binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams)
binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN)
copy(bytes[20:], payload)
return nil
}
// SCTPSack is the SCTP Selective ACK chunk layer.
type SCTPSack struct {
SCTPChunk
CumulativeTSNAck uint32
AdvertisedReceiverWindowCredit uint32
NumGapACKs, NumDuplicateTSNs uint16
GapACKs []uint16
DuplicateTSNs []uint32
}
// LayerType return LayerTypeSCTPSack
func (sc *SCTPSack) LayerType() gopacket.LayerType {
return LayerTypeSCTPSack
}
func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPSack{
SCTPChunk: chunk,
CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
NumGapACKs: binary.BigEndian.Uint16(data[12:14]),
NumDuplicateTSNs: binary.BigEndian.Uint16(data[14:16]),
}
// We maximize gapAcks and dupTSNs here so we're not allocating tons
// of memory based on a user-controlable field. Our maximums are not exact,
// but should give us sane defaults... we'll still hit slice boundaries and
// fail if the user-supplied values are too high (in the for loops below), but
// the amount of memory we'll have allocated because of that should be small
// (< sc.ActualLength)
gapAcks := sc.SCTPChunk.ActualLength / 2
dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4
if gapAcks > int(sc.NumGapACKs) {
gapAcks = int(sc.NumGapACKs)
}
if dupTSNs > int(sc.NumDuplicateTSNs) {
dupTSNs = int(sc.NumDuplicateTSNs)
}
sc.GapACKs = make([]uint16, 0, gapAcks)
sc.DuplicateTSNs = make([]uint32, 0, dupTSNs)
bytesRemaining := data[16:]
for i := 0; i < int(sc.NumGapACKs); i++ {
sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))
bytesRemaining = bytesRemaining[2:]
}
for i := 0; i < int(sc.NumDuplicateTSNs); i++ {
sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))
bytesRemaining = bytesRemaining[4:]
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs)
bytes, err := b.PrependBytes(roundUpToNearest4(length))
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs)))
binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs)))
for i, v := range sc.GapACKs {
binary.BigEndian.PutUint16(bytes[16+i*2:], v)
}
offset := 16 + 2*len(sc.GapACKs)
for i, v := range sc.DuplicateTSNs {
binary.BigEndian.PutUint32(bytes[offset+i*4:], v)
}
return nil
}
// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and
// heartbeat ack layers.
type SCTPHeartbeatParameter SCTPParameter
// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack.
type SCTPHeartbeat struct {
SCTPChunk
Parameters []SCTPHeartbeatParameter
}
// LayerType returns gopacket.LayerTypeSCTPHeartbeat.
func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType {
if sc.Type == SCTPChunkTypeHeartbeatAck {
return LayerTypeSCTPHeartbeatAck
}
// sc.Type == SCTPChunkTypeHeartbeat
return LayerTypeSCTPHeartbeat
}
func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPHeartbeat{
SCTPChunk: chunk,
}
paramData := data[4:sc.Length]
for len(paramData) > 0 {
p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData))
paramData = paramData[p.ActualLength:]
sc.Parameters = append(sc.Parameters, p)
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var payload []byte
for _, param := range sc.Parameters {
payload = append(payload, SCTPParameter(param).Bytes()...)
}
length := 4 + len(payload)
bytes, err := b.PrependBytes(roundUpToNearest4(length))
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
copy(bytes[4:], payload)
return nil
}
// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers.
type SCTPErrorParameter SCTPParameter
// SCTPError is the SCTP error layer, also used for SCTP aborts.
type SCTPError struct {
SCTPChunk
Parameters []SCTPErrorParameter
}
// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError.
func (sc *SCTPError) LayerType() gopacket.LayerType {
if sc.Type == SCTPChunkTypeAbort {
return LayerTypeSCTPAbort
}
// sc.Type == SCTPChunkTypeError
return LayerTypeSCTPError
}
func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error {
// remarkably similar to decodeSCTPHeartbeat ;)
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPError{
SCTPChunk: chunk,
}
paramData := data[4:sc.Length]
for len(paramData) > 0 {
p := SCTPErrorParameter(decodeSCTPParameter(paramData))
paramData = paramData[p.ActualLength:]
sc.Parameters = append(sc.Parameters, p)
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var payload []byte
for _, param := range sc.Parameters {
payload = append(payload, SCTPParameter(param).Bytes()...)
}
length := 4 + len(payload)
bytes, err := b.PrependBytes(roundUpToNearest4(length))
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
copy(bytes[4:], payload)
return nil
}
// SCTPShutdown is the SCTP shutdown layer.
type SCTPShutdown struct {
SCTPChunk
CumulativeTSNAck uint32
}
// LayerType returns gopacket.LayerTypeSCTPShutdown.
func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown }
func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPShutdown{
SCTPChunk: chunk,
CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], 8)
binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
return nil
}
// SCTPShutdownAck is the SCTP shutdown layer.
type SCTPShutdownAck struct {
SCTPChunk
}
// LayerType returns gopacket.LayerTypeSCTPShutdownAck.
func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck }
func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPShutdownAck{
SCTPChunk: chunk,
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], 4)
return nil
}
// SCTPCookieEcho is the SCTP Cookie Echo layer.
type SCTPCookieEcho struct {
SCTPChunk
Cookie []byte
}
// LayerType returns gopacket.LayerTypeSCTPCookieEcho.
func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho }
func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPCookieEcho{
SCTPChunk: chunk,
}
sc.Cookie = data[4:sc.Length]
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
length := 4 + len(sc.Cookie)
bytes, err := b.PrependBytes(roundUpToNearest4(length))
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
copy(bytes[4:], sc.Cookie)
return nil
}
// This struct is used by all empty SCTP chunks (currently CookieAck and
// ShutdownComplete).
type SCTPEmptyLayer struct {
SCTPChunk
}
// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or
// LayerTypeSCTPCookieAck.
func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType {
if sc.Type == SCTPChunkTypeShutdownComplete {
return LayerTypeSCTPShutdownComplete
}
// sc.Type == SCTPChunkTypeCookieAck
return LayerTypeSCTPCookieAck
}
func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error {
chunk, err := decodeSCTPChunk(data)
if err != nil {
return err
}
sc := &SCTPEmptyLayer{
SCTPChunk: chunk,
}
p.AddLayer(sc)
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
}
// SerializeTo is for gopacket.SerializableLayer.
func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
bytes[0] = uint8(sc.Type)
bytes[1] = sc.Flags
binary.BigEndian.PutUint16(bytes[2:4], 4)
return nil
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,327 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"github.com/google/gopacket"
)
// TCP is the layer for TCP headers.
type TCP struct {
BaseLayer
SrcPort, DstPort TCPPort
Seq uint32
Ack uint32
DataOffset uint8
FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
Window uint16
Checksum uint16
Urgent uint16
sPort, dPort []byte
Options []TCPOption
Padding []byte
opts [4]TCPOption
tcpipchecksum
}
// TCPOptionKind represents a TCP option code.
type TCPOptionKind uint8
const (
TCPOptionKindEndList = 0
TCPOptionKindNop = 1
TCPOptionKindMSS = 2 // len = 4
TCPOptionKindWindowScale = 3 // len = 3
TCPOptionKindSACKPermitted = 4 // len = 2
TCPOptionKindSACK = 5 // len = n
TCPOptionKindEcho = 6 // len = 6, obsolete
TCPOptionKindEchoReply = 7 // len = 6, obsolete
TCPOptionKindTimestamps = 8 // len = 10
TCPOptionKindPartialOrderConnectionPermitted = 9 // len = 2, obsolete
TCPOptionKindPartialOrderServiceProfile = 10 // len = 3, obsolete
TCPOptionKindCC = 11 // obsolete
TCPOptionKindCCNew = 12 // obsolete
TCPOptionKindCCEcho = 13 // obsolete
TCPOptionKindAltChecksum = 14 // len = 3, obsolete
TCPOptionKindAltChecksumData = 15 // len = n, obsolete
)
func (k TCPOptionKind) String() string {
switch k {
case TCPOptionKindEndList:
return "EndList"
case TCPOptionKindNop:
return "NOP"
case TCPOptionKindMSS:
return "MSS"
case TCPOptionKindWindowScale:
return "WindowScale"
case TCPOptionKindSACKPermitted:
return "SACKPermitted"
case TCPOptionKindSACK:
return "SACK"
case TCPOptionKindEcho:
return "Echo"
case TCPOptionKindEchoReply:
return "EchoReply"
case TCPOptionKindTimestamps:
return "Timestamps"
case TCPOptionKindPartialOrderConnectionPermitted:
return "PartialOrderConnectionPermitted"
case TCPOptionKindPartialOrderServiceProfile:
return "PartialOrderServiceProfile"
case TCPOptionKindCC:
return "CC"
case TCPOptionKindCCNew:
return "CCNew"
case TCPOptionKindCCEcho:
return "CCEcho"
case TCPOptionKindAltChecksum:
return "AltChecksum"
case TCPOptionKindAltChecksumData:
return "AltChecksumData"
default:
return fmt.Sprintf("Unknown(%d)", k)
}
}
type TCPOption struct {
OptionType TCPOptionKind
OptionLength uint8
OptionData []byte
}
func (t TCPOption) String() string {
hd := hex.EncodeToString(t.OptionData)
if len(hd) > 0 {
hd = " 0x" + hd
}
switch t.OptionType {
case TCPOptionKindMSS:
return fmt.Sprintf("TCPOption(%s:%v%s)",
t.OptionType,
binary.BigEndian.Uint16(t.OptionData),
hd)
case TCPOptionKindTimestamps:
if len(t.OptionData) == 8 {
return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
t.OptionType,
binary.BigEndian.Uint32(t.OptionData[:4]),
binary.BigEndian.Uint32(t.OptionData[4:8]),
hd)
}
}
return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
}
// LayerType returns gopacket.LayerTypeTCP
func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var optionLength int
for _, o := range t.Options {
switch o.OptionType {
case 0, 1:
optionLength += 1
default:
optionLength += 2 + len(o.OptionData)
}
}
if opts.FixLengths {
if rem := optionLength % 4; rem != 0 {
t.Padding = lotsOfZeros[:4-rem]
}
t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
}
bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort))
binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort))
binary.BigEndian.PutUint32(bytes[4:], t.Seq)
binary.BigEndian.PutUint32(bytes[8:], t.Ack)
binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset())
binary.BigEndian.PutUint16(bytes[14:], t.Window)
binary.BigEndian.PutUint16(bytes[18:], t.Urgent)
start := 20
for _, o := range t.Options {
bytes[start] = byte(o.OptionType)
switch o.OptionType {
case 0, 1:
start++
default:
if opts.FixLengths {
o.OptionLength = uint8(len(o.OptionData) + 2)
}
bytes[start+1] = o.OptionLength
copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
start += int(o.OptionLength)
}
}
copy(bytes[start:], t.Padding)
if opts.ComputeChecksums {
// zero out checksum bytes in current serialization.
bytes[16] = 0
bytes[17] = 0
csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
if err != nil {
return err
}
t.Checksum = csum
}
binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
return nil
}
func (t *TCP) ComputeChecksum() (uint16, error) {
return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
}
func (t *TCP) flagsAndOffset() uint16 {
f := uint16(t.DataOffset) << 12
if t.FIN {
f |= 0x0001
}
if t.SYN {
f |= 0x0002
}
if t.RST {
f |= 0x0004
}
if t.PSH {
f |= 0x0008
}
if t.ACK {
f |= 0x0010
}
if t.URG {
f |= 0x0020
}
if t.ECE {
f |= 0x0040
}
if t.CWR {
f |= 0x0080
}
if t.NS {
f |= 0x0100
}
return f
}
func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
tcp.sPort = data[0:2]
tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
tcp.dPort = data[2:4]
tcp.Seq = binary.BigEndian.Uint32(data[4:8])
tcp.Ack = binary.BigEndian.Uint32(data[8:12])
tcp.DataOffset = data[12] >> 4
tcp.FIN = data[13]&0x01 != 0
tcp.SYN = data[13]&0x02 != 0
tcp.RST = data[13]&0x04 != 0
tcp.PSH = data[13]&0x08 != 0
tcp.ACK = data[13]&0x10 != 0
tcp.URG = data[13]&0x20 != 0
tcp.ECE = data[13]&0x40 != 0
tcp.CWR = data[13]&0x80 != 0
tcp.NS = data[12]&0x01 != 0
tcp.Window = binary.BigEndian.Uint16(data[14:16])
tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
tcp.Options = tcp.opts[:0]
if tcp.DataOffset < 5 {
return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
}
dataStart := int(tcp.DataOffset) * 4
if dataStart > len(data) {
df.SetTruncated()
tcp.Payload = nil
tcp.Contents = data
return errors.New("TCP data offset greater than packet length")
}
tcp.Contents = data[:dataStart]
tcp.Payload = data[dataStart:]
// From here on, data points just to the header options.
data = data[20:dataStart]
for len(data) > 0 {
if tcp.Options == nil {
// Pre-allocate to avoid allocating a slice.
tcp.Options = tcp.opts[:0]
}
tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
opt := &tcp.Options[len(tcp.Options)-1]
switch opt.OptionType {
case TCPOptionKindEndList: // End of options
opt.OptionLength = 1
tcp.Padding = data[1:]
break
case TCPOptionKindNop: // 1 byte padding
opt.OptionLength = 1
default:
opt.OptionLength = data[1]
if opt.OptionLength < 2 {
return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
} else if int(opt.OptionLength) > len(data) {
return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
}
opt.OptionData = data[2:opt.OptionLength]
}
data = data[opt.OptionLength:]
}
return nil
}
func (t *TCP) CanDecode() gopacket.LayerClass {
return LayerTypeTCP
}
func (t *TCP) NextLayerType() gopacket.LayerType {
lt := t.DstPort.LayerType()
if lt == gopacket.LayerTypePayload {
lt = t.SrcPort.LayerType()
}
return lt
}
func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
tcp := &TCP{}
err := tcp.DecodeFromBytes(data, p)
p.AddLayer(tcp)
p.SetTransportLayer(tcp)
if err != nil {
return err
}
if p.DecodeOptions().DecodeStreamsAsDatagrams {
return p.NextDecoder(tcp.NextLayerType())
} else {
return p.NextDecoder(gopacket.LayerTypePayload)
}
}
func (t *TCP) TransportFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
}
// For testing only
func (t *TCP) SetInternalPortsForTesting() {
t.sPort = make([]byte, 2)
t.dPort = make([]byte, 2)
binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))
}

@ -0,0 +1,104 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"errors"
"fmt"
"github.com/google/gopacket"
)
// Checksum computation for TCP/UDP.
type tcpipchecksum struct {
pseudoheader tcpipPseudoHeader
}
type tcpipPseudoHeader interface {
pseudoheaderChecksum() (uint32, error)
}
func (ip *IPv4) pseudoheaderChecksum() (csum uint32, err error) {
if err := ip.AddressTo4(); err != nil {
return 0, err
}
csum += (uint32(ip.SrcIP[0]) + uint32(ip.SrcIP[2])) << 8
csum += uint32(ip.SrcIP[1]) + uint32(ip.SrcIP[3])
csum += (uint32(ip.DstIP[0]) + uint32(ip.DstIP[2])) << 8
csum += uint32(ip.DstIP[1]) + uint32(ip.DstIP[3])
return csum, nil
}
func (ip *IPv6) pseudoheaderChecksum() (csum uint32, err error) {
if err := ip.AddressTo16(); err != nil {
return 0, err
}
for i := 0; i < 16; i += 2 {
csum += uint32(ip.SrcIP[i]) << 8
csum += uint32(ip.SrcIP[i+1])
csum += uint32(ip.DstIP[i]) << 8
csum += uint32(ip.DstIP[i+1])
}
return csum, nil
}
// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
// initial checksum data that's already been computed.
func tcpipChecksum(data []byte, csum uint32) uint16 {
// to handle odd lengths, we loop to length - 1, incrementing by 2, then
// handle the last byte specifically by checking against the original
// length.
length := len(data) - 1
for i := 0; i < length; i += 2 {
// For our test packet, doing this manually is about 25% faster
// (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
csum += uint32(data[i]) << 8
csum += uint32(data[i+1])
}
if len(data)%2 == 1 {
csum += uint32(data[length]) << 8
}
for csum > 0xffff {
csum = (csum >> 16) + (csum & 0xffff)
}
return ^uint16(csum)
}
// computeChecksum computes a TCP or UDP checksum. headerAndPayload is the
// serialized TCP or UDP header plus its payload, with the checksum zero'd
// out. headerProtocol is the IP protocol number of the upper-layer header.
func (c *tcpipchecksum) computeChecksum(headerAndPayload []byte, headerProtocol IPProtocol) (uint16, error) {
if c.pseudoheader == nil {
return 0, errors.New("TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use")
}
length := uint32(len(headerAndPayload))
csum, err := c.pseudoheader.pseudoheaderChecksum()
if err != nil {
return 0, err
}
csum += uint32(headerProtocol)
csum += length & 0xffff
csum += length >> 16
return tcpipChecksum(headerAndPayload, csum), nil
}
// SetNetworkLayerForChecksum tells this layer which network layer is wrapping it.
// This is needed for computing the checksum when serializing, since TCP/IP transport
// layer checksums depends on fields in the IPv4 or IPv6 layer that contains it.
// The passed in layer must be an *IPv4 or *IPv6.
func (i *tcpipchecksum) SetNetworkLayerForChecksum(l gopacket.NetworkLayer) error {
switch v := l.(type) {
case *IPv4:
i.pseudoheader = v
case *IPv6:
i.pseudoheader = v
default:
return fmt.Errorf("cannot use layer type %v for tcp checksum network layer", l.LayerType())
}
return nil
}

@ -0,0 +1,103 @@
#!/usr/bin/python
# Copyright 2012 Google, Inc. All rights reserved.
"""TestCreator creates test templates from pcap files."""
import argparse
import base64
import glob
import re
import string
import subprocess
import sys
class Packet(object):
"""Helper class encapsulating packet from a pcap file."""
def __init__(self, packet_lines):
self.packet_lines = packet_lines
self.data = self._DecodeText(packet_lines)
@classmethod
def _DecodeText(cls, packet_lines):
packet_bytes = []
# First line is timestamp and stuff, skip it.
# Format: 0x0010: 0000 0020 3aff 3ffe 0000 0000 0000 0000 ....:.?.........
for line in packet_lines[1:]:
m = re.match(r'\s+0x[a-f\d]+:\s+((?:[\da-f]{2,4}\s)*)', line, re.IGNORECASE)
if m is None: continue
for hexpart in m.group(1).split():
packet_bytes.append(base64.b16decode(hexpart.upper()))
return ''.join(packet_bytes)
def Test(self, name, link_type):
"""Yields a test using this packet, as a set of lines."""
yield '// testPacket%s is the packet:' % name
for line in self.packet_lines:
yield '// ' + line
yield 'var testPacket%s = []byte{' % name
data = list(self.data)
while data:
linebytes, data = data[:16], data[16:]
yield ''.join(['\t'] + ['0x%02x, ' % ord(c) for c in linebytes])
yield '}'
yield 'func TestPacket%s(t *testing.T) {' % name
yield '\tp := gopacket.NewPacket(testPacket%s, LinkType%s, gopacket.Default)' % (name, link_type)
yield '\tif p.ErrorLayer() != nil {'
yield '\t\tt.Error("Failed to decode packet:", p.ErrorLayer().Error())'
yield '\t}'
yield '\tcheckLayers(p, []gopacket.LayerType{LayerType%s, FILL_ME_IN_WITH_ACTUAL_LAYERS}, t)' % link_type
yield '}'
yield 'func BenchmarkDecodePacket%s(b *testing.B) {' % name
yield '\tfor i := 0; i < b.N; i++ {'
yield '\t\tgopacket.NewPacket(testPacket%s, LinkType%s, gopacket.NoCopy)' % (name, link_type)
yield '\t}'
yield '}'
def GetTcpdumpOutput(filename):
"""Runs tcpdump on the given file, returning output as string."""
return subprocess.check_output(
['tcpdump', '-XX', '-s', '0', '-n', '-r', filename])
def TcpdumpOutputToPackets(output):
"""Reads a pcap file with TCPDump, yielding Packet objects."""
pdata = []
for line in output.splitlines():
if line[0] not in string.whitespace and pdata:
yield Packet(pdata)
pdata = []
pdata.append(line)
if pdata:
yield Packet(pdata)
def main():
class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
def _format_usage(self, usage, actions, groups, prefix=None):
header =('TestCreator creates gopacket tests using a pcap file.\n\n'
'Tests are written to standard out... they can then be \n'
'copied into the file of your choice and modified as \n'
'you see.\n\n')
return header + argparse.ArgumentDefaultsHelpFormatter._format_usage(
self, usage, actions, groups, prefix)
parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter)
parser.add_argument('--link_type', default='Ethernet', help='the link type (default: %(default)s)')
parser.add_argument('--name', default='Packet%d', help='the layer type, must have "%d" inside it')
parser.add_argument('files', metavar='file.pcap', type=str, nargs='+', help='the files to process')
args = parser.parse_args()
for arg in args.files:
for path in glob.glob(arg):
for i, packet in enumerate(TcpdumpOutputToPackets(GetTcpdumpOutput(path))):
print '\n'.join(packet.Test(
args.name % i, args.link_type))
if __name__ == '__main__':
main()

@ -0,0 +1,120 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
// UDP is the layer for UDP headers.
type UDP struct {
BaseLayer
SrcPort, DstPort UDPPort
Length uint16
Checksum uint16
sPort, dPort []byte
tcpipchecksum
}
// LayerType returns gopacket.LayerTypeUDP
func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP }
func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2]))
udp.sPort = data[0:2]
udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4]))
udp.dPort = data[2:4]
udp.Length = binary.BigEndian.Uint16(data[4:6])
udp.Checksum = binary.BigEndian.Uint16(data[6:8])
udp.BaseLayer = BaseLayer{Contents: data[:8]}
switch {
case udp.Length >= 8:
hlen := int(udp.Length)
if hlen > len(data) {
df.SetTruncated()
hlen = len(data)
}
udp.Payload = data[8:hlen]
case udp.Length == 0: // Jumbogram, use entire rest of data
udp.Payload = data[8:]
default:
return fmt.Errorf("UDP packet too small: %d bytes", udp.Length)
}
return nil
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (u *UDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var jumbo bool
payload := b.Bytes()
if _, ok := u.pseudoheader.(*IPv6); ok {
if len(payload)+8 > 65535 {
jumbo = true
}
}
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
binary.BigEndian.PutUint16(bytes, uint16(u.SrcPort))
binary.BigEndian.PutUint16(bytes[2:], uint16(u.DstPort))
if opts.FixLengths {
if jumbo {
u.Length = 0
} else {
u.Length = uint16(len(payload)) + 8
}
}
binary.BigEndian.PutUint16(bytes[4:], u.Length)
if opts.ComputeChecksums {
// zero out checksum bytes
bytes[6] = 0
bytes[7] = 0
csum, err := u.computeChecksum(b.Bytes(), IPProtocolUDP)
if err != nil {
return err
}
u.Checksum = csum
}
binary.BigEndian.PutUint16(bytes[6:], u.Checksum)
return nil
}
func (u *UDP) CanDecode() gopacket.LayerClass {
return LayerTypeUDP
}
// NextLayerType use the destination port to select the
// right next decoder. It tries first to decode via the
// destination port, then the source port.
func (u *UDP) NextLayerType() gopacket.LayerType {
if lt := u.DstPort.LayerType(); lt != gopacket.LayerTypePayload {
return lt
}
return u.SrcPort.LayerType()
}
func decodeUDP(data []byte, p gopacket.PacketBuilder) error {
udp := &UDP{}
err := udp.DecodeFromBytes(data, p)
p.AddLayer(udp)
p.SetTransportLayer(udp)
if err != nil {
return err
}
return p.NextDecoder(udp.NextLayerType())
}
func (u *UDP) TransportFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointUDPPort, u.sPort, u.dPort)
}

@ -0,0 +1,44 @@
// Copyright 2012 Google, Inc. All rights reserved.
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// UDPLite is the layer for UDP-Lite headers (rfc 3828).
type UDPLite struct {
BaseLayer
SrcPort, DstPort UDPLitePort
ChecksumCoverage uint16
Checksum uint16
sPort, dPort []byte
}
// LayerType returns gopacket.LayerTypeUDPLite
func (u *UDPLite) LayerType() gopacket.LayerType { return LayerTypeUDPLite }
func decodeUDPLite(data []byte, p gopacket.PacketBuilder) error {
udp := &UDPLite{
SrcPort: UDPLitePort(binary.BigEndian.Uint16(data[0:2])),
sPort: data[0:2],
DstPort: UDPLitePort(binary.BigEndian.Uint16(data[2:4])),
dPort: data[2:4],
ChecksumCoverage: binary.BigEndian.Uint16(data[4:6]),
Checksum: binary.BigEndian.Uint16(data[6:8]),
BaseLayer: BaseLayer{data[:8], data[8:]},
}
p.AddLayer(udp)
p.SetTransportLayer(udp)
return p.NextDecoder(gopacket.LayerTypePayload)
}
func (u *UDPLite) TransportFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointUDPLitePort, u.sPort, u.dPort)
}

@ -0,0 +1,308 @@
// Copyright 2014 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
type USBEventType uint8
const (
USBEventTypeSubmit USBEventType = 'S'
USBEventTypeComplete USBEventType = 'C'
USBEventTypeError USBEventType = 'E'
)
func (a USBEventType) String() string {
switch a {
case USBEventTypeSubmit:
return "SUBMIT"
case USBEventTypeComplete:
return "COMPLETE"
case USBEventTypeError:
return "ERROR"
default:
return "Unknown event type"
}
}
type USBRequestBlockSetupRequest uint8
const (
USBRequestBlockSetupRequestGetStatus USBRequestBlockSetupRequest = 0x00
USBRequestBlockSetupRequestClearFeature USBRequestBlockSetupRequest = 0x01
USBRequestBlockSetupRequestSetFeature USBRequestBlockSetupRequest = 0x03
USBRequestBlockSetupRequestSetAddress USBRequestBlockSetupRequest = 0x05
USBRequestBlockSetupRequestGetDescriptor USBRequestBlockSetupRequest = 0x06
USBRequestBlockSetupRequestSetDescriptor USBRequestBlockSetupRequest = 0x07
USBRequestBlockSetupRequestGetConfiguration USBRequestBlockSetupRequest = 0x08
USBRequestBlockSetupRequestSetConfiguration USBRequestBlockSetupRequest = 0x09
USBRequestBlockSetupRequestSetIdle USBRequestBlockSetupRequest = 0x0a
)
func (a USBRequestBlockSetupRequest) String() string {
switch a {
case USBRequestBlockSetupRequestGetStatus:
return "GET_STATUS"
case USBRequestBlockSetupRequestClearFeature:
return "CLEAR_FEATURE"
case USBRequestBlockSetupRequestSetFeature:
return "SET_FEATURE"
case USBRequestBlockSetupRequestSetAddress:
return "SET_ADDRESS"
case USBRequestBlockSetupRequestGetDescriptor:
return "GET_DESCRIPTOR"
case USBRequestBlockSetupRequestSetDescriptor:
return "SET_DESCRIPTOR"
case USBRequestBlockSetupRequestGetConfiguration:
return "GET_CONFIGURATION"
case USBRequestBlockSetupRequestSetConfiguration:
return "SET_CONFIGURATION"
case USBRequestBlockSetupRequestSetIdle:
return "SET_IDLE"
default:
return "UNKNOWN"
}
}
type USBTransportType uint8
const (
USBTransportTypeTransferIn USBTransportType = 0x80 // Indicates send or receive
USBTransportTypeIsochronous USBTransportType = 0x00 // Isochronous transfers occur continuously and periodically. They typically contain time sensitive information, such as an audio or video stream.
USBTransportTypeInterrupt USBTransportType = 0x01 // Interrupt transfers are typically non-periodic, small device "initiated" communication requiring bounded latency, such as pointing devices or keyboards.
USBTransportTypeControl USBTransportType = 0x02 // Control transfers are typically used for command and status operations.
USBTransportTypeBulk USBTransportType = 0x03 // Bulk transfers can be used for large bursty data, using all remaining available bandwidth, no guarantees on bandwidth or latency, such as file transfers.
)
func (a USBTransportType) LayerType() gopacket.LayerType {
return USBTypeMetadata[a].LayerType
}
func (a USBTransportType) String() string {
switch a {
case USBTransportTypeTransferIn:
return "Transfer In"
case USBTransportTypeIsochronous:
return "Isochronous"
case USBTransportTypeInterrupt:
return "Interrupt"
case USBTransportTypeControl:
return "Control"
case USBTransportTypeBulk:
return "Bulk"
default:
return "Unknown transport type"
}
}
type USBDirectionType uint8
const (
USBDirectionTypeUnknown USBDirectionType = iota
USBDirectionTypeIn
USBDirectionTypeOut
)
func (a USBDirectionType) String() string {
switch a {
case USBDirectionTypeIn:
return "In"
case USBDirectionTypeOut:
return "Out"
default:
return "Unknown direction type"
}
}
// The reference at http://www.beyondlogic.org/usbnutshell/usb1.shtml contains more information about the protocol.
type USB struct {
BaseLayer
ID uint64
EventType USBEventType
TransferType USBTransportType
Direction USBDirectionType
EndpointNumber uint8
DeviceAddress uint8
BusID uint16
TimestampSec int64
TimestampUsec int32
Setup bool
Data bool
Status int32
UrbLength uint32
UrbDataLength uint32
UrbInterval uint32
UrbStartFrame uint32
UrbCopyOfTransferFlags uint32
IsoNumDesc uint32
}
func (u *USB) LayerType() gopacket.LayerType { return LayerTypeUSB }
func (m *USB) NextLayerType() gopacket.LayerType {
if m.Setup {
return LayerTypeUSBRequestBlockSetup
} else if m.Data {
}
return m.TransferType.LayerType()
}
func decodeUSB(data []byte, p gopacket.PacketBuilder) error {
d := &USB{}
return decodingLayerDecoder(d, data, p)
}
func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.ID = binary.LittleEndian.Uint64(data[0:8])
m.EventType = USBEventType(data[8])
m.TransferType = USBTransportType(data[9])
m.EndpointNumber = data[10] & 0x7f
if data[10]&uint8(USBTransportTypeTransferIn) > 0 {
m.Direction = USBDirectionTypeIn
} else {
m.Direction = USBDirectionTypeOut
}
m.DeviceAddress = data[11]
m.BusID = binary.LittleEndian.Uint16(data[12:14])
if uint(data[14]) == 0 {
m.Setup = true
}
if uint(data[15]) == 0 {
m.Data = true
}
m.TimestampSec = int64(binary.LittleEndian.Uint64(data[16:24]))
m.TimestampUsec = int32(binary.LittleEndian.Uint32(data[24:28]))
m.Status = int32(binary.LittleEndian.Uint32(data[28:32]))
m.UrbLength = binary.LittleEndian.Uint32(data[32:36])
m.UrbDataLength = binary.LittleEndian.Uint32(data[36:40])
m.Contents = data[:40]
m.Payload = data[40:]
if m.Setup {
m.Payload = data[40:]
} else if m.Data {
m.Payload = data[uint32(len(data))-m.UrbDataLength:]
}
// if 64 bit, dissect_linux_usb_pseudo_header_ext
if false {
m.UrbInterval = binary.LittleEndian.Uint32(data[40:44])
m.UrbStartFrame = binary.LittleEndian.Uint32(data[44:48])
m.UrbDataLength = binary.LittleEndian.Uint32(data[48:52])
m.IsoNumDesc = binary.LittleEndian.Uint32(data[52:56])
m.Contents = data[:56]
m.Payload = data[56:]
}
// crc5 or crc16
// eop (end of packet)
return nil
}
type USBRequestBlockSetup struct {
BaseLayer
RequestType uint8
Request USBRequestBlockSetupRequest
Value uint16
Index uint16
Length uint16
}
func (u *USBRequestBlockSetup) LayerType() gopacket.LayerType { return LayerTypeUSBRequestBlockSetup }
func (m *USBRequestBlockSetup) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func (m *USBRequestBlockSetup) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.RequestType = data[0]
m.Request = USBRequestBlockSetupRequest(data[1])
m.Value = binary.LittleEndian.Uint16(data[2:4])
m.Index = binary.LittleEndian.Uint16(data[4:6])
m.Length = binary.LittleEndian.Uint16(data[6:8])
m.Contents = data[:8]
m.Payload = data[8:]
return nil
}
func decodeUSBRequestBlockSetup(data []byte, p gopacket.PacketBuilder) error {
d := &USBRequestBlockSetup{}
return decodingLayerDecoder(d, data, p)
}
type USBControl struct {
BaseLayer
}
func (u *USBControl) LayerType() gopacket.LayerType { return LayerTypeUSBControl }
func (m *USBControl) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func (m *USBControl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.Contents = data
return nil
}
func decodeUSBControl(data []byte, p gopacket.PacketBuilder) error {
d := &USBControl{}
return decodingLayerDecoder(d, data, p)
}
type USBInterrupt struct {
BaseLayer
}
func (u *USBInterrupt) LayerType() gopacket.LayerType { return LayerTypeUSBInterrupt }
func (m *USBInterrupt) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func (m *USBInterrupt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.Contents = data
return nil
}
func decodeUSBInterrupt(data []byte, p gopacket.PacketBuilder) error {
d := &USBInterrupt{}
return decodingLayerDecoder(d, data, p)
}
type USBBulk struct {
BaseLayer
}
func (u *USBBulk) LayerType() gopacket.LayerType { return LayerTypeUSBBulk }
func (m *USBBulk) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func (m *USBBulk) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
m.Contents = data
return nil
}
func decodeUSBBulk(data []byte, p gopacket.PacketBuilder) error {
d := &USBBulk{}
return decodingLayerDecoder(d, data, p)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save