Browse Source

Merge branch 'master' into feedback-upload-refactor

Miro 5 years ago
parent
commit
794b72f3b0
100 changed files with 12997 additions and 308 deletions
  1. 5 1
      .travis.yml
  2. 7 3
      MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java
  3. 43 16
      README.md
  4. 3 1
      psiphon/TCPConn.go
  5. 72 0
      psiphon/common/networkConfig.go
  6. 55 0
      psiphon/common/networkConfig_linux.go
  7. 27 0
      psiphon/common/networkConfig_other.go
  8. 6 6
      psiphon/common/networkInterface.go
  9. 781 0
      psiphon/common/packetman/packetman.go
  10. 690 0
      psiphon/common/packetman/packetman_linux.go
  11. 201 0
      psiphon/common/packetman/packetman_linux_test.go
  12. 277 0
      psiphon/common/packetman/packetman_test.go
  13. 57 0
      psiphon/common/packetman/packetman_unsupported.go
  14. 28 3
      psiphon/common/parameters/clientParameters.go
  15. 10 0
      psiphon/common/parameters/clientParameters_test.go
  16. 75 0
      psiphon/common/parameters/packetman.go
  17. 10 0
      psiphon/common/protocol/protocol.go
  18. 9 14
      psiphon/common/tun/tun_darwin.go
  19. 19 44
      psiphon/common/tun/tun_linux.go
  20. 0 47
      psiphon/common/tun/utils.go
  21. 36 0
      psiphon/common/utils.go
  22. 52 0
      psiphon/common/utils_test.go
  23. 62 0
      psiphon/controller.go
  24. 7 6
      psiphon/controller_test.go
  25. 20 0
      psiphon/dataStore.go
  26. 6 3
      psiphon/dialParameters.go
  27. 20 12
      psiphon/dialParameters_test.go
  28. 1 0
      psiphon/exchange_test.go
  29. 2 2
      psiphon/httpProxy.go
  30. 1 1
      psiphon/meekConn.go
  31. 5 0
      psiphon/net.go
  32. 10 7
      psiphon/notice.go
  33. 7 3
      psiphon/remoteServerList.go
  34. 21 6
      psiphon/server/api.go
  35. 3 0
      psiphon/server/config.go
  36. 60 25
      psiphon/server/meek.go
  37. 4 0
      psiphon/server/net.go
  38. 202 0
      psiphon/server/packetman.go
  39. 46 0
      psiphon/server/server_packetman_test.go
  40. 48 3
      psiphon/server/server_test.go
  41. 55 7
      psiphon/server/services.go
  42. 1 0
      psiphon/server/sessionID_test.go
  43. 53 12
      psiphon/server/tunnelServer.go
  44. 33 22
      psiphon/serverApi.go
  45. 7 0
      psiphon/tactics.go
  46. 102 0
      psiphon/tactics_test.go
  47. 19 2
      psiphon/upstreamproxy/proxy_http.go
  48. 4 4
      psiphon/upstreamproxy/upstreamproxy.go
  49. 59 12
      vendor/github.com/elazarl/goproxy/README.md
  50. 6 1
      vendor/github.com/elazarl/goproxy/counterecryptor.go
  51. 11 5
      vendor/github.com/elazarl/goproxy/ctx.go
  52. 16 0
      vendor/github.com/elazarl/goproxy/dispatcher.go
  53. 1 1
      vendor/github.com/elazarl/goproxy/doc.go
  54. 79 0
      vendor/github.com/elazarl/goproxy/ext/auth/basic.go
  55. 3 0
      vendor/github.com/elazarl/goproxy/go.mod
  56. 3 0
      vendor/github.com/elazarl/goproxy/go.sum
  57. 51 12
      vendor/github.com/elazarl/goproxy/https.go
  58. 5 0
      vendor/github.com/elazarl/goproxy/logger.go
  59. 60 18
      vendor/github.com/elazarl/goproxy/proxy.go
  60. 1 0
      vendor/github.com/elazarl/goproxy/responses.go
  61. 32 9
      vendor/github.com/elazarl/goproxy/signer.go
  62. 121 0
      vendor/github.com/elazarl/goproxy/websocket.go
  63. 10 0
      vendor/github.com/florianl/go-nfqueue/LICENSE
  64. 14 0
      vendor/github.com/florianl/go-nfqueue/README.md
  65. 113 0
      vendor/github.com/florianl/go-nfqueue/attribute.go
  66. 10 0
      vendor/github.com/florianl/go-nfqueue/doc.go
  67. 9 0
      vendor/github.com/florianl/go-nfqueue/go.mod
  68. 36 0
      vendor/github.com/florianl/go-nfqueue/go.sum
  69. 3 0
      vendor/github.com/florianl/go-nfqueue/internal/unix/doc.go
  70. 16 0
      vendor/github.com/florianl/go-nfqueue/internal/unix/types_linux.go
  71. 11 0
      vendor/github.com/florianl/go-nfqueue/internal/unix/types_other.go
  72. 197 0
      vendor/github.com/florianl/go-nfqueue/nfqueue.go
  73. 169 0
      vendor/github.com/florianl/go-nfqueue/nfqueue_gteq_1.12.go
  74. 173 0
      vendor/github.com/florianl/go-nfqueue/nfqueue_lt_1.12.go
  75. 162 0
      vendor/github.com/florianl/go-nfqueue/types.go
  76. 54 0
      vendor/github.com/google/gopacket/AUTHORS
  77. 215 0
      vendor/github.com/google/gopacket/CONTRIBUTING.md
  78. 28 0
      vendor/github.com/google/gopacket/LICENSE
  79. 12 0
      vendor/github.com/google/gopacket/README.md
  80. 178 0
      vendor/github.com/google/gopacket/base.go
  81. 157 0
      vendor/github.com/google/gopacket/decode.go
  82. 432 0
      vendor/github.com/google/gopacket/doc.go
  83. 236 0
      vendor/github.com/google/gopacket/flows.go
  84. 288 0
      vendor/github.com/google/gopacket/gc
  85. 9 0
      vendor/github.com/google/gopacket/go.mod
  86. 19 0
      vendor/github.com/google/gopacket/go.sum
  87. 107 0
      vendor/github.com/google/gopacket/layerclass.go
  88. 118 0
      vendor/github.com/google/gopacket/layers/arp.go
  89. 166 0
      vendor/github.com/google/gopacket/layers/asf.go
  90. 194 0
      vendor/github.com/google/gopacket/layers/asf_presencepong.go
  91. 52 0
      vendor/github.com/google/gopacket/layers/base.go
  92. 481 0
      vendor/github.com/google/gopacket/layers/bfd.go
  93. 659 0
      vendor/github.com/google/gopacket/layers/cdp.go
  94. 109 0
      vendor/github.com/google/gopacket/layers/ctp.go
  95. 592 0
      vendor/github.com/google/gopacket/layers/dhcpv4.go
  96. 360 0
      vendor/github.com/google/gopacket/layers/dhcpv6.go
  97. 621 0
      vendor/github.com/google/gopacket/layers/dhcpv6_options.go
  98. 1098 0
      vendor/github.com/google/gopacket/layers/dns.go
  99. 61 0
      vendor/github.com/google/gopacket/layers/doc.go
  100. 2118 0
      vendor/github.com/google/gopacket/layers/dot11.go

+ 5 - 1
.travis.yml

@@ -15,6 +15,8 @@ script:
 - go test -race -v ./common/fragmentor
 - go test -race -v ./common/obfuscator
 - go test -race -v ./common/osl
+# TODO: enable all packetman tests with tag PACKET_MANIPULATOR_TEST; requires CAP_NETADMIN and CAP_RAW
+- go test -race -v ./common/packetman
 - go test -race -v ./common/parameters
 - go test -race -v ./common/protocol
 - go test -race -v ./common/quic
@@ -27,6 +29,7 @@ script:
 - go test -race -v ./common/values
 - go test -race -v ./common/wildcard
 - go test -race -v ./transferstats
+# TODO: seet "packetman" comment above
 - go test -race -v ./server
 - go test -race -v ./server/psinet
 - go test -race -v ../Server/logging/analysis
@@ -39,10 +42,11 @@ script:
 - go test -v -covermode=count -coverprofile=obfuscator.coverprofile ./common/obfuscator
 - go test -v -covermode=count -coverprofile=osl.coverprofile ./common/osl
 - go test -v -covermode=count -coverprofile=parameters.coverprofile ./common/parameters
+- go test -v -covermode=count -coverprofile=packetman.coverprofile ./common/packetman
 - go test -v -covermode=count -coverprofile=protocol.coverprofile ./common/protocol
 - go test -v -covermode=count -coverprofile=quic.coverprofile ./common/quic
 - go test -v -covermode=count -coverprofile=tactics.coverprofile ./common/tactics
-# TODO: see comment above
+# TODO: see "tun" test comment above
 #- sudo -E env "PATH=$PATH" go test -v -covermode=count -coverprofile=tun.coverprofile ./common/tun
 - go test -v -covermode=count -coverprofile=values.coverprofile ./common/values
 - go test -v -covermode=count -coverprofile=wildcard.coverprofile ./common/wildcard

+ 7 - 3
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -1131,14 +1131,18 @@ public class PsiphonTunnel {
         candidates.put("192", new PrivateAddress("192.168.0.1", "192.168.0.0", 16, "192.168.0.2"));
         candidates.put("169", new PrivateAddress("169.254.1.1", "169.254.1.0", 24, "169.254.1.2"));
 
-        List<NetworkInterface> netInterfaces;
+        Enumeration<NetworkInterface> netInterfaces;
         try {
-            netInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+            netInterfaces = NetworkInterface.getNetworkInterfaces();
         } catch (SocketException e) {
             throw new Exception("selectPrivateAddress failed", e);
         }
 
-        for (NetworkInterface netInterface : netInterfaces) {
+        if (netInterfaces == null) {
+            throw new Exception("no network interfaces found");
+        }
+
+        for (NetworkInterface netInterface : Collections.list(netInterfaces)) {
             for (InetAddress inetAddress : Collections.list(netInterface.getInetAddresses())) {
                 if (inetAddress instanceof Inet4Address) {
                     String ipAddress = inetAddress.getHostAddress();

+ 43 - 16
README.md

@@ -150,26 +150,53 @@ Acknowledgements
 
 Psiphon Tunnel Core uses:
 
-* [Go](https://golang.org/)
+* [Go](https://golang.org)
+* [agl/ed25519](https://github.com/agl/ed25519)
+* [AndreasBriese/bbloom](https://github.com/AndreasBriese/bbloom)
+* [aristanetworks/goarista/monotime](https://github.com/aristanetworks/goarista)
+* [armon/go-proxyproto](https://github.com/armon/go-proxyproto)
+* [armon/go-socks](https://github.com/armon/go-socks5)
+* [bifurcation/mint](https://github.com/bifurcation/mint)
 * [boltdb/bolt](https://github.com/boltdb/bolt)
-* [patrickmn/go-cache](https://github.com/patrickmn/go-cache)
-* [miekg/dns](https://github.com/miekg/dns)
-* [ThomsonReutersEikon/go-ntlm](https://github.com/ThomsonReutersEikon/go-ntlm)
-* [Yawning/goptlib](https://github.com/Yawning/goptlib)
-* [zach-klippenstein/goregen](https://github.com/zach-klippenstein/goregen)
+* [cheekybits/genny/generic](https://github.com/cheekybits/genny/generic)
+* [cloudflare/tls-tris](https://github.com/cloudflare/tls-tris)
+* [codahale/sss](https://github.com/codahale/sss)
+* [cognusion/go-cache-lru](https://github.com/cognusion/go-cache-lru)
 * [creack/goselect](https://github.com/creack/goselect)
-* [Sirupsen/logrus](https://github.com/Sirupsen/logrus)
+* [davecgh/go-spew/spew](https://github.com/davecgh/go-spew/spew)
+* [deckarep/golang-set](https://github.com/deckarep/golang-set)
+* [dgraph-io/badger](https://github.com/dgraph-io/badger)
+* [dgryski/go-farm](https://github.com/dgryski/go-farm)
+* [elazarl/goproxy](https://github.com/elazarl/goproxy)
+* [florianl/go-nfqueue](https://github.com/florianl/go-nfqueue)
+* [gobwas/glob](https://github.com/gobwas/glob)
+* [golang/protobuf](https://github.com/golang/protobuf)
+* [google/gopacket](https://github.com/google/gopacket)
 * [grafov/m3u8](https://github.com/grafov/m3u8)
-* [oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang)
-* [goarista/monotime](https://github.com/aristanetworks/goarista)
-* [spacemonkeygo/openssl](https://github.com/spacemonkeygo/openssl)
-* [kardianos/osext](https://github.com/kardianos/osext)
-* [mitchellh/panicwrap](https://github.com/mitchellh/panicwrap)
+* [hashicorp/golang-lru](https://github.com/hashicorp/golang-lru)
 * [juju/ratelimit](https://github.com/juju/ratelimit)
-* [codahale/sss](https://github.com/codahale/sss)
+* [kardianos/osext](https://github.com/kardianos/osext)
+* [lucas-clemente/quic-go](https://github.com/lucas-clemente/quic-go)
 * [marusama/semaphore](https://github.com/marusama/semaphore)
+* [mdlayher/netlink)](https://github.com/mdlayher/netlink)
+* [miekg/dns](https://github.com/miekg/dns)
+* [mitchellh/panicwrap](https://github.com/mitchellh/panicwrap)
+* [oschwald/maxminddb-golang](https://github.com/oschwald/maxminddb-golang)
+* [patrickmn/go-cache](https://github.com/patrickmn/go-cache)
+* [pkg/errors](https://github.com/pkg/errors)
+* [pmezard/go-difflib](https://github.com/pmezard/go-difflib)
+* [redjack/marionette](https://github.com/redjack/marionette)
+* [refraction-networking/gotapdance](https://github.com/refraction-networking/gotapdance)
 * [refraction-networking/utls](https://github.com/refraction-networking/utls)
-* [lucas-clemente/quic-go](https://github.com/lucas-clemente/quic-go)
-* [cloudflare/tls-tris](https://github.com/cloudflare/tls-tris)
-* [Yawning/chacha20](https://github.com/Yawning/chacha20)
+* [ryanuber/go-glob](https://github.com/ryanuber/go-glob)
+* [sergeyfrolov/bsbuffer](https://github.com/sergeyfrolov/bsbuffer)
+* [sirupsen/logrus](https://github.com/sirupsen/logrus)
+* [stretchr/testify](https://github.com/stretchr/testify)
+* [syndtr/gocapability/capability](https://github.com/syndtr/gocapability/capability)
+* [ThomsonReutersEikon/go-ntlm](https://github.com/ThomsonReutersEikon/go-ntlm)
 * [wader/filtertransport](https://github.com/wader/filtertransport)
+* [Yawning/chacha20](https://github.com/Yawning/chacha20)
+* [Yawning/goptlib](https://github.com/Yawning/goptlib)
+* [zach-klippenstein/goregen](https://github.com/zach-klippenstein/goregen)
+* [zap](https://go.uber.org/zap)
+

+ 3 - 1
psiphon/TCPConn.go

@@ -136,7 +136,9 @@ func proxiedTcpDial(
 	go func() {
 		conn, err := upstreamDialer("tcp", addr)
 		if _, ok := err.(*upstreamproxy.Error); ok {
-			NoticeUpstreamProxyError(err)
+			if config.UpstreamProxyErrorCallback != nil {
+				config.UpstreamProxyErrorCallback(err)
+			}
 		}
 		resultChannel <- upstreamDialResult{
 			conn: conn,

+ 72 - 0
psiphon/common/networkConfig.go

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"fmt"
+	"os/exec"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+)
+
+// RunNetworkConfigCommand execs a network config command, such as "ifconfig"
+// or "iptables". On platforms that support capabilities, the network config
+// capabilities of the current process is made available to the command
+// subprocess. Alternatively, "sudo" will be used when useSudo is true.
+func RunNetworkConfigCommand(
+	logger Logger,
+	useSudo bool,
+	commandName string, commandArgs ...string) error {
+
+	// configureSubprocessCapabilities will set inheritable
+	// capabilities on platforms which support that (Linux).
+	// Specifically, CAP_NET_ADMIN will be transferred from
+	// this process to the child command.
+
+	err := configureNetworkConfigSubprocessCapabilities()
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	// TODO: use CommandContext to interrupt on process shutdown?
+	// (the commands currently being issued shouldn't block...)
+
+	if useSudo {
+		commandArgs = append([]string{commandName}, commandArgs...)
+		commandName = "sudo"
+	}
+
+	cmd := exec.Command(commandName, commandArgs...)
+	output, err := cmd.CombinedOutput()
+
+	logger.WithTraceFields(LogFields{
+		"command": commandName,
+		"args":    commandArgs,
+		"output":  string(output),
+		"error":   err,
+	}).Debug("exec")
+
+	if err != nil {
+		err := fmt.Errorf(
+			"command %s %+v failed with %s", commandName, commandArgs, string(output))
+		return errors.Trace(err)
+	}
+	return nil
+}

+ 55 - 0
psiphon/common/networkConfig_linux.go

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+import (
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+	"github.com/syndtr/gocapability/capability"
+)
+
+func configureNetworkConfigSubprocessCapabilities() error {
+
+	// If this process has CAP_NET_ADMIN, make it available to be inherited
+	// be child processes via ambient mechanism described here:
+	// https://github.com/torvalds/linux/commit/58319057b7847667f0c9585b9de0e8932b0fdb08
+	//
+	// The ambient mechanism is available in Linux kernel 4.3 and later.
+
+	// When using capabilities, this process should have CAP_NET_ADMIN in order
+	// to create tun devices. And the subprocess operations such as using "ifconfig"
+	// and "iptables" for network config require the same CAP_NET_ADMIN capability.
+
+	cap, err := capability.NewPid(0)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	if cap.Get(capability.EFFECTIVE, capability.CAP_NET_ADMIN) {
+
+		cap.Set(capability.INHERITABLE|capability.AMBIENT, capability.CAP_NET_ADMIN)
+
+		err = cap.Apply(capability.AMBIENT)
+		if err != nil {
+			return errors.Trace(err)
+		}
+	}
+
+	return nil
+}

+ 27 - 0
psiphon/common/networkConfig_other.go

@@ -0,0 +1,27 @@
+// +build !linux
+
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package common
+
+func configureNetworkConfigSubprocessCapabilities() error {
+	// No-op on this platform
+	return nil
+}

+ 6 - 6
psiphon/common/networkInterface.go

@@ -25,10 +25,10 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 )
 
-// GetInterfaceIPAddresses takes an interface name, such as "eth0", and returns
-// the first IPv4 and IPv6 addresses associated with it. Either of the IPv4 or
-// IPv6 address may be nil. If neither type of address is found, an error
-// is returned.
+// GetInterfaceIPAddresses takes an interface name, such as "eth0", and
+// returns the first non-link local IPv4 and IPv6 addresses associated with
+// it. Either of the IPv4 or IPv6 address may be nil. If neither type of
+// address is found, an error is returned.
 func GetInterfaceIPAddresses(interfaceName string) (net.IP, net.IP, error) {
 
 	var IPv4Address, IPv6Address net.IP
@@ -51,11 +51,11 @@ func GetInterfaceIPAddresses(interfaceName string) (net.IP, net.IP, error) {
 		}
 
 		if ipNet.IP.To4() != nil {
-			if IPv4Address == nil {
+			if IPv4Address == nil && !ipNet.IP.IsLinkLocalUnicast() {
 				IPv4Address = ipNet.IP
 			}
 		} else {
-			if IPv6Address == nil {
+			if IPv6Address == nil && !ipNet.IP.IsLinkLocalUnicast() {
 				IPv6Address = ipNet.IP
 			}
 		}

+ 781 - 0
psiphon/common/packetman/packetman.go

@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+Package packetman implements low-level manipulation of TCP packets, enabling a
+variety of strategies to evade network censorship.
+
+This implementation is entirely based on and is a subset of Geneva:
+
+  Come as You Are: Helping Unmodified Clients Bypass Censorship with
+  Server-side Evasion
+  Kevin Bock, George Hughey, Louis-Henri Merino, Tania Arya, Daniel Liscinsky,
+  Regina Pogosian, Dave Levin
+  ACM SIGCOMM 2020
+
+  Geneva: Evolving Censorship Evasion Strategies
+  Kevin Bock, George Hughey, Xiao Qiang, Dave Levin
+  ACM CCS 2019 (Conference on Computer and Communications Security)
+
+  https://github.com/Kkevsterrr/geneva
+
+This package implements the equivilent of the Geneva "engine", which can
+execute packet manipulation strategies. It does not implement the genetic
+algorithm component.
+
+Other notable differences:
+
+- We intercept, parse, and transform only server-side outbound SYN-ACK
+  packets. Geneva supports client-side packet manipulation with a more diverse
+  set of trigger packets, but in practise we cannot execute most low-level
+  packet operations on client platforms such as Android and iOS.
+
+- For expediancy, we use a simplified strategy syntax (called transformation
+  specs, to avoid confusion with the more general original). As we do not
+  evolve strategies, we do not use a tree representation and some
+  randomization tranformations are simplified.
+
+At this time, full functionality is limited to the Linux platform.
+
+Security: external parties can induce the server to emit a SYN-ACK, invoking
+the packet manipulation logic. External parties cannot set the transformation
+specs, and, as the input is the server-side generated SYN-ACK packet, cannot
+influence the packet manipulation with any external input parameters.
+
+*/
+package packetman
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"net"
+	"strings"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+)
+
+// Config specifies a packet manipulation configuration.
+type Config struct {
+
+	// Logger is used for logging events and metrics.
+	Logger common.Logger
+
+	// ProtocolPorts specifies the set of TCP ports to which SYN-ACK packet
+	// interception and manipulation is to be applied. To accommodate hosts with
+	// multiple IP addresses, packet interception is applied to all interfaces.
+	ProtocolPorts []int
+
+	// On Linux, which uses NFQUEUE and raw sockets, QueueNumber is the NFQUEUE
+	// queue-num parameter to be used.
+	QueueNumber int
+
+	// On Linux, which uses NFQUEUE and raw sockets, SocketMark is the SO_MARK
+	// value to be used. When 0, a default value is used.
+	SocketMark int
+
+	// Specs is the list of packet transformation Spec value that are to be
+	// available for packet manipulation. Spec names must be unique.
+	Specs []*Spec
+
+	// SelectSpecName is a callback invoked for each intercepted SYN-ACK packet.
+	// SelectSpecName must return a name of a Spec, in Specs, to apply that
+	// transformation spec, or "" to send the SYN-ACK packet unmodified.
+	//
+	// The inputs protocolPort and clientIP allow the callback to select a Spec
+	// based on the protocol running at the intercepted packet's port and/or
+	// client GeoIP.
+	SelectSpecName func(protocolPort int, clientIP net.IP) string
+
+	// SudoNetworkConfigCommands specifies whether to use "sudo" when executing
+	// network configuration commands. See comment for same parameter in
+	// psiphon/common/tun.
+	SudoNetworkConfigCommands bool
+
+	// AllowNoIPv6NetworkConfiguration indicates that failures while configuring
+	// tun interfaces and routing for IPv6 are to be logged as warnings only. See
+	// comment for same parameter in psiphon/common/tun.
+	AllowNoIPv6NetworkConfiguration bool
+}
+
+// Spec specifies a set of transformations to be applied to an intercepted
+// SYN-ACK packet to produce zero or more replacement packets to be sent in
+// its place.
+//
+// Each element in PacketSpecs specifies a new outgoing packet. Each element
+// in a packet specification specifies an individual transformation to be
+// applied, in turn, to a copy of the intercepted SYN-ACK packet, producing
+// the outgoing packet.
+//
+// Syntax of individual tranformations:
+//
+// "TCP-flags random|<flags>"
+// "TCP-<field> random|<base64>"
+// "TCP-option-<option> random|omit|<base64>"
+// "TCP-payload random|<base64>"
+//
+// flags:   FSRPAUECN
+//
+// fields:  srcport, dstport, seq, ack, dataoffset, window, checksum, urgent
+//
+// options: eol, nop, mss, windowscale, sackpermitted, sack, timestamps,
+//          altchecksum, altchecksumdata, md5header, usertimeout
+//
+//
+// For example, this Geneva strategy:
+//   [TCP:flags:SA]-duplicate(tamper{TCP:flags:replace:R},tamper{TCP:flags:replace:S})-| \/
+//
+// is represented as follows (in JSON encoding):
+//   [["TCP-flags R"], ["TCP-flags S"]]
+//
+//
+// Field and option values must be the expected length (see implementation).
+//
+// A Spec may produce invalid packets. For example, the total options length
+// can exceed 40 bytes and the DataOffset field may overflow.
+type Spec struct {
+	Name        string
+	PacketSpecs [][]string
+}
+
+// Validate checks that the transformation spec is syntactically correct.
+func (s *Spec) Validate() error {
+	_, err := compileSpec(s)
+	return errors.Trace(err)
+}
+
+type compiledSpec struct {
+	name                string
+	compiledPacketSpecs [][]transformation
+}
+
+func compileSpec(spec *Spec) (*compiledSpec, error) {
+
+	compiledPacketSpecs := make([][]transformation, len(spec.PacketSpecs))
+	for i, _ := range spec.PacketSpecs {
+		compiledPacketSpecs[i] = make([]transformation, len(spec.PacketSpecs[i]))
+		for j, transformationSpec := range spec.PacketSpecs[i] {
+			transform, err := compileTransformation(transformationSpec)
+			if err != nil {
+				return nil, errors.Trace(err)
+			}
+			compiledPacketSpecs[i][j] = transform
+		}
+	}
+	return &compiledSpec{
+		name:                spec.Name,
+		compiledPacketSpecs: compiledPacketSpecs}, nil
+}
+
+func (spec *compiledSpec) apply(interceptedPacket gopacket.Packet) ([][]byte, error) {
+
+	packets := make([][]byte, len(spec.compiledPacketSpecs))
+
+	for i, packetTransformations := range spec.compiledPacketSpecs {
+
+		var networkLayer gopacket.NetworkLayer
+		var serializableNetworkLayer gopacket.SerializableLayer
+
+		// Copy the network layer (IPv4 or IPv6) as modifications may be made to
+		// checksums or lengths in that layer. Note this is not a deep copy of
+		// fields such as the Options slice, as these are not modified.
+
+		interceptedIPv4Layer := interceptedPacket.Layer(layers.LayerTypeIPv4)
+		if interceptedIPv4Layer != nil {
+			transformedIPv4 := *interceptedIPv4Layer.(*layers.IPv4)
+			networkLayer = &transformedIPv4
+			serializableNetworkLayer = &transformedIPv4
+		} else {
+			interceptedIPv6Layer := interceptedPacket.Layer(layers.LayerTypeIPv6)
+			transformedIPv6 := *interceptedIPv6Layer.(*layers.IPv6)
+			networkLayer = &transformedIPv6
+			serializableNetworkLayer = &transformedIPv6
+		}
+
+		interceptedTCP := interceptedPacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
+
+		// Copy the TCP layer before transforming it. Again this is not a deep copy.
+		// If a transformation modifies the Options slice, it will be copied at that
+		// time.
+
+		transformedTCP := *interceptedTCP
+		var payload gopacket.Payload
+		setCalculatedField := false
+
+		for _, transform := range packetTransformations {
+			transform.apply(&transformedTCP, &payload)
+			if transform.setsCalculatedField() {
+				setCalculatedField = true
+			}
+		}
+
+		err := transformedTCP.SetNetworkLayerForChecksum(networkLayer)
+		if err != nil {
+			return nil, errors.Trace(err)
+		}
+
+		buffer := gopacket.NewSerializeBuffer()
+		options := gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true}
+
+		gopacket.SerializeLayers(
+			buffer,
+			options,
+			serializableNetworkLayer,
+			&transformedTCP,
+			payload)
+
+		// In the first SerializeLayers call, all IP and TCP length and checksums
+		// are recalculated and set to the correct values with transformations
+		// applied.
+		//
+		// If the spec calls for setting the TCP DataOffset or Checksum, a second
+		// SerializeLayers call is performed, which will repave these values without
+		// recalculation; all other calculated lengths and checksums are retained
+		// from the first round.
+
+		if setCalculatedField {
+			buffer.Clear()
+			gopacket.SerializeLayers(
+				buffer,
+				gopacket.SerializeOptions{},
+				serializableNetworkLayer,
+				&transformedTCP,
+				payload)
+		}
+
+		packets[i] = buffer.Bytes()
+	}
+
+	return packets, nil
+}
+
+type transformation interface {
+	apply(tcp *layers.TCP, payload *gopacket.Payload)
+	setsCalculatedField() bool
+}
+
+const (
+	transformationTypeUnknown = iota
+	transformationTypeOmit
+	transformationTypeRandom
+	transformationTypeValue
+)
+
+func compileTransformation(spec string) (transformation, error) {
+
+	parts := strings.Split(spec, " ")
+	if len(parts) != 2 {
+		return nil, errors.Tracef("invalid spec: %s", spec)
+	}
+	fieldSpec := parts[0]
+	valueSpec := parts[1]
+
+	parts = strings.Split(fieldSpec, "-")
+	if (len(parts) != 2 && len(parts) != 3) || parts[0] != "TCP" {
+		return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
+	}
+
+	var transformationType int
+
+	if valueSpec == "omit" {
+		transformationType = transformationTypeOmit
+	} else if valueSpec == "random" {
+		transformationType = transformationTypeRandom
+	} else {
+		transformationType = transformationTypeValue
+	}
+
+	var t transformation
+	var err error
+
+	if len(parts) == 3 {
+		if parts[1] != "option" {
+			return nil, errors.Tracef("invalid field spec: %s", fieldSpec)
+		}
+		t, err = newTransformationTCPOption(parts[2], transformationType, valueSpec)
+	} else if parts[1] == "flags" {
+		t, err = newTransformationTCPFlags(transformationType, valueSpec)
+	} else if parts[1] == "payload" {
+		t, err = newTransformationTCPPayload(transformationType, valueSpec)
+	} else {
+		t, err = newTransformationTCPField(parts[1], transformationType, valueSpec)
+	}
+	if err != nil {
+		return nil, errors.Tracef("invalid field spec: %s: %v", fieldSpec, err)
+	}
+	return t, nil
+}
+
+type transformationTCPFlags struct {
+	transformationType int
+	flags              string
+}
+
+func newTransformationTCPFlags(
+	transformationType int, valueSpec string) (*transformationTCPFlags, error) {
+
+	var flags string
+
+	switch transformationType {
+	case transformationTypeRandom:
+	case transformationTypeValue:
+		checkFlags := valueSpec
+		for _, f := range "FSRPAUECN" {
+			checkFlags = strings.ReplaceAll(checkFlags, string(f), "")
+		}
+		if checkFlags != "" {
+			return nil, errors.Tracef("invalid value spec: %s", valueSpec)
+		}
+		flags = valueSpec
+	default:
+		return nil, errors.Tracef("invalid transformation type")
+	}
+
+	return &transformationTCPFlags{
+		transformationType: transformationType,
+		flags:              flags,
+	}, nil
+}
+
+func (t *transformationTCPFlags) apply(tcp *layers.TCP, _ *gopacket.Payload) {
+
+	var flags string
+
+	if t.transformationType == transformationTypeRandom {
+
+		// Differs from Geneva, which often selects real flag combinations,
+		// presumably to focus its search space:
+		// https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121.
+
+		for _, f := range "FSRPAUECN" {
+			if prng.FlipCoin() {
+				flags += string(f)
+			}
+		}
+	} else {
+		flags = t.flags
+	}
+
+	tcp.FIN = strings.Index(t.flags, "F") != -1
+	tcp.SYN = strings.Index(t.flags, "S") != -1
+	tcp.RST = strings.Index(t.flags, "R") != -1
+	tcp.PSH = strings.Index(t.flags, "P") != -1
+	tcp.ACK = strings.Index(t.flags, "A") != -1
+	tcp.URG = strings.Index(t.flags, "U") != -1
+	tcp.ECE = strings.Index(t.flags, "E") != -1
+	tcp.CWR = strings.Index(t.flags, "C") != -1
+	tcp.NS = strings.Index(t.flags, "N") != -1
+}
+
+func (t *transformationTCPFlags) setsCalculatedField() bool {
+	return false
+}
+
+type transformationTCPField struct {
+	fieldName          string
+	transformationType int
+	value              []byte
+}
+
+const (
+	tcpFieldSrcPort    = "srcport"
+	tcpFieldDstPort    = "dstport"
+	tcpFieldSeq        = "seq"
+	tcpFieldAck        = "ack"
+	tcpFieldDataOffset = "dataoffset"
+	tcpFieldWindow     = "window"
+	tcpFieldChecksum   = "checksum"
+	tcpFieldUrgent     = "urgent"
+)
+
+func newTransformationTCPField(
+	fieldName string, transformationType int, valueSpec string) (*transformationTCPField, error) {
+
+	length := 0
+
+	switch fieldName {
+	case tcpFieldSrcPort:
+		length = 2
+	case tcpFieldDstPort:
+		length = 2
+	case tcpFieldSeq:
+		length = 4
+	case tcpFieldAck:
+		length = 4
+	case tcpFieldDataOffset:
+		length = 1
+	case tcpFieldWindow:
+		length = 2
+	case tcpFieldChecksum:
+		length = 2
+	case tcpFieldUrgent:
+		length = 2
+	default:
+		return nil, errors.Tracef("invalid field name: %s", fieldName)
+	}
+
+	var decodedValue []byte
+
+	switch transformationType {
+	case transformationTypeRandom:
+	case transformationTypeValue:
+		var err error
+		decodedValue, err = hex.DecodeString(valueSpec)
+		if err == nil && len(decodedValue) != length {
+			err = fmt.Errorf("invalid value length: %d", len(decodedValue))
+		}
+		if err != nil {
+			return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
+		}
+	default:
+		return nil, errors.Tracef("invalid transformation type")
+	}
+
+	return &transformationTCPField{
+		fieldName:          fieldName,
+		transformationType: transformationType,
+		value:              decodedValue,
+	}, nil
+}
+
+func (t *transformationTCPField) apply(tcp *layers.TCP, _ *gopacket.Payload) {
+
+	var value [4]byte
+
+	if t.transformationType == transformationTypeRandom {
+		_, _ = prng.Read(value[:])
+	} else {
+		copy(value[:], t.value)
+	}
+
+	switch t.fieldName {
+	case tcpFieldSrcPort:
+		tcp.SrcPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
+	case tcpFieldDstPort:
+		tcp.DstPort = layers.TCPPort(binary.BigEndian.Uint16(value[:]))
+	case tcpFieldSeq:
+		tcp.Seq = binary.BigEndian.Uint32(value[:])
+	case tcpFieldAck:
+		tcp.Ack = binary.BigEndian.Uint32(value[:])
+	case tcpFieldDataOffset:
+		tcp.DataOffset = value[0]
+		// DataOffset is a 4-bit field; the most significant 4 bits are ignored
+		tcp.DataOffset &= 0x0f
+	case tcpFieldWindow:
+		// Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/tcp_layer.py#L117-L121
+		tcp.Window = binary.BigEndian.Uint16(value[:])
+	case tcpFieldChecksum:
+		tcp.Checksum = binary.BigEndian.Uint16(value[:])
+	case tcpFieldUrgent:
+		tcp.Urgent = binary.BigEndian.Uint16(value[:])
+	}
+}
+
+func (t *transformationTCPField) setsCalculatedField() bool {
+	return t.fieldName == tcpFieldDataOffset || t.fieldName == tcpFieldChecksum
+}
+
+type transformationTCPOption struct {
+	optionName         string
+	transformationType int
+	value              []byte
+}
+
+const (
+	tcpOptionEOL             = "eol"
+	tcpOptionNOP             = "nop"
+	tcpOptionMSS             = "mss"
+	tcpOptionWindowScale     = "windowscale"
+	tcpOptionSACKPermitted   = "sackpermitted"
+	tcpOptionSACK            = "sack"
+	tcpOptionTimestamps      = "timestamps"
+	tcpOptionAltChecksum     = "altchecksum"
+	tcpOptionAltChecksumData = "altchecksumdata"
+	tcpOptionMD5Header       = "md5header"
+	tcpOptionUserTimeout     = "usertimeout"
+)
+
+func tcpOptionInfo(optionName string) (layers.TCPOptionKind, []int, bool) {
+
+	var kind layers.TCPOptionKind
+	var validLengths []int
+	switch optionName {
+	case tcpOptionEOL:
+		kind = layers.TCPOptionKindEndList
+		validLengths = nil // no option length field
+	case tcpOptionNOP:
+		kind = layers.TCPOptionKindNop
+		validLengths = nil
+	case tcpOptionMSS:
+		kind = layers.TCPOptionKindMSS
+		validLengths = []int{2}
+	case tcpOptionWindowScale:
+		kind = layers.TCPOptionKindWindowScale
+		validLengths = []int{1}
+	case tcpOptionSACKPermitted:
+		kind = layers.TCPOptionKindSACKPermitted
+		validLengths = []int{0}
+	case tcpOptionSACK:
+		// https://tools.ietf.org/html/rfc2018
+		kind = layers.TCPOptionKindSACK
+		validLengths = []int{8, 16, 24, 32}
+	case tcpOptionTimestamps:
+		kind = layers.TCPOptionKindTimestamps
+		validLengths = []int{8}
+	case tcpOptionAltChecksum:
+		kind = layers.TCPOptionKindAltChecksum
+		validLengths = []int{1}
+	case tcpOptionAltChecksumData:
+		// https://tools.ietf.org/html/rfc1145:
+		// "this field is used only when the alternate checksum that is negotiated is longer than 16 bits"
+		//
+		// Geneva allows setting length 0.
+		kind = layers.TCPOptionKindAltChecksumData
+		validLengths = []int{0, 4}
+	case tcpOptionMD5Header:
+		// https://tools.ietf.org/html/rfc2385
+		kind = layers.TCPOptionKind(19)
+		validLengths = []int{16}
+	case tcpOptionUserTimeout:
+		// https://tools.ietf.org/html/rfc5482
+		kind = layers.TCPOptionKind(28)
+		validLengths = []int{2}
+	default:
+		return kind, nil, false
+	}
+	return kind, validLengths, true
+}
+
+func newTransformationTCPOption(
+	optionName string, transformationType int, valueSpec string) (*transformationTCPOption, error) {
+
+	_, validLengths, ok := tcpOptionInfo(optionName)
+	if !ok {
+		return nil, errors.Tracef("invalid option name: %s", optionName)
+	}
+
+	var decodedValue []byte
+
+	switch transformationType {
+	case transformationTypeOmit:
+	case transformationTypeRandom:
+	case transformationTypeValue:
+		var err error
+		decodedValue, err = hex.DecodeString(valueSpec)
+		if err == nil {
+			if validLengths == nil {
+				validLengths = []int{0}
+			}
+			if !common.ContainsInt(validLengths, len(decodedValue)) {
+				err = fmt.Errorf("invalid value length: %d", len(decodedValue))
+			}
+		}
+		if err != nil {
+			return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
+		}
+	default:
+		return nil, errors.Tracef("invalid transformation type")
+	}
+
+	return &transformationTCPOption{
+		optionName:         optionName,
+		transformationType: transformationType,
+		value:              decodedValue,
+	}, nil
+}
+
+func (t *transformationTCPOption) apply(tcp *layers.TCP, _ *gopacket.Payload) {
+
+	// This transformation makes a copy of all existing TCPOption structs, so
+	// transformed option slices are not shared between multiple packets.
+	//
+	// All existing options are retained in the existing order. Modified options
+	// are overwritten in place. New options are appended to the end of the
+	// option list.
+	//
+	// Total option set size is not tracked or validated and the DataOffset TCP
+	// field can overflow.
+	//
+	// Limitations:
+	// - Inserting an option at a specific position is not supported.
+	// - OptionLengths cannot be set to arbitrary values.
+	// - Each option transformation executes a full copy of the existing option
+	//   list, which is not efficient for a long list of option transformations.
+
+	kind, validLengths, _ := tcpOptionInfo(t.optionName)
+
+	var options []layers.TCPOption
+
+	// The for loop iterates over all existing options plus one additional
+	// iteration, copying or modifying existing options and then appending a new
+	// option if required. This flag ensures that we don't both modify and append
+	// a new option.
+	applied := false
+
+	for i := 0; i <= len(tcp.Options); i++ {
+
+		if i < len(tcp.Options) {
+			option := tcp.Options[i]
+			if option.OptionType != kind {
+				options = append(options, layers.TCPOption{
+					OptionType:   option.OptionType,
+					OptionLength: option.OptionLength,
+					OptionData:   append([]byte(nil), option.OptionData...),
+				})
+				continue
+			}
+		} else if applied {
+			// Skip the append iteration if we already applied the transformation to an
+			// existing option.
+			continue
+		}
+
+		// TCP options with validLengths == nil have only the "kind" byte and total
+		// length 1. Options with validLengths have the "kind" byte, the "length"
+		// byte, and 0 or more data bytes; in this case, "length" is 2 + the length
+		// of the data.
+
+		switch t.transformationType {
+
+		case transformationTypeOmit:
+			continue
+
+		case transformationTypeRandom:
+			if validLengths == nil {
+				options = append(options, layers.TCPOption{
+					OptionType:   kind,
+					OptionLength: 1,
+				})
+			} else {
+				length := validLengths[prng.Range(0, len(validLengths)-1)]
+				var data []byte
+				if length > 0 {
+					data = prng.Bytes(length)
+				}
+				options = append(options, layers.TCPOption{
+					OptionType:   kind,
+					OptionLength: 2 + uint8(length),
+					OptionData:   data,
+				})
+			}
+			applied = true
+
+		case transformationTypeValue:
+			if validLengths == nil {
+				options = append(options, layers.TCPOption{
+					OptionType:   kind,
+					OptionLength: 1,
+				})
+			} else {
+				length := len(t.value)
+				var data []byte
+				if length > 0 {
+					data = append([]byte(nil), t.value...)
+				}
+				options = append(options, layers.TCPOption{
+					OptionType:   kind,
+					OptionLength: 2 + uint8(length),
+					OptionData:   data,
+				})
+			}
+			applied = true
+		}
+	}
+
+	tcp.Options = options
+}
+
+func (t *transformationTCPOption) setsCalculatedField() bool {
+	return false
+}
+
+type transformationTCPPayload struct {
+	transformationType int
+	value              []byte
+}
+
+func newTransformationTCPPayload(
+	transformationType int, valueSpec string) (*transformationTCPPayload, error) {
+
+	var decodedValue []byte
+
+	switch transformationType {
+	case transformationTypeOmit:
+	case transformationTypeRandom:
+	case transformationTypeValue:
+		var err error
+		decodedValue, err = hex.DecodeString(valueSpec)
+		if err != nil {
+			return nil, errors.Tracef("invalid value spec: %s: %v", valueSpec, err)
+		}
+	default:
+		return nil, errors.Tracef("invalid transformation type")
+	}
+
+	return &transformationTCPPayload{
+		transformationType: transformationType,
+		value:              decodedValue,
+	}, nil
+}
+
+func (t *transformationTCPPayload) apply(tcp *layers.TCP, payload *gopacket.Payload) {
+
+	var value []byte
+
+	switch t.transformationType {
+	case transformationTypeOmit:
+
+	case transformationTypeRandom:
+		// Differs from Geneva: https://github.com/Kkevsterrr/geneva/blob/de6823ba7723582054d2047083262cabffa85f36/layers/layer.py#L191-L197
+		value = prng.Bytes(prng.Range(1, 200))
+
+	case transformationTypeValue:
+		value = t.value
+	}
+
+	if value == nil {
+		// Omit the payload.
+		*payload = nil
+	} else {
+		// Change the payload.
+		*payload = append([]byte(nil), value...)
+	}
+}
+
+func (t *transformationTCPPayload) setsCalculatedField() bool {
+	return false
+}
+
+func stripEOLOption(packet gopacket.Packet) {
+
+	// gopacket.NewPacket appears to decode padding (0s) as an explicit EOL
+	// option (value 0) at the end of the option list. This helper strips that
+	// option, allowing append-option transformations to work as expected.
+	// gopacket TCP serialization will re-add padding as required.
+
+	tcpLayer := packet.Layer(layers.LayerTypeTCP).(*layers.TCP)
+	if len(tcpLayer.Options) > 0 &&
+		tcpLayer.Options[len(tcpLayer.Options)-1].OptionType == layers.TCPOptionKindEndList {
+		tcpLayer.Options = tcpLayer.Options[:len(tcpLayer.Options)-1]
+	}
+}

+ 690 - 0
psiphon/common/packetman/packetman_linux.go

@@ -0,0 +1,690 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package packetman
+
+import (
+	"context"
+	"encoding/binary"
+	"log"
+	"net"
+	"strconv"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+	"github.com/florianl/go-nfqueue"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	cache "github.com/patrickmn/go-cache"
+)
+
+func IsSupported() bool {
+	return true
+}
+
+const (
+	netlinkSocketIOTimeout = 10 * time.Millisecond
+	defaultSocketMark      = 0x70736970 // "PSIP"
+	appliedSpecCacheTTL    = 1 * time.Minute
+)
+
+// Manipulator is a SYN-ACK packet manipulator.
+//
+// NFQUEUE/Netlink is used to intercept SYN-ACK packets, on all local
+// interfaces, with source port equal to one of the ProtocolPorts specified in
+// Config. For each intercepted SYN-ACK packet, the SelectSpecName callback in
+// Config is invoked; the callback determines which packet transformation spec
+// to apply, based on, for example, client GeoIP, protocol, or other
+// considerations.
+//
+// Protocol network listeners use GetAppliedSpecName to determine which
+// transformation spec was applied to a given accepted connection.
+//
+// When a manipulations are to be applied to a SYN-ACK packet, NFQUEUE is
+// instructed to drop the packet and one or more new packets, created by
+// applying transformations to the original SYN-ACK packet, are injected via
+// raw sockets. Raw sockets are used as NFQUEUE supports only replacing the
+// original packet with one alternative packet.
+//
+// To avoid an intercept loop, injected packets are marked (SO_MARK) and the
+// filter for NFQUEUE excludes packets with this mark.
+//
+// To avoid breaking TCP in unexpected cases, Manipulator fails open --
+// allowing the original packet to proceed -- when packet parsing fails. For
+// the same reason, the queue-bypass NFQUEUE option is set.
+//
+// As an iptables filter ensures only SYN-ACK packets are sent to the
+// NFQUEUEs, the overhead of packet interception, parsing, and injection is
+// incurred no more than once per TCP connection.
+//
+// NFQUEUE with queue-bypass requires Linux kernel 2.6.39; 3.16 or later is
+// validated and recommended.
+type Manipulator struct {
+	config             *Config
+	mutex              sync.Mutex
+	runContext         context.Context
+	stopRunning        context.CancelFunc
+	waitGroup          *sync.WaitGroup
+	injectIPv4FD       int
+	injectIPv6FD       int
+	nfqueue            *nfqueue.Nfqueue
+	compiledSpecsMutex sync.Mutex
+	compiledSpecs      map[string]*compiledSpec
+	appliedSpecCache   *cache.Cache
+}
+
+// NewManipulator creates a new Manipulator.
+func NewManipulator(config *Config) (*Manipulator, error) {
+
+	m := &Manipulator{
+		config: config,
+	}
+
+	err := m.SetSpecs(config.Specs)
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
+
+	// To avoid memory exhaustion, do not retain unconsumed appliedSpecCache
+	// entries for a longer time than it may reasonably take to complete the TCP
+	// handshake.
+	m.appliedSpecCache = cache.New(appliedSpecCacheTTL, appliedSpecCacheTTL/2)
+
+	return m, nil
+}
+
+// Start initializes NFQUEUEs and raw sockets for packet manipulation. Start
+// returns when initialization is complete; once it returns, the caller may
+// assume that any SYN-ACK packets on configured ports will be intercepted. In
+// the case of initialization failure, Start will undo any partial
+// initialization. When Start succeeds, the caller must call Stop to free
+// resources and restore networking state.
+func (m *Manipulator) Start() (retErr error) {
+
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+
+	if m.runContext != nil {
+		return errors.TraceNew("already running")
+	}
+
+	err := m.configureIPTables(true)
+	if err != nil {
+		return errors.Trace(err)
+	}
+	defer func() {
+		if retErr != nil {
+			m.configureIPTables(false)
+		}
+	}()
+
+	m.injectIPv4FD, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
+	if err != nil {
+		return errors.Trace(err)
+	}
+	defer func() {
+		if retErr != nil {
+			syscall.Close(m.injectIPv4FD)
+		}
+	}()
+
+	err = syscall.SetsockoptInt(m.injectIPv4FD, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	err = syscall.SetsockoptInt(m.injectIPv4FD, syscall.SOL_SOCKET, syscall.SO_MARK, m.getSocketMark())
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	m.injectIPv6FD, err = syscall.Socket(syscall.AF_INET6, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
+	if err != nil && !m.config.AllowNoIPv6NetworkConfiguration {
+		return errors.Trace(err)
+	}
+	defer func() {
+		if retErr != nil {
+			syscall.Close(m.injectIPv6FD)
+		}
+	}()
+
+	if m.injectIPv6FD != 0 {
+		err = syscall.SetsockoptInt(m.injectIPv6FD, syscall.IPPROTO_IPV6, syscall.IP_HDRINCL, 1)
+		if err != nil {
+			// There's no AllowNoIPv6NetworkConfiguration in this case: if we can
+			// create an IPv6 socket, we must be able to set its options.
+			return errors.Trace(err)
+		}
+
+		err = syscall.SetsockoptInt(m.injectIPv6FD, syscall.SOL_SOCKET, syscall.SO_MARK, m.getSocketMark())
+		if err != nil {
+			return errors.Trace(err)
+		}
+	}
+
+	// Use a reasonable buffer size to avoid excess allocation. As we're
+	// intercepting only locally generated SYN-ACK packets, which should have no
+	// payload, this size should be more than sufficient.
+	maxPacketLen := uint32(1500)
+
+	// Use the kernel default of 1024:
+	// https://github.com/torvalds/linux/blob/cd8dead0c39457e58ec1d36db93aedca811d48f1/net/netfilter/nfnetlink_queue.c#L51,
+	// via https://github.com/florianl/go-nfqueue/issues/3.
+	maxQueueLen := uint32(1024)
+
+	// Note: runContext alone is not sufficient to interrupt the
+	// nfqueue.socketCallback goroutine spawned by nfqueue.Register; timeouts
+	// must be set. See comment in Manipulator.Stop.
+
+	m.nfqueue, err = nfqueue.Open(
+		&nfqueue.Config{
+			NfQueue:      uint16(m.config.QueueNumber),
+			MaxPacketLen: maxPacketLen,
+			MaxQueueLen:  maxQueueLen,
+			Copymode:     nfqueue.NfQnlCopyPacket,
+			Logger:       newNfqueueLogger(m.config.Logger),
+			ReadTimeout:  netlinkSocketIOTimeout,
+			WriteTimeout: netlinkSocketIOTimeout,
+		})
+	if err != nil {
+		return errors.Trace(err)
+	}
+	defer func() {
+		if retErr != nil {
+			m.nfqueue.Close()
+		}
+	}()
+
+	runContext, stopRunning := context.WithCancel(context.Background())
+	defer func() {
+		if retErr != nil {
+			stopRunning()
+		}
+	}()
+
+	err = m.nfqueue.Register(runContext, m.handleInterceptedPacket)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	m.runContext = runContext
+	m.stopRunning = stopRunning
+
+	return nil
+}
+
+// Stop halts packet manipulation, frees resources, and restores networking
+// state.
+func (m *Manipulator) Stop() {
+
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+
+	if m.runContext == nil {
+		return
+	}
+
+	m.stopRunning()
+
+	// stopRunning will cancel the context passed into nfqueue.Register. The
+	// goroutine spawned by Register, nfqueue.socketCallback, polls the context
+	// after a read timeout:
+	// https://github.com/florianl/go-nfqueue/blob/1e38df738c06deffbac08da8fec4b7c28a69b918/nfqueue_gteq_1.12.go#L138-L146
+	//
+	// There's no stop synchronization exposed by nfqueue. Calling nfqueue.Close
+	// while socketCallback is still running can result in errors such as
+	// "nfqueuenfqueue_gteq_1.12.go:134: Could not unbind from queue: netlink
+	// send: sendmsg: bad file descriptor".
+	//
+	// To avoid invalid file descriptor operations and spurious error messages,
+	// sleep for two polling periods, which should be sufficient, in most cases,
+	// for socketCallback to poll the context and exit.
+
+	time.Sleep(2 * netlinkSocketIOTimeout)
+
+	m.nfqueue.Close()
+
+	syscall.Close(m.injectIPv4FD)
+
+	if m.injectIPv6FD != 0 {
+		syscall.Close(m.injectIPv6FD)
+	}
+
+	m.configureIPTables(false)
+}
+
+// SetSpecs installs a new set of packet transformation Spec values, replacing
+// the initial specs from Config.Specs, or any previous SetSpecs call. When
+// SetSpecs returns an error, the previous set of specs is retained.
+func (m *Manipulator) SetSpecs(specs []*Spec) error {
+
+	compiledSpecs := make(map[string]*compiledSpec)
+	for _, spec := range specs {
+		if spec.Name == "" {
+			return errors.TraceNew("invalid spec name")
+		}
+		if _, ok := compiledSpecs[spec.Name]; ok {
+			return errors.TraceNew("duplicate spec name")
+		}
+		compiledSpec, err := compileSpec(spec)
+		if err != nil {
+			return errors.Trace(err)
+		}
+		compiledSpecs[spec.Name] = compiledSpec
+	}
+
+	m.compiledSpecsMutex.Lock()
+	m.compiledSpecs = compiledSpecs
+	m.compiledSpecsMutex.Unlock()
+
+	return nil
+}
+
+func makeConnectionID(
+	srcIP net.IP, srcPort uint16, dstIP net.IP, dstPort uint16) string {
+
+	// Create a unique connection ID, for appliedSpecCache, from the 4-tuple
+	// srcIP, dstIP, srcPort, dstPort. In the SYN/ACK context, src is the server
+	// and dst is the client.
+	//
+	// Limitation: there may be many repeat connections from one dstIP,
+	// especially if many clients are behind the same NAT. Each TCP connection
+	// will have a distinct dstPort. In principle, there remains a race between
+	// populating appliedSpecCache, the TCP connection terminating on the
+	// client-side and the NAT reusing the dstPort, and consuming
+	// appliedSpecCache.
+
+	// From: https://github.com/golang/go/blob/b88efc7e7ac15f9e0b5d8d9c82f870294f6a3839/src/net/ip.go#L55
+	var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
+	const uint16Len = 2
+
+	var connID [net.IPv6len + uint16Len + net.IPv6len + uint16Len]byte
+
+	offset := 0
+	if len(srcIP) == net.IPv4len {
+		copy(connID[offset:], v4InV6Prefix)
+		offset += len(v4InV6Prefix)
+		copy(connID[offset:], srcIP)
+		offset += len(srcIP)
+	} else { // net.IPv6len
+		copy(connID[offset:], srcIP)
+		offset += len(srcIP)
+	}
+	binary.BigEndian.PutUint16(connID[offset:], srcPort)
+	offset += uint16Len
+
+	if len(dstIP) == net.IPv4len {
+		copy(connID[offset:], v4InV6Prefix)
+		offset += len(v4InV6Prefix)
+		copy(connID[offset:], dstIP)
+		offset += len(dstIP)
+	} else { // net.IPv6len
+		copy(connID[offset:], dstIP)
+		offset += len(dstIP)
+	}
+	binary.BigEndian.PutUint16(connID[offset:], dstPort)
+	offset += uint16Len
+
+	return string(connID[:])
+}
+
+// GetAppliedSpecName returns the packet manipulation spec name applied to the
+// TCP connection, represented by its local and remote address components,
+// that was ultimately accepted by a network listener.
+//
+// This allows SelectSpecName, the spec selector, to be non-deterministic
+// while also allowing for accurate packet manipulation metrics to be
+// associated with each TCP connection.
+//
+// For a given connection, GetAppliedSpecName must be called before a TTL
+// clears the stored value. Calling GetAppliedSpecName immediately clears the
+// stored value for the given connection.
+//
+// To obtain the correct result GetAppliedSpecName must be called with a
+// RemoteAddr which reflects the true immediate network peer address. In
+// particular, for proxied net.Conns which present a synthetic RemoteAddr with
+// the original address of a proxied client (e.g., armon/go-proxyproto, or
+// psiphon/server.meekConn) the true peer RemoteAddr must instead be
+// provided.
+func (m *Manipulator) GetAppliedSpecName(
+	localAddr, remoteAddr *net.TCPAddr) (string, error) {
+
+	connID := makeConnectionID(
+		localAddr.IP,
+		uint16(localAddr.Port),
+		remoteAddr.IP,
+		uint16(remoteAddr.Port))
+
+	specName, found := m.appliedSpecCache.Get(connID)
+	if !found {
+		return "", errors.TraceNew("connection not found")
+	}
+
+	m.appliedSpecCache.Delete(connID)
+
+	return specName.(string), nil
+}
+
+func (m *Manipulator) setAppliedSpecName(
+	interceptedPacket gopacket.Packet, specName string) {
+
+	srcIP, dstIP, _, _ := m.getPacketAddressInfo(interceptedPacket)
+
+	interceptedTCP := interceptedPacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
+
+	connID := makeConnectionID(
+		srcIP,
+		uint16(interceptedTCP.SrcPort),
+		dstIP,
+		uint16(interceptedTCP.DstPort))
+
+	m.appliedSpecCache.Set(connID, specName, cache.DefaultExpiration)
+}
+
+func (m *Manipulator) getSocketMark() int {
+	if m.config.SocketMark == 0 {
+		return defaultSocketMark
+	}
+	return m.config.SocketMark
+}
+
+func (m *Manipulator) handleInterceptedPacket(attr nfqueue.Attribute) int {
+
+	if attr.PacketID == nil || attr.Payload == nil {
+		m.config.Logger.WithTrace().Warning("missing nfqueue data")
+		return 0
+	}
+
+	// Trigger packet manipulation only if the packet is a SYN-ACK and has no
+	// payload (which a transformation _may_ discard). The iptables filter for
+	// NFQUEUE should already ensure that only SYN-ACK packets are sent through
+	// the queue. To avoid breaking all TCP connections in an unanticipated case,
+	// fail open -- allow the packet -- if these conditions are not met or if
+	// parsing the packet fails.
+
+	packet, err := m.parseInterceptedPacket(*attr.Payload)
+	if err != nil {
+
+		// Fail open in this case.
+		m.nfqueue.SetVerdict(*attr.PacketID, nfqueue.NfAccept)
+
+		m.config.Logger.WithTraceFields(
+			common.LogFields{"error": err}).Warning("unexpected packet")
+		return 0
+	}
+
+	spec, err := m.getCompiledSpec(packet)
+	if err != nil {
+
+		// Fail open in this case.
+		m.nfqueue.SetVerdict(*attr.PacketID, nfqueue.NfAccept)
+
+		m.config.Logger.WithTraceFields(
+			common.LogFields{"error": err}).Warning("get strategy failed")
+		return 0
+	}
+
+	// Call setAppliedSpecName cache _before_ accepting the packet or injecting
+	// manipulated packets to avoid a potential race in which the TCP handshake
+	// completes and GetAppliedSpecName is called before the cache is populated.
+
+	if spec == nil {
+
+		// No packet manipulation in this case.
+		m.setAppliedSpecName(packet, "")
+		m.nfqueue.SetVerdict(*attr.PacketID, nfqueue.NfAccept)
+		return 0
+	}
+
+	m.setAppliedSpecName(packet, spec.name)
+	m.nfqueue.SetVerdict(*attr.PacketID, nfqueue.NfDrop)
+
+	err = m.injectPackets(packet, spec)
+	if err != nil {
+		m.config.Logger.WithTraceFields(
+			common.LogFields{"error": err}).Warning("inject packets failed")
+		return 0
+	}
+
+	return 0
+}
+
+func (m *Manipulator) parseInterceptedPacket(packetData []byte) (gopacket.Packet, error) {
+
+	// Note that NFQUEUE doesn't send an Ethernet layer. This first layer is
+	// either IPv4 or IPv6.
+	//
+	// As we parse only one packet per TCP connection, we are not using the
+	// faster DecodingLayerParser API,
+	// https://godoc.org/github.com/google/gopacket#hdr-Fast_Decoding_With_DecodingLayerParser,
+	// or zero-copy approaches.
+	//
+	// TODO: use a stub gopacket.Decoder as the first layer to avoid the extra
+	// NewPacket call? Use distinct NFQUEUE queue numbers and nfqueue instances
+	// for IPv4 and IPv6?
+
+	packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)
+
+	if packet.ErrorLayer() != nil {
+		packet = gopacket.NewPacket(packetData, layers.LayerTypeIPv6, gopacket.Default)
+	}
+
+	errLayer := packet.ErrorLayer()
+	if errLayer != nil {
+		return nil, errors.Trace(errLayer.Error())
+	}
+
+	// After this check, Layer([IPv4,IPv6]/TCP) return values are assumed to be
+	// non-nil and unchecked layer type assertions are assumed safe.
+
+	tcpLayer := packet.Layer(layers.LayerTypeTCP)
+	if tcpLayer == nil {
+		return nil, errors.TraceNew("missing TCP layer")
+	}
+
+	if packet.Layer(gopacket.LayerTypePayload) != nil {
+		return nil, errors.TraceNew("unexpected payload layer")
+	}
+
+	tcp := tcpLayer.(*layers.TCP)
+
+	// Any of the ECN TCP flags (https://tools.ietf.org/html/rfc3168 and
+	// rfc3540), ECE/CWR/NS, may be set in a SYN-ACK, and are retained.
+	//
+	// Limitation: these additional flags are retained as-is on injected packets
+	// only when no TCP flag transformation is applied.
+
+	if !tcp.SYN || !tcp.ACK ||
+		tcp.FIN || tcp.RST || tcp.PSH || tcp.URG {
+		return nil, errors.TraceNew("unexpected TCP flags")
+	}
+
+	stripEOLOption(packet)
+
+	return packet, nil
+}
+
+func (m *Manipulator) getCompiledSpec(interceptedPacket gopacket.Packet) (*compiledSpec, error) {
+
+	_, dstIP, _, _ := m.getPacketAddressInfo(interceptedPacket)
+
+	interceptedTCP := interceptedPacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
+
+	protocolPort := interceptedTCP.SrcPort
+	clientIP := dstIP
+
+	specName := m.config.SelectSpecName(int(protocolPort), clientIP)
+	if specName == "" {
+		return nil, nil
+	}
+
+	// Concurrency note: m.compiledSpecs may be replaced by SetSpecs, but any
+	// reference to an individual compiledSpec remains valid; each compiledSpec
+	// is read-only.
+
+	m.compiledSpecsMutex.Lock()
+	spec, ok := m.compiledSpecs[specName]
+	m.compiledSpecsMutex.Unlock()
+
+	if !ok {
+		return nil, errors.Tracef("invalid spec name: %s", specName)
+	}
+
+	return spec, nil
+}
+
+func (m *Manipulator) injectPackets(interceptedPacket gopacket.Packet, spec *compiledSpec) error {
+
+	// A sockAddr parameter with dstIP (but not port) set appears to be required
+	// even with the IP_HDRINCL socket option.
+
+	_, _, injectFD, sockAddr := m.getPacketAddressInfo(interceptedPacket)
+
+	injectPackets, err := spec.apply(interceptedPacket)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	for _, injectPacket := range injectPackets {
+
+		err = syscall.Sendto(injectFD, injectPacket, 0, sockAddr)
+		if err != nil {
+			return errors.Trace(err)
+		}
+	}
+
+	return nil
+}
+
+func (m *Manipulator) getPacketAddressInfo(interceptedPacket gopacket.Packet) (net.IP, net.IP, int, syscall.Sockaddr) {
+
+	var srcIP, dstIP net.IP
+	var injectFD int
+	var sockAddr syscall.Sockaddr
+
+	ipv4Layer := interceptedPacket.Layer(layers.LayerTypeIPv4)
+	if ipv4Layer != nil {
+		interceptedIPv4 := ipv4Layer.(*layers.IPv4)
+		srcIP = interceptedIPv4.SrcIP
+		dstIP = interceptedIPv4.DstIP
+		injectFD = m.injectIPv4FD
+		var ipv4 [4]byte
+		copy(ipv4[:], interceptedIPv4.DstIP.To4())
+		sockAddr = &syscall.SockaddrInet4{Addr: ipv4, Port: 0}
+	} else {
+		interceptedIPv6 := interceptedPacket.Layer(layers.LayerTypeIPv6).(*layers.IPv6)
+		srcIP = interceptedIPv6.SrcIP
+		dstIP = interceptedIPv6.DstIP
+		injectFD = m.injectIPv6FD
+		var ipv6 [16]byte
+		copy(ipv6[:], interceptedIPv6.DstIP.To16())
+		sockAddr = &syscall.SockaddrInet6{Addr: ipv6, Port: 0}
+	}
+
+	return srcIP, dstIP, injectFD, sockAddr
+}
+
+func (m *Manipulator) configureIPTables(addRules bool) error {
+
+	execCommands := func(mode string) error {
+
+		ports := make([]string, len(m.config.ProtocolPorts))
+		for i, port := range m.config.ProtocolPorts {
+			ports[i] = strconv.Itoa(port)
+		}
+
+		socketMark := strconv.Itoa(m.getSocketMark())
+
+		args := []string{
+			mode, "OUTPUT",
+			"--protocol", "tcp",
+			"--match", "multiport",
+			"--source-ports", strings.Join(ports, ","),
+			"--match", "mark",
+			"!", "--mark", socketMark,
+			"--tcp-flags", "ALL", "SYN,ACK",
+			"-j", "NFQUEUE",
+			"--queue-bypass",
+			"--queue-num", strconv.Itoa(m.config.QueueNumber),
+		}
+
+		err := common.RunNetworkConfigCommand(
+			m.config.Logger,
+			m.config.SudoNetworkConfigCommands,
+			"iptables",
+			args...)
+		if mode != "-D" && err != nil {
+			return errors.Trace(err)
+		}
+
+		err = common.RunNetworkConfigCommand(
+			m.config.Logger,
+			m.config.SudoNetworkConfigCommands,
+			"ip6tables",
+			args...)
+		if mode != "-D" && err != nil {
+			if m.config.AllowNoIPv6NetworkConfiguration {
+				m.config.Logger.WithTraceFields(
+					common.LogFields{
+						"error": err}).Warning(
+					"configure IPv6 NFQUEUE failed")
+			} else {
+				return errors.Trace(err)
+			}
+		}
+
+		return nil
+	}
+
+	// To avoid duplicates, first try to drop existing rules, then add. Also try
+	// to revert any partial configuration in the case of an error.
+
+	_ = execCommands("-D")
+
+	if addRules {
+		err := execCommands("-I")
+		if err != nil {
+			_ = execCommands("-D")
+		}
+		return errors.Trace(err)
+	}
+
+	return nil
+}
+
+func newNfqueueLogger(logger common.Logger) *log.Logger {
+	return log.New(
+		&nfqueueLoggerWriter{logger: logger},
+		"nfqueue",
+		log.Lshortfile)
+}
+
+type nfqueueLoggerWriter struct {
+	logger common.Logger
+}
+
+func (n *nfqueueLoggerWriter) Write(p []byte) (int, error) {
+	n.logger.WithTraceFields(
+		common.LogFields{"log": string(p)}).Warning("nfqueue log")
+	return len(p), nil
+}

+ 201 - 0
psiphon/common/packetman/packetman_linux_test.go

@@ -0,0 +1,201 @@
+// +build PACKET_MANIPULATOR_TEST
+
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package packetman
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"strconv"
+	"testing"
+	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/stacktrace"
+)
+
+func TestPacketManipulatorIPv4(t *testing.T) {
+	testPacketManipulator(false, t)
+}
+
+func TestPacketManipulatorIPv6(t *testing.T) {
+	testPacketManipulator(true, t)
+}
+
+func testPacketManipulator(useIPv6 bool, t *testing.T) {
+
+	// Test: run a Manipulator in front of a web server; make an HTTP request;
+	// the expected transformation spec should be executed (as reported by
+	// GetAppliedSpecName) and the request must succeed.
+
+	ipv4, ipv6, err := common.GetRoutableInterfaceIPAddresses()
+	if err != nil {
+		t.Fatalf("GetRoutableInterfaceIPAddressesfailed: %v", err)
+	}
+
+	network := "tcp4"
+	address := net.JoinHostPort(ipv4.String(), "0")
+	if useIPv6 {
+		network = "tcp6"
+		address = net.JoinHostPort(ipv6.String(), "0")
+	}
+
+	listener, err := net.Listen(network, address)
+	if err != nil {
+		t.Fatalf("net.Listen failed: %v", err)
+	}
+	defer listener.Close()
+
+	hostStr, portStr, err := net.SplitHostPort(listener.Addr().String())
+	if err != nil {
+		t.Fatalf("net.SplitHostPort failed: %s", err.Error())
+	}
+	listenerPort, _ := strconv.Atoi(portStr)
+
+	// [["TCP-flags S"]] replaces the original SYN-ACK packet with a single
+	// SYN packet, implementing TCP simultaneous open.
+
+	testSpecName := "test-spec"
+	config := &Config{
+		Logger:        newTestLogger(),
+		ProtocolPorts: []int{listenerPort},
+		Specs:         []*Spec{&Spec{Name: testSpecName, PacketSpecs: [][]string{[]string{"TCP-flags S"}}}},
+		SelectSpecName: func(protocolPort int, _ net.IP) string {
+			if protocolPort == listenerPort {
+				return testSpecName
+			}
+			return ""
+		},
+		QueueNumber: 1,
+	}
+
+	m, err := NewManipulator(config)
+	if err != nil {
+		t.Fatalf("NewManipulator failed: %v", err)
+	}
+
+	err = m.Start()
+	if err != nil {
+		t.Fatalf("Manipulator.Start failed: %v", err)
+	}
+	defer m.Stop()
+
+	go func() {
+		serveMux := http.NewServeMux()
+		serveMux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
+			io.WriteString(w, "test-response\n")
+		})
+
+		server := &http.Server{
+			Handler: serveMux,
+			ConnState: func(conn net.Conn, state http.ConnState) {
+				if state == http.StateNew {
+					localAddr := conn.LocalAddr().(*net.TCPAddr)
+					remoteAddr := conn.RemoteAddr().(*net.TCPAddr)
+					specName, err := m.GetAppliedSpecName(localAddr, remoteAddr)
+					if err != nil {
+						t.Fatalf("GetAppliedSpecName failed: %v", err)
+					}
+					if specName != testSpecName {
+						t.Fatalf("unexpected spec name: %s", specName)
+					}
+				}
+			},
+		}
+
+		server.Serve(listener)
+	}()
+
+	httpClient := &http.Client{
+		Timeout: 30 * time.Second,
+	}
+
+	response, err := httpClient.Get(fmt.Sprintf("http://%s:%s", hostStr, portStr))
+	if err != nil {
+		t.Fatalf("http.Get failed: %v", err)
+	}
+	defer response.Body.Close()
+	_, err = ioutil.ReadAll(response.Body)
+	if err != nil {
+		t.Fatalf("ioutil.ReadAll failed: %v", err)
+	}
+
+	if response.StatusCode != http.StatusOK {
+		t.Fatalf("unexpected response code: %d", response.StatusCode)
+	}
+}
+
+func newTestLogger() common.Logger {
+	return &testLogger{}
+}
+
+type testLogger struct {
+}
+
+func (logger *testLogger) WithTrace() common.LogTrace {
+	return &testLogTrace{
+		trace: stacktrace.GetParentFunctionName(),
+	}
+}
+
+func (logger *testLogger) WithTraceFields(fields common.LogFields) common.LogTrace {
+	return &testLogTrace{
+		trace:  stacktrace.GetParentFunctionName(),
+		fields: fields,
+	}
+}
+
+func (logger *testLogger) LogMetric(metric string, fields common.LogFields) {
+}
+
+type testLogTrace struct {
+	trace  string
+	fields common.LogFields
+}
+
+func (log *testLogTrace) log(
+	noticeType string, args ...interface{}) {
+
+	fmt.Printf("[%s] %s: %+v: %s\n",
+		noticeType,
+		log.trace,
+		log.fields,
+		fmt.Sprint(args...))
+}
+
+func (log *testLogTrace) Debug(args ...interface{}) {
+	log.log("DEBUG", args...)
+}
+
+func (log *testLogTrace) Info(args ...interface{}) {
+	log.log("INFO", args...)
+}
+
+func (log *testLogTrace) Warning(args ...interface{}) {
+	log.log("ALERT", args...)
+}
+
+func (log *testLogTrace) Error(args ...interface{}) {
+	log.log("ERROR", args...)
+}

+ 277 - 0
psiphon/common/packetman/packetman_test.go

@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package packetman
+
+import (
+	"bytes"
+	"encoding/json"
+	"net"
+	"testing"
+
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+)
+
+func TestTransformations(t *testing.T) {
+
+	// Test: apply various transformations to an original packet, then parse the
+	// resulting packets and check that flags/fields/options are as expected.
+
+	// Limitation: gopacket, used here in the test to verify transformations,
+	// will fail to parse some or all of certain packets that can be created by
+	// certain transformations. gopacket will fail to parse packets with too many
+	// option bytes or invalid DataOffset values. gopacket will stop
+	// deserializing TCP options as soon as it encounters the EOL option, even if
+	// the packet actually contains more options. Etc.
+
+	specJSON := []byte(`
+    {
+        "Name": "test-spec",
+        "PacketSpecs": [
+            ["TCP-flags SA",
+             "TCP-flags S",
+             "TCP-srcport ffff",
+             "TCP-dstport ffff",
+             "TCP-seq ffffffff",
+             "TCP-ack ffffffff",
+             "TCP-dataoffset 0f",
+             "TCP-window ffff",
+             "TCP-checksum ffff",
+             "TCP-urgent ffff",
+             "TCP-option-nop omit",
+             "TCP-option-mss ffff",
+             "TCP-option-windowscale ff",
+             "TCP-option-sackpermitted ",
+	         "TCP-option-sack ffffffffffffffff",
+	         "TCP-option-timestamps ffffffffffffffff",
+             "TCP-payload eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+             "TCP-payload ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"],
+
+            ["TCP-flags random",
+             "TCP-srcport random",
+             "TCP-dstport random",
+             "TCP-seq random",
+             "TCP-ack random",
+             "TCP-dataoffset random",
+             "TCP-window random",
+             "TCP-checksum random",
+             "TCP-urgent random",
+             "TCP-option-mss random",
+             "TCP-option-windowscale random",
+             "TCP-option-sackpermitted random",
+             "TCP-option-timestamps random",
+             "TCP-payload random"]
+        ]
+    }
+	`)
+
+	var spec *Spec
+	err := json.Unmarshal(specJSON, &spec)
+	if err != nil {
+		t.Fatalf("json.Unmarshal failed: %v", err)
+	}
+
+	c, err := compileSpec(spec)
+	if err != nil {
+		t.Fatalf("compileSpec failed: %v", err)
+	}
+
+	if c.name != spec.Name {
+		t.Fatalf("unexpected compiled spec name: %s", c.name)
+	}
+
+	originalIPv4 := &layers.IPv4{
+		Version:  0x04,
+		IHL:      0x05,
+		Protocol: 0x06,
+		SrcIP:    net.IPv4(192, 168, 0, 1),
+		DstIP:    net.IPv4(192, 168, 0, 2),
+	}
+
+	originalTCP := &layers.TCP{
+		SYN: true,
+		ACK: true,
+		Options: []layers.TCPOption{
+			layers.TCPOption{OptionType: layers.TCPOptionKindNop, OptionLength: 1},
+			layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2},
+			layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)},
+			layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0}, 8)},
+		},
+	}
+
+	originalTCP.SetNetworkLayerForChecksum(originalIPv4)
+
+	originalPayload := gopacket.Payload([]byte{0, 0, 0, 0})
+
+	buffer := gopacket.NewSerializeBuffer()
+	gopacket.SerializeLayers(
+		buffer,
+		gopacket.SerializeOptions{FixLengths: true, ComputeChecksums: true},
+		originalIPv4,
+		originalTCP,
+		originalPayload)
+	originalPacketData := buffer.Bytes()
+
+	originalPacket := gopacket.NewPacket(originalPacketData, layers.LayerTypeIPv4, gopacket.Default)
+	errLayer := originalPacket.ErrorLayer()
+	if errLayer != nil {
+		t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error())
+	}
+
+	stripEOLOption(originalPacket)
+
+	repeats := 1000
+repeatLoop:
+	for i := 0; i < repeats; i++ {
+
+		lastRepeat := i == repeats-1
+
+		injectPackets, err := c.apply(originalPacket)
+		if err != nil {
+			t.Fatalf("apply failed: %v", err)
+		}
+
+		if len(injectPackets) != 2 {
+			t.Fatalf("unexpected injectPackets count: %d", len(injectPackets))
+		}
+
+		for packetNum, packetData := range injectPackets {
+
+			packet := gopacket.NewPacket(packetData, layers.LayerTypeIPv4, gopacket.Default)
+
+			errLayer := packet.ErrorLayer()
+			if errLayer != nil {
+				t.Fatalf("gopacket.NewPacket failed: %v", errLayer.Error())
+			}
+
+			tcpLayer := packet.Layer(layers.LayerTypeTCP)
+			if tcpLayer == nil {
+				t.Fatalf("missing TCP layer")
+			}
+
+			tcp := tcpLayer.(*layers.TCP)
+
+			payloadLayer := packet.Layer(gopacket.LayerTypePayload)
+			if payloadLayer == nil {
+				t.Fatalf("missing payload layer")
+			}
+
+			payload := payloadLayer.(*gopacket.Payload)
+
+			optionsEqual := func(a, b layers.TCPOption) bool {
+				if a.OptionType != b.OptionType ||
+					a.OptionLength != b.OptionLength ||
+					!bytes.Equal(a.OptionData, b.OptionData) {
+					return false
+				}
+				return true
+			}
+
+			optionsListEqual := func(a, b []layers.TCPOption) bool {
+				if len(a) != len(b) {
+					return false
+				}
+				for i, o := range a {
+					if !optionsEqual(o, b[i]) {
+						return false
+					}
+				}
+				return true
+			}
+
+			if packetNum == 0 {
+
+				// With multiple, redundant value specs (TCP-flags in the test case) the
+				// _last_ value spec should be applied. Values should be truncated to
+				// protocol lengths. The NOP option in the original packet should be
+				// omitted.
+
+				expectedOptions := []layers.TCPOption{
+					layers.TCPOption{OptionType: layers.TCPOptionKindSACKPermitted, OptionLength: 2},
+					layers.TCPOption{OptionType: layers.TCPOptionKindSACK, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)},
+					layers.TCPOption{OptionType: layers.TCPOptionKindTimestamps, OptionLength: 10, OptionData: bytes.Repeat([]byte{0xff}, 8)},
+					layers.TCPOption{OptionType: layers.TCPOptionKindMSS, OptionLength: 4, OptionData: bytes.Repeat([]byte{0xff}, 2)},
+					layers.TCPOption{OptionType: layers.TCPOptionKindWindowScale, OptionLength: 3, OptionData: bytes.Repeat([]byte{0xff}, 1)},
+					layers.TCPOption{OptionType: layers.TCPOptionKindEndList, OptionLength: 1},
+				}
+
+				if tcp.SrcPort != 0xffff ||
+					tcp.DstPort != 0xffff ||
+					tcp.Seq != 0xffffffff ||
+					tcp.Ack != 0xffffffff ||
+					tcp.FIN || !tcp.SYN || tcp.RST || tcp.PSH || tcp.ACK ||
+					tcp.URG || tcp.ECE || tcp.CWR || tcp.NS ||
+					tcp.Window != 0xffff ||
+					tcp.Urgent != 0xffff ||
+					!optionsListEqual(tcp.Options, expectedOptions) {
+					t.Fatalf("unexpected TCP layer: %+v", tcp)
+				}
+
+				expectedPayload := bytes.Repeat([]byte{0xff}, 32)
+				if !bytes.Equal(expectedPayload, *payload) {
+					t.Fatalf("unexpected payload: %x", *payload)
+				}
+
+			} else {
+
+				// In at least one repeat, randomized fields fully differ from original,
+				// including zero-values; original NOP and SACK options retained; random
+				// options have correct protocol lengths.
+
+				if tcp.SrcPort == originalTCP.SrcPort ||
+					tcp.DstPort == originalTCP.DstPort ||
+					tcp.Seq == originalTCP.Seq ||
+					tcp.Ack == originalTCP.Ack ||
+					(tcp.FIN == originalTCP.FIN &&
+						tcp.SYN == originalTCP.SYN &&
+						tcp.RST == originalTCP.RST &&
+						tcp.PSH == originalTCP.PSH &&
+						tcp.ACK == originalTCP.ACK &&
+						tcp.URG == originalTCP.URG &&
+						tcp.ECE == originalTCP.ECE &&
+						tcp.CWR == originalTCP.CWR &&
+						tcp.NS == originalTCP.NS) ||
+					tcp.Window == originalTCP.Window ||
+					tcp.Checksum == originalTCP.Checksum ||
+					tcp.Urgent == originalTCP.Urgent ||
+					len(tcp.Options) != 7 ||
+					!optionsEqual(tcp.Options[0], originalTCP.Options[0]) ||
+					!optionsEqual(tcp.Options[1], originalTCP.Options[1]) ||
+					!optionsEqual(tcp.Options[2], originalTCP.Options[2]) ||
+					tcp.Options[3].OptionType != layers.TCPOptionKindTimestamps ||
+					tcp.Options[3].OptionLength != 10 ||
+					optionsEqual(tcp.Options[3], originalTCP.Options[3]) ||
+					tcp.Options[4].OptionType != layers.TCPOptionKindMSS ||
+					tcp.Options[4].OptionLength != 4 ||
+					tcp.Options[5].OptionType != layers.TCPOptionKindWindowScale ||
+					tcp.Options[5].OptionLength != 3 ||
+					tcp.Options[6].OptionType != layers.TCPOptionKindEndList ||
+					bytes.Equal(originalPayload, *payload) {
+
+					if lastRepeat {
+						t.Fatalf("unexpected TCP layer: %+v", tcp)
+					}
+				} else {
+					break repeatLoop
+				}
+			}
+		}
+	}
+}

+ 57 - 0
psiphon/common/packetman/packetman_unsupported.go

@@ -0,0 +1,57 @@
+// +build !linux
+
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package packetman
+
+import (
+	std_errors "errors"
+	"net"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+)
+
+func IsSupported() bool {
+	return false
+}
+
+var errUnsupported = std_errors.New("operation unsupported on this platform")
+
+type Manipulator struct {
+}
+
+func NewManipulator(_ *Config) (*Manipulator, error) {
+	return nil, errors.Trace(errUnsupported)
+}
+
+func (m *Manipulator) Start() error {
+	return errors.Trace(errUnsupported)
+}
+
+func (m *Manipulator) Stop() {
+}
+
+func (m *Manipulator) SetSpecs(_ []*Spec) error {
+	return errors.Trace(errUnsupported)
+}
+
+func (m *Manipulator) GetAppliedSpecName(_, _ *net.TCPAddr) (string, error) {
+	return "", errors.Trace(errUnsupported)
+}

+ 28 - 3
psiphon/common/parameters/clientParameters.go

@@ -86,6 +86,8 @@ const (
 	StaggerConnectionWorkersPeriod                   = "StaggerConnectionWorkersPeriod"
 	StaggerConnectionWorkersJitter                   = "StaggerConnectionWorkersJitter"
 	LimitIntensiveConnectionWorkers                  = "LimitIntensiveConnectionWorkers"
+	UpstreamProxyErrorMinWaitDuration                = "UpstreamProxyErrorMinWaitDuration"
+	UpstreamProxyErrorMaxWaitDuration                = "UpstreamProxyErrorMaxWaitDuration"
 	IgnoreHandshakeStatsRegexps                      = "IgnoreHandshakeStatsRegexps"
 	PrioritizeTunnelProtocolsProbability             = "PrioritizeTunnelProtocolsProbability"
 	PrioritizeTunnelProtocols                        = "PrioritizeTunnelProtocols"
@@ -237,6 +239,9 @@ const (
 	BPFServerTCPProbability                          = "BPFServerTCPProbability"
 	BPFClientTCPProgram                              = "BPFClientTCPProgram"
 	BPFClientTCPProbability                          = "BPFClientTCPProbability"
+	ServerPacketManipulationSpecs                    = "ServerPacketManipulationSpecs"
+	ServerProtocolPacketManipulations                = "ServerProtocolPacketManipulations"
+	ServerPacketManipulationProbability              = "ServerPacketManipulationProbability"
 	FeedbackUploadURLs                               = "FeedbackUploadURLs"
 	FeedbackEncryptionPublicKey                      = "FeedbackEncryptionPublicKey"
 	FeedbackTacticsWaitPeriod                        = "FeedbackTacticsWaitPeriod"
@@ -288,6 +293,8 @@ var defaultClientParameters = map[string]struct {
 	StaggerConnectionWorkersPeriod:           {value: time.Duration(0), minimum: time.Duration(0)},
 	StaggerConnectionWorkersJitter:           {value: 0.1, minimum: 0.0},
 	LimitIntensiveConnectionWorkers:          {value: 0, minimum: 0},
+	UpstreamProxyErrorMinWaitDuration:        {value: 10 * time.Second, minimum: time.Duration(0)},
+	UpstreamProxyErrorMaxWaitDuration:        {value: 30 * time.Second, minimum: time.Duration(0)},
 	IgnoreHandshakeStatsRegexps:              {value: false},
 	TunnelOperateShutdownTimeout:             {value: 1 * time.Second, minimum: 1 * time.Millisecond, flags: useNetworkLatencyMultiplier},
 	TunnelPortForwardDialTimeout:             {value: 10 * time.Second, minimum: 1 * time.Millisecond, flags: useNetworkLatencyMultiplier},
@@ -494,11 +501,15 @@ var defaultClientParameters = map[string]struct {
 	ApplicationParametersProbability: {value: 1.0, minimum: 0.0},
 	ApplicationParameters:            {value: KeyValues{}},
 
-	BPFServerTCPProgram:     {value: (*BPFProgramSpec)(nil)},
-	BPFServerTCPProbability: {value: 0.5, minimum: 0.0},
+	BPFServerTCPProgram:     {value: (*BPFProgramSpec)(nil), flags: serverSideOnly},
+	BPFServerTCPProbability: {value: 0.5, minimum: 0.0, flags: serverSideOnly},
 	BPFClientTCPProgram:     {value: (*BPFProgramSpec)(nil)},
 	BPFClientTCPProbability: {value: 0.5, minimum: 0.0},
 
+	ServerPacketManipulationSpecs:       {value: PacketManipulationSpecs{}, flags: serverSideOnly},
+	ServerProtocolPacketManipulations:   {value: make(ProtocolPacketManipulations), flags: serverSideOnly},
+	ServerPacketManipulationProbability: {value: 0.5, minimum: 0.0, flags: serverSideOnly},
+
 	FeedbackUploadURLs:                 {value: TransferURLs{}},
 	FeedbackEncryptionPublicKey:        {value: ""},
 	FeedbackTacticsWaitPeriod:          {value: 5 * time.Second, minimum: 0 * time.Second, flags: useNetworkLatencyMultiplier},
@@ -1089,7 +1100,7 @@ func (p ClientParametersAccessor) QUICVersions(name string) protocol.QUICVersion
 // corresponding to the specified labeled set and label value. The return
 // value is nil when no set is found.
 func (p ClientParametersAccessor) LabeledQUICVersions(name, label string) protocol.QUICVersions {
-	var value protocol.LabeledQUICVersions
+	value := protocol.LabeledQUICVersions{}
 	p.snapshot.getValue(name, &value)
 	return value[label]
 }
@@ -1164,3 +1175,17 @@ func (p ClientParametersAccessor) BPFProgram(name string) (bool, string, []bpf.R
 	rawInstructions, _ := value.Assemble()
 	return true, value.Name, rawInstructions
 }
+
+// PacketManipulationSpecs returns a PacketManipulationSpecs parameter value.
+func (p ClientParametersAccessor) PacketManipulationSpecs(name string) PacketManipulationSpecs {
+	value := PacketManipulationSpecs{}
+	p.snapshot.getValue(name, &value)
+	return value
+}
+
+// ProtocolPacketManipulations returns a ProtocolPacketManipulations parameter value.
+func (p ClientParametersAccessor) ProtocolPacketManipulations(name string) ProtocolPacketManipulations {
+	value := make(ProtocolPacketManipulations)
+	p.snapshot.getValue(name, &value)
+	return value
+}

+ 10 - 0
psiphon/common/parameters/clientParameters_test.go

@@ -129,6 +129,16 @@ func TestGetDefaultParameters(t *testing.T) {
 					"BPFProgramSpec returned %+v %+v %+v expected %+v",
 					ok, name, rawInstructions, v)
 			}
+		case PacketManipulationSpecs:
+			g := p.Get().PacketManipulationSpecs(name)
+			if !reflect.DeepEqual(v, g) {
+				t.Fatalf("PacketManipulationSpecs returned %+v expected %+v", g, v)
+			}
+		case ProtocolPacketManipulations:
+			g := p.Get().ProtocolPacketManipulations(name)
+			if !reflect.DeepEqual(v, g) {
+				t.Fatalf("ProtocolPacketManipulations returned %+v expected %+v", g, v)
+			}
 		default:
 			t.Fatalf("Unhandled default type: %s", name)
 		}

+ 75 - 0
psiphon/common/parameters/packetman.go

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package parameters
+
+import (
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/packetman"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
+)
+
+// PacketManipulationSpecs is a list of packet manipulation specs.
+type PacketManipulationSpecs []*packetman.Spec
+
+// Validate checks that each spec name is unique and that each spec compiles.
+func (specs PacketManipulationSpecs) Validate() error {
+	specNames := make(map[string]bool)
+	for _, spec := range specs {
+		if spec.Name == "" {
+			return errors.TraceNew("missing spec name")
+		}
+		if ok, _ := specNames[spec.Name]; ok {
+			return errors.TraceNew("duplicate spec name")
+		}
+		specNames[spec.Name] = true
+		err := spec.Validate()
+		if err != nil {
+			return errors.Trace(err)
+		}
+	}
+	return nil
+}
+
+// ProtocolPacketManipulations is a map from tunnel protocol names (or "All")
+// to a list of packet manipulation spec names.
+type ProtocolPacketManipulations map[string][]string
+
+// Validate checks that tunnel protocol and spec names are valid. Duplicate
+// spec names are allowed in each entry, enabling weighted selection.
+func (manipulations ProtocolPacketManipulations) Validate(specs PacketManipulationSpecs) error {
+	validSpecNames := make(map[string]bool)
+	for _, spec := range specs {
+		validSpecNames[spec.Name] = true
+	}
+	for tunnelProtocol, specNames := range manipulations {
+		if tunnelProtocol != protocol.TUNNEL_PROTOCOLS_ALL {
+			if !protocol.TunnelProtocolMayUseServerPacketManipulation(tunnelProtocol) {
+				return errors.TraceNew("invalid tunnel protocol for packet manipulation")
+			}
+		}
+
+		for _, specName := range specNames {
+			if ok, _ := validSpecNames[specName]; !ok {
+				return errors.TraceNew("invalid spec name")
+			}
+		}
+	}
+	return nil
+}

+ 10 - 0
psiphon/common/protocol/protocol.go

@@ -43,6 +43,8 @@ const (
 	TUNNEL_PROTOCOL_TAPDANCE_OBFUSCATED_SSH          = "TAPDANCE-OSSH"
 	TUNNEL_PROTOCOL_CONJOUR_OBFUSCATED_SSH           = "CONJOUR-OSSH"
 
+	TUNNEL_PROTOCOLS_ALL = "All"
+
 	SERVER_ENTRY_SOURCE_EMBEDDED   = "EMBEDDED"
 	SERVER_ENTRY_SOURCE_REMOTE     = "REMOTE"
 	SERVER_ENTRY_SOURCE_DISCOVERY  = "DISCOVERY"
@@ -228,6 +230,14 @@ func TunnelProtocolSupportsUpstreamProxy(protocol string) bool {
 	return !TunnelProtocolUsesQUIC(protocol)
 }
 
+func TunnelProtocolMayUseServerPacketManipulation(protocol string) bool {
+	return protocol == TUNNEL_PROTOCOL_SSH ||
+		protocol == TUNNEL_PROTOCOL_OBFUSCATED_SSH ||
+		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK ||
+		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS ||
+		protocol == TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET
+}
+
 func UseClientTunnelProtocol(
 	clientProtocol string,
 	serverProtocols TunnelProtocols) bool {

+ 9 - 14
psiphon/common/tun/tun_darwin.go

@@ -247,11 +247,6 @@ func (device *Device) writeTunPacket(packet []byte) error {
 	return nil
 }
 
-func configureNetworkConfigSubprocessCapabilities() error {
-	// Not supported on Darwin
-	return nil
-}
-
 func resetNATTables(_ *ServerConfig, _ net.IP) error {
 	// Not supported on Darwin
 	// TODO: could use pfctl -K?
@@ -272,7 +267,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -289,7 +284,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -309,7 +304,7 @@ func configureServerInterface(
 		egressInterface = DEFAULT_PUBLIC_INTERFACE_NAME
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"sysctl",
@@ -318,7 +313,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"sysctl",
@@ -359,14 +354,14 @@ func configureServerInterface(
 	}).Debug("pf.conf")
 
 	// Disable first to avoid "pfctl: pf already enabled"
-	_ = runNetworkConfigCommand(
+	_ = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"pfctl",
 		"-q",
 		"-d")
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"pfctl",
@@ -394,7 +389,7 @@ func configureClientInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -412,7 +407,7 @@ func configureClientInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -429,7 +424,7 @@ func configureClientInterface(
 
 		// TODO: IPv6
 
-		err = runNetworkConfigCommand(
+		err = common.RunNetworkConfigCommand(
 			config.Logger,
 			config.SudoNetworkConfigCommands,
 			"route",

+ 19 - 44
psiphon/common/tun/tun_linux.go

@@ -30,7 +30,6 @@ import (
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
-	"github.com/syndtr/gocapability/capability"
 )
 
 const (
@@ -140,36 +139,6 @@ func (device *Device) writeTunPacket(packet []byte) error {
 	return nil
 }
 
-func configureNetworkConfigSubprocessCapabilities() error {
-
-	// If this process has CAP_NET_ADMIN, make it available to be inherited
-	// be child processes via ambient mechanism described here:
-	// https://github.com/torvalds/linux/commit/58319057b7847667f0c9585b9de0e8932b0fdb08
-	//
-	// The ambient mechanism is available in Linux kernel 4.3 and later.
-
-	// When using capabilities, this process should have CAP_NET_ADMIN in order
-	// to create tun devices. And the subprocess operations such as using "ifconfig"
-	// and "iptables" for network config require the same CAP_NET_ADMIN capability.
-
-	cap, err := capability.NewPid(0)
-	if err != nil {
-		return errors.Trace(err)
-	}
-
-	if cap.Get(capability.EFFECTIVE, capability.CAP_NET_ADMIN) {
-
-		cap.Set(capability.INHERITABLE|capability.AMBIENT, capability.CAP_NET_ADMIN)
-
-		err = cap.Apply(capability.AMBIENT)
-		if err != nil {
-			return errors.Trace(err)
-		}
-	}
-
-	return nil
-}
-
 func resetNATTables(
 	config *ServerConfig,
 	IPAddress net.IP) error {
@@ -182,7 +151,7 @@ func resetNATTables(
 	// the already unlikely event that there's still in-flight traffic when the address is
 	// recycled.
 
-	err := runNetworkConfigCommand(
+	err := common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"conntrack",
@@ -215,7 +184,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -227,7 +196,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -253,7 +222,7 @@ func configureServerInterface(
 
 	// TODO: need only set forwarding for specific interfaces?
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"sysctl",
@@ -262,7 +231,7 @@ func configureServerInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"sysctl",
@@ -282,7 +251,7 @@ func configureServerInterface(
 
 	for _, mode := range []string{"-D", "-A"} {
 
-		err = runNetworkConfigCommand(
+		err = common.RunNetworkConfigCommand(
 			config.Logger,
 			config.SudoNetworkConfigCommands,
 			"iptables",
@@ -295,7 +264,7 @@ func configureServerInterface(
 			return errors.Trace(err)
 		}
 
-		err = runNetworkConfigCommand(
+		err = common.RunNetworkConfigCommand(
 			config.Logger,
 			config.SudoNetworkConfigCommands,
 			"ip6tables",
@@ -330,7 +299,7 @@ func configureClientInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -343,7 +312,7 @@ func configureClientInterface(
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		config.Logger,
 		config.SudoNetworkConfigCommands,
 		"ifconfig",
@@ -384,7 +353,7 @@ func configureClientInterface(
 		// Note: use "replace" instead of "add" as route from
 		// previous run (e.g., tun_test case) may not yet be cleared.
 
-		err = runNetworkConfigCommand(
+		err = common.RunNetworkConfigCommand(
 			config.Logger,
 			config.SudoNetworkConfigCommands,
 			"ip",
@@ -419,8 +388,14 @@ func fixBindToDevice(logger common.Logger, useSudo bool, tunDeviceName string) e
 
 	// Fix the problem described here:
 	// https://stackoverflow.com/questions/24011205/cant-perform-tcp-handshake-through-a-nat-between-two-nics-with-so-bindtodevice/
+	//
+	// > the linux kernel is configured on certain mainstream distributions
+	// > (Ubuntu...) to act as a router and drop packets where the source
+	// > address is suspect in order to prevent spoofing (search "rp_filter" on
+	// > https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt and
+	// > RFC3704)
 
-	err := runNetworkConfigCommand(
+	err := common.RunNetworkConfigCommand(
 		logger,
 		useSudo,
 		"sysctl",
@@ -429,7 +404,7 @@ func fixBindToDevice(logger common.Logger, useSudo bool, tunDeviceName string) e
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		logger,
 		useSudo,
 		"sysctl",
@@ -438,7 +413,7 @@ func fixBindToDevice(logger common.Logger, useSudo bool, tunDeviceName string) e
 		return errors.Trace(err)
 	}
 
-	err = runNetworkConfigCommand(
+	err = common.RunNetworkConfigCommand(
 		logger,
 		useSudo,
 		"sysctl",

+ 0 - 47
psiphon/common/tun/utils.go

@@ -23,60 +23,13 @@ import (
 	std_errors "errors"
 	"fmt"
 	"net"
-	"os/exec"
 	"strconv"
 
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 )
 
 var errUnsupported = std_errors.New("operation unsupported on this platform")
 
-// runNetworkConfigCommand execs a network config command, such as "ifconfig"
-// or "iptables". On platforms that support capabilities, the network config
-// capabilities of the current process is made available to the command
-// subprocess. Alternatively, "sudo" will be used when useSudo is true.
-func runNetworkConfigCommand(
-	logger common.Logger,
-	useSudo bool,
-	commandName string, commandArgs ...string) error {
-
-	// configureSubprocessCapabilities will set inheritable
-	// capabilities on platforms which support that (Linux).
-	// Specifically, CAP_NET_ADMIN will be transferred from
-	// this process to the child command.
-
-	err := configureNetworkConfigSubprocessCapabilities()
-	if err != nil {
-		return errors.Trace(err)
-	}
-
-	// TODO: use CommandContext to interrupt on server shutdown?
-	// (the commands currently being issued shouldn't block...)
-
-	if useSudo {
-		commandArgs = append([]string{commandName}, commandArgs...)
-		commandName = "sudo"
-	}
-
-	cmd := exec.Command(commandName, commandArgs...)
-	output, err := cmd.CombinedOutput()
-
-	logger.WithTraceFields(common.LogFields{
-		"command": commandName,
-		"args":    commandArgs,
-		"output":  string(output),
-		"error":   err,
-	}).Debug("exec")
-
-	if err != nil {
-		err := fmt.Errorf(
-			"command %s %+v failed with %s", commandName, commandArgs, string(output))
-		return errors.Trace(err)
-	}
-	return nil
-}
-
 func splitIPMask(IPAddressCIDR string) (string, string, error) {
 
 	IP, IPNet, err := net.ParseCIDR(IPAddressCIDR)

+ 36 - 0
psiphon/common/utils.go

@@ -23,10 +23,12 @@ import (
 	"bytes"
 	"compress/zlib"
 	"crypto/rand"
+	std_errors "errors"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"math"
+	"net/url"
 	"os"
 	"time"
 
@@ -237,3 +239,37 @@ func DoFileMigration(migration FileMigration) error {
 
 	return nil
 }
+
+// SafeParseURL wraps url.Parse, stripping the input URL from any error
+// message. This allows logging url.Parse errors without unintentially logging
+// PII that may appear in the input URL.
+func SafeParseURL(rawurl string) (*url.URL, error) {
+	parsedURL, err := url.Parse(rawurl)
+	if err != nil {
+		// Unwrap yields just the url.Error error field without the url.Error URL
+		// and operation fields.
+		err = std_errors.Unwrap(err)
+		if err == nil {
+			err = std_errors.New("SafeParseURL: Unwrap failed")
+		} else {
+			err = fmt.Errorf("url.Parse: %v", err)
+		}
+	}
+	return parsedURL, err
+}
+
+// SafeParseRequestURI wraps url.ParseRequestURI, stripping the input URL from
+// any error message. This allows logging url.ParseRequestURI errors without
+// unintentially logging PII that may appear in the input URL.
+func SafeParseRequestURI(rawurl string) (*url.URL, error) {
+	parsedURL, err := url.ParseRequestURI(rawurl)
+	if err != nil {
+		err = std_errors.Unwrap(err)
+		if err == nil {
+			err = std_errors.New("SafeParseRequestURI: Unwrap failed")
+		} else {
+			err = fmt.Errorf("url.ParseRequestURI: %v", err)
+		}
+	}
+	return parsedURL, err
+}

+ 52 - 0
psiphon/common/utils_test.go

@@ -22,7 +22,9 @@ package common
 import (
 	"bytes"
 	"encoding/json"
+	"net/url"
 	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -90,3 +92,53 @@ func TestFormatByteCount(t *testing.T) {
 		})
 	}
 }
+
+func TestSafeParseURL(t *testing.T) {
+
+	invalidURL := "https://invalid url"
+
+	_, err := url.Parse(invalidURL)
+
+	if err == nil {
+		t.Error("unexpected parse success")
+	}
+
+	if strings.Index(err.Error(), invalidURL) == -1 {
+		t.Error("URL not in error string")
+	}
+
+	_, err = SafeParseURL(invalidURL)
+
+	if err == nil {
+		t.Error("unexpected parse success")
+	}
+
+	if strings.Index(err.Error(), invalidURL) != -1 {
+		t.Error("URL in error string")
+	}
+}
+
+func TestSafeParseRequestURI(t *testing.T) {
+
+	invalidURL := "https://invalid url"
+
+	_, err := url.ParseRequestURI(invalidURL)
+
+	if err == nil {
+		t.Error("unexpected parse success")
+	}
+
+	if strings.Index(err.Error(), invalidURL) == -1 {
+		t.Error("URL not in error string")
+	}
+
+	_, err = SafeParseRequestURI(invalidURL)
+
+	if err == nil {
+		t.Error("unexpected parse success")
+	}
+
+	if strings.Index(err.Error(), invalidURL) != -1 {
+		t.Error("URL in error string")
+	}
+}

+ 62 - 0
psiphon/controller.go

@@ -1664,6 +1664,67 @@ loop:
 			continue
 		}
 
+		// upstreamProxyErrorCallback will post NoticeUpstreamProxyError when the
+		// tunnel dial fails due to an upstream proxy error. As the upstream proxy
+		// is user configured, the error message may need to be relayed to the user.
+
+		// As the callback may be invoked after establishment is over (e.g., if an
+		// initial dial isn't fully shutdown when ConnectTunnel returns; or a meek
+		// underlying TCP connection re-dial) don't access these variables
+		// directly.
+		callbackCandidateServerEntry := candidateServerEntry
+		callbackEstablishCtx := controller.establishCtx
+
+		upstreamProxyErrorCallback := func(err error) {
+
+			// Do not post the notice when overall establishment context is canceled or
+			// timed-out: the upstream proxy connection error is likely a result of the
+			// cancellation, and not a condition to be fixed by the user. In the case
+			// of meek underlying TCP connection re-dials, this condition will always
+			// be true; however in this case the initial dial succeeded with the
+			// current upstream proxy settings, so any upstream proxy error is
+			// transient.
+			if callbackEstablishCtx.Err() != nil {
+				return
+			}
+
+			// Another class of non-fatal upstream proxy error arises from proxies
+			// which limit permitted proxied ports. In this case, some tunnels may fail
+			// due to dial port, while others may eventually succeed. To avoid this
+			// class of errors, delay posting the notice. If the upstream proxy works,
+			// _some_ tunnel should connect. If the upstream proxy configuration is
+			// broken, the error should persist and eventually get posted.
+
+			p := controller.config.GetClientParameters().Get()
+			workerPoolSize := p.Int(parameters.ConnectionWorkerPoolSize)
+			minWaitDuration := p.Duration(parameters.UpstreamProxyErrorMinWaitDuration)
+			maxWaitDuration := p.Duration(parameters.UpstreamProxyErrorMaxWaitDuration)
+			p.Close()
+
+			controller.concurrentEstablishTunnelsMutex.Lock()
+			establishConnectTunnelCount := controller.establishConnectTunnelCount
+			controller.concurrentEstablishTunnelsMutex.Unlock()
+
+			// Delay UpstreamProxyErrorMinWaitDuration (excluding time spent waiting
+			// for network connectivity) and then until either
+			// UpstreamProxyErrorMaxWaitDuration has elapsed or, to post sooner if many
+			// candidates are failing, at least workerPoolSize tunnel connection
+			// attempts have completed. We infer that at least workerPoolSize
+			// candidates have completed by checking that at least 2*workerPoolSize
+			// candidates have started.
+
+			elapsedTime := time.Since(
+				callbackCandidateServerEntry.adjustedEstablishStartTime)
+
+			if elapsedTime < minWaitDuration ||
+				(elapsedTime < maxWaitDuration &&
+					establishConnectTunnelCount < 2*workerPoolSize) {
+				return
+			}
+
+			NoticeUpstreamProxyError(err)
+		}
+
 		// Select the tunnel protocol. The selection will be made at random
 		// from protocols supported by the server entry, optionally limited by
 		// LimitTunnelProtocols.
@@ -1727,6 +1788,7 @@ loop:
 
 		dialParams, err := MakeDialParameters(
 			controller.config,
+			upstreamProxyErrorCallback,
 			canReplay,
 			selectProtocol,
 			candidateServerEntry.serverEntry,

+ 7 - 6
psiphon/controller_test.go

@@ -42,6 +42,7 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic"
 	"github.com/elazarl/goproxy"
+	"github.com/elazarl/goproxy/ext/auth"
 )
 
 func TestMain(m *testing.M) {
@@ -566,11 +567,6 @@ func controllerRun(t *testing.T, runConfig *controllerRunConfig) {
 		t.Fatalf("error committing configuration file: %s", err)
 	}
 
-	// Enable tactics requests. This will passively exercise the code
-	// paths. server_test runs a more comprehensive test that checks
-	// that the tactics request succeeds.
-	config.NetworkID = "NETWORK1"
-
 	err = OpenDataStore(config)
 	if err != nil {
 		t.Fatalf("error initializing datastore: %s", err)
@@ -1044,7 +1040,7 @@ func initDisruptor() {
 	}()
 }
 
-const upstreamProxyURL = "http://127.0.0.1:2161"
+const upstreamProxyURL = "http://testUser:testPassword@127.0.0.1:2161"
 
 var upstreamProxyCustomHeaders = map[string][]string{"X-Test-Header-Name": {"test-header-value1", "test-header-value2"}}
 
@@ -1068,6 +1064,11 @@ func initUpstreamProxy() {
 		proxy := goproxy.NewProxyHttpServer()
 		proxy.Logger = log.New(ioutil.Discard, "", 0)
 
+		auth.ProxyBasic(
+			proxy,
+			"testRealm",
+			func(user, passwd string) bool { return user == "testUser" && passwd == "testPassword" })
+
 		proxy.OnRequest().DoFunc(
 			func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
 				if !hasExpectedCustomHeaders(r.Header) {

+ 20 - 0
psiphon/dataStore.go

@@ -600,6 +600,16 @@ func (iterator *ServerEntryIterator) reset(isInitialRound bool) error {
 		return nil
 	}
 
+	// Support stand-alone GetTactics operation. See TacticsStorer for more
+	// details.
+	if iterator.isTacticsServerEntryIterator {
+		err := OpenDataStore(iterator.config)
+		if err != nil {
+			return errors.Trace(err)
+		}
+		defer CloseDataStore()
+	}
+
 	// BoltDB implementation note:
 	// We don't keep a transaction open for the duration of the iterator
 	// because this would expose the following semantics to consumer code:
@@ -740,6 +750,16 @@ func (iterator *ServerEntryIterator) Next() (*protocol.ServerEntry, error) {
 		return nil, nil
 	}
 
+	// Support stand-alone GetTactics operation. See TacticsStorer for more
+	// details.
+	if iterator.isTacticsServerEntryIterator {
+		err := OpenDataStore(iterator.config)
+		if err != nil {
+			return nil, errors.Trace(err)
+		}
+		defer CloseDataStore()
+	}
+
 	// There are no region/protocol indexes for the server entries bucket.
 	// Loop until we have the next server entry that matches the iterator
 	// filter requirements.

+ 6 - 3
psiphon/dialParameters.go

@@ -26,7 +26,6 @@ import (
 	"fmt"
 	"net"
 	"net/http"
-	"net/url"
 	"strings"
 	"sync/atomic"
 	"time"
@@ -146,6 +145,7 @@ type DialParameters struct {
 // when establishment is cancelled.
 func MakeDialParameters(
 	config *Config,
+	upstreamProxyErrorCallback func(error),
 	canReplay func(serverEntry *protocol.ServerEntry, replayProtocol string) bool,
 	selectProtocol func(serverEntry *protocol.ServerEntry) (string, bool),
 	serverEntry *protocol.ServerEntry,
@@ -263,8 +263,10 @@ func MakeDialParameters(
 		isReplay = false
 	}
 
-	// Set IsExchanged so that full dial parameters are stored and replayed upon success.
+	// Set IsExchanged such that full dial parameters are stored and replayed
+	// upon success.
 	dialParams.IsExchanged = false
+
 	dialParams.ServerEntry = serverEntry
 	dialParams.NetworkID = networkID
 	dialParams.IsReplay = isReplay
@@ -618,7 +620,7 @@ func MakeDialParameters(
 
 	if config.UseUpstreamProxy() {
 		// Note: UpstreamProxyURL will be validated in the dial
-		proxyURL, err := url.Parse(config.UpstreamProxyURL)
+		proxyURL, err := common.SafeParseURL(config.UpstreamProxyURL)
 		if err == nil {
 			dialParams.UpstreamProxyType = proxyURL.Scheme
 		}
@@ -663,6 +665,7 @@ func MakeDialParameters(
 		IPv6Synthesizer:               config.IPv6Synthesizer,
 		TrustedCACertificatesFilename: config.TrustedCACertificatesFilename,
 		FragmentorConfig:              fragmentor.NewUpstreamConfig(p, dialParams.TunnelProtocol, dialParams.FragmentorSeed),
+		UpstreamProxyErrorCallback:    upstreamProxyErrorCallback,
 	}
 
 	// Unconditionally initialize MeekResolvedIPAddress, so a valid string can

+ 20 - 12
psiphon/dialParameters_test.go

@@ -108,7 +108,10 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 	// Test: expected dial parameter fields set
 
-	dialParams, err := MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	upstreamProxyErrorCallback := func(_ error) {}
+
+	dialParams, err := MakeDialParameters(
+		clientConfig, upstreamProxyErrorCallback, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -201,11 +204,16 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("missing API request fields")
 	}
 
+	dialConfig := dialParams.GetDialConfig()
+	if dialConfig.UpstreamProxyErrorCallback == nil {
+		t.Fatalf("missing upstreamProxyErrorCallback")
+	}
+
 	// Test: no replay after dial reported to fail
 
 	dialParams.Failed(clientConfig)
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -220,7 +228,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 	testNetworkID = prng.HexString(8)
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -237,7 +245,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 	dialParams.Succeeded()
 
-	replayDialParams, err := MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	replayDialParams, err := MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -323,7 +331,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("SetClientParameters failed: %s", err)
 	}
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -338,7 +346,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 	time.Sleep(1 * time.Second)
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -353,7 +361,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 	serverEntries[0].ConfigurationVersion += 1
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -377,14 +385,14 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("SetClientParameters failed: %s", err)
 	}
 
-	dialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	dialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
 
 	dialParams.Succeeded()
 
-	replayDialParams, err = MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
+	replayDialParams, err = MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntries[0], false, 0, 0)
 	if err != nil {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
@@ -432,7 +440,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 
 		if i%10 == 0 {
 
-			dialParams, err := MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntry, false, 0, 0)
+			dialParams, err := MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntry, false, 0, 0)
 			if err != nil {
 				t.Fatalf("MakeDialParameters failed: %s", err)
 			}
@@ -461,7 +469,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 				t.Fatalf("ServerEntryIterator.Next failed: %s", err)
 			}
 
-			dialParams, err := MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntry, false, 0, 0)
+			dialParams, err := MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntry, false, 0, 0)
 			if err != nil {
 				t.Fatalf("MakeDialParameters failed: %s", err)
 			}
@@ -483,7 +491,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 				t.Fatalf("ServerEntryIterator.Next failed: %s", err)
 			}
 
-			dialParams, err := MakeDialParameters(clientConfig, canReplay, selectProtocol, serverEntry, false, 0, 0)
+			dialParams, err := MakeDialParameters(clientConfig, nil, canReplay, selectProtocol, serverEntry, false, 0, 0)
 			if err != nil {
 				t.Fatalf("MakeDialParameters failed: %s", err)
 			}

+ 1 - 0
psiphon/exchange_test.go

@@ -180,6 +180,7 @@ func TestServerEntryExchange(t *testing.T) {
 
 			dialParams, err := MakeDialParameters(
 				config,
+				nil,
 				canReplay,
 				selectProtocol,
 				serverEntry,

+ 2 - 2
psiphon/httpProxy.go

@@ -316,7 +316,7 @@ func (proxy *HttpProxy) urlProxyHandler(responseWriter http.ResponseWriter, requ
 	}
 
 	// Origin URL must be well-formed, absolute, and have a scheme of "http" or "https"
-	originURL, err := url.ParseRequestURI(originURLString)
+	originURL, err := common.SafeParseRequestURI(originURLString)
 	if err != nil {
 		NoticeWarning("%s", errors.Trace(FilterUrlError(err)))
 		forceClose(responseWriter)
@@ -678,7 +678,7 @@ func (proxy *HttpProxy) serve() {
 
 // toAbsoluteURL takes a base URL and a relative URL and constructs an appropriate absolute URL.
 func toAbsoluteURL(baseURL *url.URL, relativeURLString string) string {
-	relativeURL, err := url.Parse(relativeURLString)
+	relativeURL, err := common.SafeParseURL(relativeURLString)
 
 	if err != nil {
 		return ""

+ 1 - 1
psiphon/meekConn.go

@@ -445,7 +445,7 @@ func DialMeek(
 			(meekConfig.DialAddress == meekConfig.HostHeader ||
 				meekConfig.DialAddress == meekConfig.HostHeader+":80") {
 
-			url, err := url.Parse(dialConfig.UpstreamProxyURL)
+			url, err := common.SafeParseURL(dialConfig.UpstreamProxyURL)
 			if err != nil {
 				return nil, errors.Trace(err)
 			}

+ 5 - 0
psiphon/net.go

@@ -98,6 +98,11 @@ type DialConfig struct {
 	// FragmentorConfig specifies whether to layer a fragmentor.Conn on top
 	// of dialed TCP conns, and the fragmentation configuration to use.
 	FragmentorConfig *fragmentor.Config
+
+	// UpstreamProxyErrorCallback is called when a dial fails due to an upstream
+	// proxy error. As the upstream proxy is user configured, the error message
+	// may need to be relayed to the user.
+	UpstreamProxyErrorCallback func(error)
 }
 
 // NetworkConnectivityChecker defines the interface to the external

+ 10 - 7
psiphon/notice.go

@@ -667,9 +667,11 @@ func NoticeSplitTunnelRegion(region string) {
 // NoticeUpstreamProxyError reports an error when connecting to an upstream proxy. The
 // user may have input, for example, an incorrect address or incorrect credentials.
 func NoticeUpstreamProxyError(err error) {
-	singletonNoticeLogger.outputNotice(
+	message := err.Error()
+	outputRepetitiveNotice(
+		"UpstreamProxyError", message, 0,
 		"UpstreamProxyError", 0,
-		"message", err.Error())
+		"message", message)
 }
 
 // NoticeClientUpgradeDownloadedBytes reports client upgrade download progress.
@@ -745,14 +747,15 @@ func NoticeExiting() {
 }
 
 // NoticeRemoteServerListResourceDownloadedBytes reports remote server list download progress.
-func NoticeRemoteServerListResourceDownloadedBytes(url string, bytes int64) {
+func NoticeRemoteServerListResourceDownloadedBytes(url string, bytes int64, duration time.Duration) {
 	if !GetEmitNetworkParameters() {
 		url = "[redacted]"
 	}
 	singletonNoticeLogger.outputNotice(
 		"RemoteServerListResourceDownloadedBytes", noticeIsDiagnostic,
 		"url", url,
-		"bytes", bytes)
+		"bytes", bytes,
+		"duration", duration.String())
 }
 
 // NoticeRemoteServerListResourceDownloaded indicates that a remote server list download
@@ -843,7 +846,7 @@ func NoticePruneServerEntry(serverEntryTag string) {
 func NoticeEstablishTunnelTimeout(timeout time.Duration) {
 	singletonNoticeLogger.outputNotice(
 		"EstablishTunnelTimeout", 0,
-		"timeout", timeout)
+		"timeout", timeout.String())
 }
 
 func NoticeFragmentor(diagnosticID string, message string) {
@@ -873,7 +876,7 @@ func NoticeServerAlert(alert protocol.AlertRequest) {
 	repetitionKey := fmt.Sprintf("ServerAlert-%+v", alert)
 	outputRepetitiveNotice(
 		repetitionKey, "", 0,
-		"ServerAlert", noticeIsDiagnostic, "reason", alert.Reason, "subject", alert.Subject)
+		"ServerAlert", 0, "reason", alert.Reason, "subject", alert.Subject)
 }
 
 type repetitiveNoticeState struct {
@@ -897,7 +900,7 @@ func outputRepetitiveNotice(
 
 	state, keyFound := repetitiveNoticeStates[repetitionKey]
 	if !keyFound {
-		state = new(repetitiveNoticeState)
+		state = &repetitiveNoticeState{message: repetitionMessage}
 		repetitiveNoticeStates[repetitionKey] = state
 	}
 

+ 7 - 3
psiphon/remoteServerList.go

@@ -462,7 +462,9 @@ func downloadRemoteServerListFile(
 		return "", nil, errors.Trace(err)
 	}
 
-	n, responseETag, err := ResumeDownload(
+	startTime := time.Now()
+
+	bytes, responseETag, err := ResumeDownload(
 		ctx,
 		httpClient,
 		sourceURL,
@@ -470,7 +472,9 @@ func downloadRemoteServerListFile(
 		destinationFilename,
 		lastETag)
 
-	NoticeRemoteServerListResourceDownloadedBytes(sourceURL, n)
+	duration := time.Since(startTime)
+
+	NoticeRemoteServerListResourceDownloadedBytes(sourceURL, bytes, duration)
 
 	if err != nil {
 		return "", nil, errors.Trace(err)
@@ -484,7 +488,7 @@ func downloadRemoteServerListFile(
 
 	downloadStatRecorder := func(authenticated bool) {
 		_ = RecordRemoteServerListStat(
-			config, tunneled, sourceURL, responseETag, authenticated)
+			config, tunneled, sourceURL, responseETag, bytes, duration, authenticated)
 	}
 
 	return responseETag, downloadStatRecorder, nil

+ 21 - 6
psiphon/server/api.go

@@ -466,6 +466,8 @@ var remoteServerListStatParams = append(
 		{"tunneled", isBooleanFlag, requestParamOptional | requestParamLogFlagAsBool},
 		{"url", isAnyString, 0},
 		{"etag", isAnyString, 0},
+		{"bytes", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"duration", isIntString, requestParamOptional | requestParamLogStringAsInt},
 		{"authenticated", isBooleanFlag, requestParamOptional | requestParamLogFlagAsBool}},
 	baseSessionParams...)
 
@@ -491,12 +493,12 @@ var failedTunnelStatParams = append(
 		{"session_id", isHexDigits, 0},
 		{"last_connected", isLastConnected, 0},
 		{"client_failed_timestamp", isISO8601Date, 0},
-		{"liveness_test_upstream_bytes", isIntString, requestParamOptional},
-		{"liveness_test_sent_upstream_bytes", isIntString, requestParamOptional},
-		{"liveness_test_downstream_bytes", isIntString, requestParamOptional},
-		{"liveness_test_received_downstream_bytes", isIntString, requestParamOptional},
-		{"bytes_up", isIntString, requestParamOptional},
-		{"bytes_down", isIntString, requestParamOptional},
+		{"liveness_test_upstream_bytes", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"liveness_test_sent_upstream_bytes", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"liveness_test_downstream_bytes", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"liveness_test_received_downstream_bytes", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"bytes_up", isIntString, requestParamOptional | requestParamLogStringAsInt},
+		{"bytes_down", isIntString, requestParamOptional | requestParamLogStringAsInt},
 		{"tunnel_error", isAnyString, 0}},
 	baseSessionAndDialParams...)
 
@@ -1066,6 +1068,19 @@ func getRequestLogFields(
 				// value for speed_test_samples via the web API protocol. Omit
 				// the field in this case.
 
+			case "tunnel_error":
+				// net/url.Error, returned from net/url.Parse, contains the original input
+				// URL, which may contain PII. New clients strip this out by using
+				// common.SafeParseURL. Legacy clients will still send the full error
+				// message, so strip it out here. The target substring should be unique to
+				// legacy clients.
+				target := "upstreamproxy error: proxyURI url.Parse: parse "
+				index := strings.Index(strValue, target)
+				if index != -1 {
+					strValue = strValue[:index+len(target)] + "<redacted>"
+				}
+				logFields[expectedParam.name] = strValue
+
 			default:
 				if expectedParam.flags&requestParamLogStringAsInt != 0 {
 					intValue, _ := strconv.Atoi(strValue)

+ 3 - 0
psiphon/server/config.go

@@ -310,6 +310,9 @@ type Config struct {
 	// tun.ServerConfig.SudoNetworkConfigCommands.
 	PacketTunnelSudoNetworkConfigCommands bool
 
+	// RunPacketManipulator specifies whether to run a packet manipulator.
+	RunPacketManipulator bool
+
 	// MaxConcurrentSSHHandshakes specifies a limit on the number of concurrent
 	// SSH handshake negotiations. This is set to mitigate spikes in memory
 	// allocations and CPU usage associated with SSH handshakes when many clients

+ 60 - 25
psiphon/server/meek.go

@@ -21,6 +21,7 @@ package server
 
 import (
 	"bytes"
+	"context"
 	"crypto/rand"
 	"crypto/tls"
 	"encoding/base64"
@@ -172,6 +173,12 @@ func NewMeekServer(
 	return meekServer, nil
 }
 
+type meekContextKey struct {
+	key string
+}
+
+var meekNetConnContextKey = &meekContextKey{"net.Conn"}
+
 // Run runs the meek server; this function blocks while serving HTTP or
 // HTTPS connections on the specified listener. This function also runs
 // a goroutine which cleans up expired meek client sessions.
@@ -217,6 +224,9 @@ func (server *MeekServer) Run() error {
 		WriteTimeout: MEEK_HTTP_CLIENT_IO_TIMEOUT,
 		Handler:      server,
 		ConnState:    server.httpConnStateCallback,
+		ConnContext: func(ctx context.Context, conn net.Conn) context.Context {
+			return context.WithValue(ctx, meekNetConnContextKey, conn)
+		},
 
 		// Disable auto HTTP/2 (https://golang.org/doc/go1.6)
 		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
@@ -621,6 +631,8 @@ func (server *MeekServer) getSessionOrEndpoint(
 
 	session.touch()
 
+	underlyingConn := request.Context().Value(meekNetConnContextKey).(net.Conn)
+
 	// Create a new meek conn that will relay the payload
 	// between meek request/responses and the tunnel server client
 	// handler. The client IP is also used to initialize the
@@ -632,6 +644,8 @@ func (server *MeekServer) getSessionOrEndpoint(
 	clientConn := newMeekConn(
 		server,
 		session,
+		underlyingConn.LocalAddr(),
+		underlyingConn.RemoteAddr(),
 		&net.TCPAddr{
 			IP:   net.ParseIP(clientIP),
 			Port: 0,
@@ -1195,41 +1209,50 @@ func makeMeekSessionID() (string, error) {
 // connection by the tunnel server (being passed to sshServer.handleClient).
 // meekConn bridges net/http request/response payload readers and writers
 // and goroutines calling Read()s and Write()s.
+//
+// meekConn implements the UnderlyingTCPAddrSource, returning the TCP
+// addresses for the _first_ underlying TCP connection in the meek tunnel.
 type meekConn struct {
-	meekServer        *MeekServer
-	meekSession       *meekSession
-	remoteAddr        net.Addr
-	protocolVersion   int
-	closeBroadcast    chan struct{}
-	closed            int32
-	lastReadChecksum  *uint64
-	readLock          sync.Mutex
-	emptyReadBuffer   chan *bytes.Buffer
-	partialReadBuffer chan *bytes.Buffer
-	fullReadBuffer    chan *bytes.Buffer
-	writeLock         sync.Mutex
-	nextWriteBuffer   chan []byte
-	writeResult       chan error
+	meekServer           *MeekServer
+	meekSession          *meekSession
+	remoteAddr           net.Addr
+	underlyingLocalAddr  net.Addr
+	underlyingRemoteAddr net.Addr
+	protocolVersion      int
+	closeBroadcast       chan struct{}
+	closed               int32
+	lastReadChecksum     *uint64
+	readLock             sync.Mutex
+	emptyReadBuffer      chan *bytes.Buffer
+	partialReadBuffer    chan *bytes.Buffer
+	fullReadBuffer       chan *bytes.Buffer
+	writeLock            sync.Mutex
+	nextWriteBuffer      chan []byte
+	writeResult          chan error
 }
 
 func newMeekConn(
 	meekServer *MeekServer,
 	meekSession *meekSession,
+	underlyingLocalAddr net.Addr,
+	underlyingRemoteAddr net.Addr,
 	remoteAddr net.Addr,
 	protocolVersion int) *meekConn {
 
 	conn := &meekConn{
-		meekServer:        meekServer,
-		meekSession:       meekSession,
-		remoteAddr:        remoteAddr,
-		protocolVersion:   protocolVersion,
-		closeBroadcast:    make(chan struct{}),
-		closed:            0,
-		emptyReadBuffer:   make(chan *bytes.Buffer, 1),
-		partialReadBuffer: make(chan *bytes.Buffer, 1),
-		fullReadBuffer:    make(chan *bytes.Buffer, 1),
-		nextWriteBuffer:   make(chan []byte, 1),
-		writeResult:       make(chan error, 1),
+		meekServer:           meekServer,
+		meekSession:          meekSession,
+		underlyingLocalAddr:  underlyingLocalAddr,
+		underlyingRemoteAddr: underlyingRemoteAddr,
+		remoteAddr:           remoteAddr,
+		protocolVersion:      protocolVersion,
+		closeBroadcast:       make(chan struct{}),
+		closed:               0,
+		emptyReadBuffer:      make(chan *bytes.Buffer, 1),
+		partialReadBuffer:    make(chan *bytes.Buffer, 1),
+		fullReadBuffer:       make(chan *bytes.Buffer, 1),
+		nextWriteBuffer:      make(chan []byte, 1),
+		writeResult:          make(chan error, 1),
 	}
 	// Read() calls and pumpReads() are synchronized by exchanging control
 	// of a single readBuffer. This is the same scheme used in and described
@@ -1238,6 +1261,18 @@ func newMeekConn(
 	return conn
 }
 
+func (conn *meekConn) GetUnderlyingTCPAddrs() (*net.TCPAddr, *net.TCPAddr, bool) {
+	localAddr, ok := conn.underlyingLocalAddr.(*net.TCPAddr)
+	if !ok {
+		return nil, nil, false
+	}
+	remoteAddr, ok := conn.underlyingRemoteAddr.(*net.TCPAddr)
+	if !ok {
+		return nil, nil, false
+	}
+	return localAddr, remoteAddr, true
+}
+
 // pumpReads causes goroutines blocking on meekConn.Read() to read
 // from the specified reader. This function blocks until the reader
 // is fully consumed or the meekConn is closed. A read buffer allows

+ 4 - 0
psiphon/server/net.go

@@ -77,3 +77,7 @@ func (server *HTTPSServer) ServeTLS(listener net.Listener, config *tris.Config)
 	tlsListener := tris.NewListener(listener, config)
 	return server.Serve(tlsListener)
 }
+
+type UnderlyingTCPAddrSource interface {
+	GetUnderlyingTCPAddrs() (*net.TCPAddr, *net.TCPAddr, bool)
+}

+ 202 - 0
psiphon/server/packetman.go

@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package server
+
+import (
+	"net"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/packetman"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/prng"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
+)
+
+func makePacketManipulatorConfig(
+	support *SupportServices) (*packetman.Config, error) {
+
+	// Packet interception is configured for any tunnel protocol port that _may_
+	// use packet manipulation. A future hot reload of tactics may apply specs to
+	// any of these protocols.
+
+	var ports []int
+	for tunnelProtocol, port := range support.Config.TunnelProtocolPorts {
+		if protocol.TunnelProtocolMayUseServerPacketManipulation(tunnelProtocol) {
+			ports = append(ports, port)
+		}
+	}
+
+	selectSpecName := func(protocolPort int, clientIP net.IP) string {
+
+		specName, err := selectPacketManipulationSpec(support, protocolPort, clientIP)
+		if err != nil {
+			log.WithTraceFields(
+				LogFields{"error": err}).Warning(
+				"failed to get tactics for packet manipulation")
+			return ""
+		}
+
+		return specName
+	}
+
+	specs, err := getPacketManipulationSpecs(support)
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
+
+	return &packetman.Config{
+		Logger:                    CommonLogger(log),
+		SudoNetworkConfigCommands: support.Config.PacketTunnelSudoNetworkConfigCommands,
+		QueueNumber:               1,
+		ProtocolPorts:             ports,
+		Specs:                     specs,
+		SelectSpecName:            selectSpecName,
+	}, nil
+}
+
+func getPacketManipulationSpecs(support *SupportServices) ([]*packetman.Spec, error) {
+
+	// By convention, parameters.ServerPacketManipulationSpecs should be in
+	// DefaultTactics, not FilteredTactics; and Tactics.Probability is ignored.
+
+	tactics, err := support.TacticsServer.GetTactics(
+		true, common.GeoIPData(NewGeoIPData()), make(common.APIParameters))
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
+
+	if tactics == nil {
+		// This server isn't configured with tactics.
+		return []*packetman.Spec{}, nil
+	}
+
+	clientParameters, err := parameters.NewClientParameters(nil)
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
+	_, err = clientParameters.Set("", false, tactics.Parameters)
+	if err != nil {
+		return nil, errors.Trace(err)
+	}
+	p := clientParameters.Get()
+
+	specs := p.PacketManipulationSpecs(parameters.ServerPacketManipulationSpecs)
+
+	return specs, nil
+}
+
+func reloadPacketManipulationSpecs(support *SupportServices) error {
+
+	specs, err := getPacketManipulationSpecs(support)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	err = support.PacketManipulator.SetSpecs(specs)
+	if err != nil {
+		return errors.Trace(err)
+	}
+
+	return nil
+}
+
+func selectPacketManipulationSpec(
+	support *SupportServices, protocolPort int, clientIP net.IP) (string, error) {
+
+	geoIPData := support.GeoIPService.Lookup(clientIP.String())
+
+	tactics, err := support.TacticsServer.GetTactics(
+		true, common.GeoIPData(geoIPData), make(common.APIParameters))
+	if err != nil {
+		return "", errors.Trace(err)
+	}
+
+	if tactics == nil {
+		// This server isn't configured with tactics.
+		return "", nil
+	}
+
+	if !prng.FlipWeightedCoin(tactics.Probability) {
+		// Skip tactics with the configured probability.
+		return "", nil
+	}
+
+	clientParameters, err := parameters.NewClientParameters(nil)
+	if err != nil {
+		return "", errors.Trace(err)
+	}
+	_, err = clientParameters.Set("", false, tactics.Parameters)
+	if err != nil {
+		return "", errors.Trace(err)
+	}
+	p := clientParameters.Get()
+
+	// GeoIP tactics filtering is applied before getting
+	// ServerPacketManipulationProbability and ServerProtocolPacketManipulations.
+	//
+	// The intercepted packet source/protocol port is used to determine the
+	// tunnel protocol name, which is used to lookup enabled packet manipulation
+	// specs in ServerProtocolPacketManipulations.
+	//
+	// When there are multiple enabled specs, one is selected at random.
+	//
+	// Specs under the key "All" apply to all protocols. Duplicate specs per
+	// entry are allowed, enabling weighted selection. If a spec appears in both
+	// "All" and a specific protocol, the duplicate(s) are retained.
+
+	if !p.WeightedCoinFlip(parameters.ServerPacketManipulationProbability) {
+		return "", nil
+	}
+
+	targetTunnelProtocol := ""
+	for tunnelProtocol, port := range support.Config.TunnelProtocolPorts {
+		if port == protocolPort {
+			targetTunnelProtocol = tunnelProtocol
+			break
+		}
+	}
+	if targetTunnelProtocol == "" {
+		return "", errors.Tracef(
+			"packet manipulation protocol port not found: %d", protocolPort)
+	}
+
+	protocolSpecs := p.ProtocolPacketManipulations(
+		parameters.ServerProtocolPacketManipulations)
+
+	// TODO: cache merged per-protocol + "All" lists?
+
+	specNames, ok := protocolSpecs[targetTunnelProtocol]
+	if !ok {
+		specNames = []string{}
+	}
+
+	allProtocolsSpecNames, ok := protocolSpecs[protocol.TUNNEL_PROTOCOLS_ALL]
+	if ok {
+		specNames = append(specNames, allProtocolsSpecNames...)
+	}
+
+	if len(specNames) < 1 {
+		// Tactics contains no candidate specs for this protocol.
+		return "", nil
+	}
+
+	return specNames[prng.Range(0, len(specNames)-1)], nil
+}

+ 46 - 0
psiphon/server/server_packetman_test.go

@@ -0,0 +1,46 @@
+// +build PACKET_MANIPULATOR_TEST
+
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package server
+
+import (
+	"testing"
+)
+
+func TestServerPacketManipulation(t *testing.T) {
+	runServer(t,
+		&runServerConfig{
+			tunnelProtocol:       "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
+			enableSSHAPIRequests: true,
+			doHotReload:          false,
+			doDefaultSponsorID:   false,
+			denyTrafficRules:     false,
+			requireAuthorization: true,
+			omitAuthorization:    false,
+			doTunneledWebRequest: true,
+			doTunneledNTPRequest: true,
+			forceFragmenting:     false,
+			forceLivenessTest:    false,
+			doPruneServerEntries: false,
+			doDanglingTCPConn:    true,
+			doPacketManipulation: true,
+		})
+}

+ 48 - 3
psiphon/server/server_test.go

@@ -131,6 +131,7 @@ func TestSSH(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -150,6 +151,7 @@ func TestOSSH(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -169,6 +171,7 @@ func TestFragmentedOSSH(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -188,6 +191,7 @@ func TestUnfrontedMeek(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -208,6 +212,7 @@ func TestUnfrontedMeekHTTPS(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -228,6 +233,7 @@ func TestUnfrontedMeekHTTPSTLS13(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -248,6 +254,7 @@ func TestUnfrontedMeekSessionTicket(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -268,6 +275,7 @@ func TestUnfrontedMeekSessionTicketTLS13(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    true,
+			doPacketManipulation: false,
 		})
 }
 
@@ -290,6 +298,7 @@ func TestQUICOSSH(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -312,6 +321,7 @@ func TestMarionetteOSSH(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -331,6 +341,7 @@ func TestWebTransportAPIRequests(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -350,6 +361,7 @@ func TestHotReload(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -369,6 +381,7 @@ func TestDefaultSponsorID(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -388,6 +401,7 @@ func TestDenyTrafficRules(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -407,6 +421,7 @@ func TestOmitAuthorization(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -426,6 +441,7 @@ func TestNoAuthorization(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -445,6 +461,7 @@ func TestUnusedAuthorization(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -464,6 +481,7 @@ func TestTCPOnlySLOK(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -483,6 +501,7 @@ func TestUDPOnlySLOK(t *testing.T) {
 			forceLivenessTest:    false,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -502,6 +521,7 @@ func TestLivenessTest(t *testing.T) {
 			forceLivenessTest:    true,
 			doPruneServerEntries: false,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -521,6 +541,7 @@ func TestPruneServerEntries(t *testing.T) {
 			forceLivenessTest:    true,
 			doPruneServerEntries: true,
 			doDanglingTCPConn:    false,
+			doPacketManipulation: false,
 		})
 }
 
@@ -539,6 +560,7 @@ type runServerConfig struct {
 	forceLivenessTest    bool
 	doPruneServerEntries bool
 	doDanglingTCPConn    bool
+	doPacketManipulation bool
 }
 
 var (
@@ -706,6 +728,8 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	// Allow port forwards to local test web server.
 	serverConfig["AllowBogons"] = true
 
+	serverConfig["RunPacketManipulator"] = runConfig.doPacketManipulation
+
 	serverConfigJSON, _ = json.Marshal(serverConfig)
 
 	serverTunnelLog := make(chan map[string]interface{}, 1)
@@ -1171,11 +1195,16 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 
 	expectClientBPFField := psiphon.ClientBPFEnabled() && doClientTactics
 	expectServerBPFField := ServerBPFEnabled() && doServerTactics
+	expectServerPacketManipulationField := runConfig.doPacketManipulation
 
 	select {
 	case logFields := <-serverTunnelLog:
 		err := checkExpectedServerTunnelLogFields(
-			runConfig, expectClientBPFField, expectServerBPFField, logFields)
+			runConfig,
+			expectClientBPFField,
+			expectServerBPFField,
+			expectServerPacketManipulationField,
+			logFields)
 		if err != nil {
 			t.Fatalf("invalid server tunnel log fields: %s", err)
 		}
@@ -1186,7 +1215,9 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	if expectUniqueUser {
 		select {
 		case logFields := <-uniqueUserLog:
-			err := checkExpectedUniqueUserLogFields(runConfig, logFields)
+			err := checkExpectedUniqueUserLogFields(
+				runConfig,
+				logFields)
 			if err != nil {
 				t.Fatalf("invalid unique user log fields: %s", err)
 			}
@@ -1209,6 +1240,7 @@ func checkExpectedServerTunnelLogFields(
 	runConfig *runServerConfig,
 	expectClientBPFField bool,
 	expectServerBPFField bool,
+	expectServerPacketManipulationField bool,
 	fields map[string]interface{}) error {
 
 	// Limitations:
@@ -1385,6 +1417,15 @@ func checkExpectedServerTunnelLogFields(
 		}
 	}
 
+	if expectServerPacketManipulationField {
+		name := "server_packet_manipulation"
+		if fields[name] == nil {
+			return fmt.Errorf("missing expected field '%s'", name)
+		} else if fmt.Sprintf("%s", fields[name]) != "test-packetman-spec" {
+			return fmt.Errorf("unexpected field value %s: '%s'", name, fields[name])
+		}
+	}
+
 	if fields["network_type"].(string) != testNetworkType {
 		return fmt.Errorf("unexpected network_type '%s'", fields["network_type"])
 	}
@@ -1951,7 +1992,10 @@ func paveTacticsConfigFile(
             "Name" : "test-client-bpf",
               "Instructions" : [
                 {"Op": "RetConstant", "Args": {"Val": 65535}}]},
-          "BPFClientTCPProbability" : 1.0
+          "BPFClientTCPProbability" : 1.0,
+          "ServerPacketManipulationSpecs" : [{"Name": "test-packetman-spec", "PacketSpecs": [["TCP-flags S"]]}],
+          "ServerPacketManipulationProbability" : 1.0,
+          "ServerProtocolPacketManipulations": {"All" : ["test-packetman-spec"]}
         }
       },
       "FilteredTactics" : [
@@ -2199,6 +2243,7 @@ func storePruneServerEntriesTest(
 
 		dialParams, err := psiphon.MakeDialParameters(
 			clientConfig,
+			nil,
 			func(_ *protocol.ServerEntry, _ string) bool { return true },
 			func(serverEntry *protocol.ServerEntry) (string, bool) {
 				return runConfig.tunnelProtocol, true

+ 55 - 7
psiphon/server/services.go

@@ -38,6 +38,7 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/buildinfo"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/osl"
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/packetman"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tun"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/server/psinet"
@@ -46,25 +47,32 @@ import (
 // RunServices initializes support functions including logging and GeoIP services;
 // and then starts the server components and runs them until os.Interrupt or
 // os.Kill signals are received. The config determines which components are run.
-func RunServices(configJSON []byte) error {
+func RunServices(configJSON []byte) (retErr error) {
+
+	loggingInitialized := false
+
+	defer func() {
+		if retErr != nil && loggingInitialized {
+			log.WithTraceFields(LogFields{"error": retErr}).Error("RunServices failed")
+		}
+	}()
 
 	rand.Seed(int64(time.Now().Nanosecond()))
 
 	config, err := LoadConfig(configJSON)
 	if err != nil {
-		log.WithTraceFields(LogFields{"error": err}).Error("load config failed")
 		return errors.Trace(err)
 	}
 
 	err = InitLogging(config)
 	if err != nil {
-		log.WithTraceFields(LogFields{"error": err}).Error("init logging failed")
 		return errors.Trace(err)
 	}
 
+	loggingInitialized = true
+
 	supportServices, err := NewSupportServices(config)
 	if err != nil {
-		log.WithTraceFields(LogFields{"error": err}).Error("init support services failed")
 		return errors.Trace(err)
 	}
 
@@ -78,7 +86,6 @@ func RunServices(configJSON []byte) error {
 
 	tunnelServer, err := NewTunnelServer(supportServices, shutdownBroadcast)
 	if err != nil {
-		log.WithTraceFields(LogFields{"error": err}).Error("init tunnel server failed")
 		return errors.Trace(err)
 	}
 
@@ -97,13 +104,27 @@ func RunServices(configJSON []byte) error {
 			AllowBogons:                 config.AllowBogons,
 		})
 		if err != nil {
-			log.WithTraceFields(LogFields{"error": err}).Error("init packet tunnel failed")
 			return errors.Trace(err)
 		}
 
 		supportServices.PacketTunnelServer = packetTunnelServer
 	}
 
+	if config.RunPacketManipulator {
+
+		packetManipulatorConfig, err := makePacketManipulatorConfig(supportServices)
+		if err != nil {
+			return errors.Trace(err)
+		}
+
+		packetManipulator, err := packetman.NewManipulator(packetManipulatorConfig)
+		if err != nil {
+			return errors.Trace(err)
+		}
+
+		supportServices.PacketManipulator = packetManipulator
+	}
+
 	// After this point, errors should be delivered to the errors channel and
 	// orderly shutdown should flow through to the end of the function to ensure
 	// all workers are synchronously stopped.
@@ -118,6 +139,23 @@ func RunServices(configJSON []byte) error {
 		}()
 	}
 
+	if config.RunPacketManipulator {
+		err := supportServices.PacketManipulator.Start()
+		if err != nil {
+			select {
+			case errorChannel <- err:
+			default:
+			}
+		} else {
+			waitGroup.Add(1)
+			go func() {
+				defer waitGroup.Done()
+				<-shutdownBroadcast
+				supportServices.PacketManipulator.Stop()
+			}()
+		}
+	}
+
 	if config.RunLoadMonitor() {
 		waitGroup.Add(1)
 		go func() {
@@ -256,7 +294,6 @@ loop:
 			break loop
 
 		case err = <-errorChannel:
-			log.WithTraceFields(LogFields{"error": err}).Error("service failed")
 			break loop
 		}
 	}
@@ -397,6 +434,7 @@ type SupportServices struct {
 	PacketTunnelServer *tun.Server
 	TacticsServer      *tactics.Server
 	Blocklist          *Blocklist
+	PacketManipulator  *packetman.Manipulator
 }
 
 // NewSupportServices initializes a new SupportServices.
@@ -479,6 +517,16 @@ func (support *SupportServices) Reload() {
 		support.TrafficRulesSet: func() { support.TunnelServer.ResetAllClientTrafficRules() },
 		support.OSLConfig:       func() { support.TunnelServer.ResetAllClientOSLConfigs() },
 	}
+	if support.Config.RunPacketManipulator {
+		reloadPostActions[support.TacticsServer] = func() {
+			err := reloadPacketManipulationSpecs(support)
+			if err != nil {
+				log.WithTraceFields(
+					LogFields{"error": errors.Trace(err)}).Warning(
+					"failed to reload packet manipulation specs")
+			}
+		}
+	}
 
 	for _, reloader := range reloaders {
 

+ 1 - 0
psiphon/server/sessionID_test.go

@@ -162,6 +162,7 @@ func TestDuplicateSessionID(t *testing.T) {
 
 		dialParams, err := psiphon.MakeDialParameters(
 			clientConfig,
+			nil,
 			func(_ *protocol.ServerEntry, _ string) bool { return false },
 			func(_ *protocol.ServerEntry) (string, bool) { return "OSSH", true },
 			serverEntry,

+ 53 - 12
psiphon/server/tunnelServer.go

@@ -1057,6 +1057,40 @@ func (sshServer *sshServer) handleClient(
 		}
 	}
 
+	serverPacketManipulation := ""
+	if sshServer.support.Config.RunPacketManipulator &&
+		protocol.TunnelProtocolMayUseServerPacketManipulation(tunnelProtocol) {
+
+		// A meekConn has synthetic address values, including the original client
+		// address in cases where the client uses an upstream proxy to connect to
+		// Psiphon. For meekConn, and any other conn implementing
+		// UnderlyingTCPAddrSource, get the underlying TCP connection addresses.
+		//
+		// Limitation: a meek tunnel may consist of several TCP connections. The
+		// server_packet_manipulation metric will reflect the packet manipulation
+		// applied to the _first_ TCP connection only.
+
+		var localAddr, remoteAddr *net.TCPAddr
+		var ok bool
+		underlying, ok := clientConn.(UnderlyingTCPAddrSource)
+		if ok {
+			localAddr, remoteAddr, ok = underlying.GetUnderlyingTCPAddrs()
+		} else {
+			localAddr, ok = clientConn.LocalAddr().(*net.TCPAddr)
+			if ok {
+				remoteAddr, ok = clientConn.RemoteAddr().(*net.TCPAddr)
+			}
+		}
+
+		if ok {
+			specName, err := sshServer.support.PacketManipulator.
+				GetAppliedSpecName(localAddr, remoteAddr)
+			if err == nil {
+				serverPacketManipulation = specName
+			}
+		}
+	}
+
 	geoIPData := sshServer.support.GeoIPService.Lookup(
 		common.IPAddressFromAddr(clientAddr))
 
@@ -1112,6 +1146,7 @@ func (sshServer *sshServer) handleClient(
 		sshServer,
 		sshListener,
 		tunnelProtocol,
+		serverPacketManipulation,
 		geoIPData)
 
 	// sshClient.run _must_ call onSSHHandshakeFinished to release the semaphore:
@@ -1156,6 +1191,7 @@ type sshClient struct {
 	sshConn                              ssh.Conn
 	activityConn                         *common.ActivityMonitoredConn
 	throttledConn                        *common.ThrottledConn
+	serverPacketManipulation             string
 	geoIPData                            GeoIPData
 	sessionID                            string
 	isFirstTunnelInSession               bool
@@ -1246,6 +1282,7 @@ func newSshClient(
 	sshServer *sshServer,
 	sshListener *sshListener,
 	tunnelProtocol string,
+	serverPacketManipulation string,
 	geoIPData GeoIPData) *sshClient {
 
 	runCtx, stopRunning := context.WithCancel(context.Background())
@@ -1255,18 +1292,19 @@ func newSshClient(
 	// unthrottled bytes during the initial protocol negotiation.
 
 	client := &sshClient{
-		sshServer:              sshServer,
-		sshListener:            sshListener,
-		tunnelProtocol:         tunnelProtocol,
-		geoIPData:              geoIPData,
-		isFirstTunnelInSession: true,
-		tcpPortForwardLRU:      common.NewLRUConns(),
-		signalIssueSLOKs:       make(chan struct{}, 1),
-		runCtx:                 runCtx,
-		stopRunning:            stopRunning,
-		stopped:                make(chan struct{}),
-		sendAlertRequests:      make(chan protocol.AlertRequest, ALERT_REQUEST_QUEUE_BUFFER_SIZE),
-		sentAlertRequests:      make(map[protocol.AlertRequest]bool),
+		sshServer:                sshServer,
+		sshListener:              sshListener,
+		tunnelProtocol:           tunnelProtocol,
+		serverPacketManipulation: serverPacketManipulation,
+		geoIPData:                geoIPData,
+		isFirstTunnelInSession:   true,
+		tcpPortForwardLRU:        common.NewLRUConns(),
+		signalIssueSLOKs:         make(chan struct{}, 1),
+		runCtx:                   runCtx,
+		stopRunning:              stopRunning,
+		stopped:                  make(chan struct{}),
+		sendAlertRequests:        make(chan protocol.AlertRequest, ALERT_REQUEST_QUEUE_BUFFER_SIZE),
+		sentAlertRequests:        make(map[protocol.AlertRequest]bool),
 	}
 
 	client.tcpTrafficState.availablePortForwardCond = sync.NewCond(new(sync.Mutex))
@@ -2261,6 +2299,9 @@ func (sshClient *sshClient) logTunnel(additionalMetrics []LogFields) {
 	// unconditionally, overwriting any value from handshake.
 	logFields["relay_protocol"] = sshClient.tunnelProtocol
 
+	if sshClient.serverPacketManipulation != "" {
+		logFields["server_packet_manipulation"] = sshClient.serverPacketManipulation
+	}
 	if sshClient.sshListener.BPFProgramName != "" {
 		logFields["server_bpf"] = sshClient.sshListener.BPFProgramName
 	}

+ 33 - 22
psiphon/serverApi.go

@@ -33,6 +33,7 @@ import (
 	"net/url"
 	"strconv"
 	"strings"
+	"time"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/buildinfo"
@@ -383,9 +384,9 @@ func (serverContext *ServerContext) DoConnectedRequest() error {
 
 	params["last_connected"] = lastConnected
 
-	// serverContext.tunnel.establishDuration is nanoseconds; divide to get to milliseconds
+	// serverContext.tunnel.establishDuration is nanoseconds; report milliseconds
 	params["establishment_duration"] =
-		fmt.Sprintf("%d", serverContext.tunnel.establishDuration/1000000)
+		fmt.Sprintf("%d", serverContext.tunnel.establishDuration/time.Millisecond)
 
 	var response []byte
 	if serverContext.psiphonHttpsClient == nil {
@@ -601,33 +602,38 @@ func confirmStatusRequestPayload(payloadInfo *statusRequestPayloadInfo) {
 	}
 }
 
-// RecordRemoteServerListStat records a completed common or OSL
-// remote server list resource download.
+// RecordRemoteServerListStat records a completed common or OSL remote server
+// list resource download.
 //
-// The RSL download event could occur when the client is unable
-// to immediately send a status request to a server, so these
-// records are stored in the persistent datastore and reported
-// via subsequent status requests sent to any Psiphon server.
+// The RSL download event could occur when the client is unable to immediately
+// send a status request to a server, so these records are stored in the
+// persistent datastore and reported via subsequent status requests sent to
+// any Psiphon server.
 //
-// Note that some common event field values may change between the
-// stat recording and reporting, including client geolocation and
-// host_id.
+// Note that some common event field values may change between the stat
+// recording and reporting, including client geolocation and host_id.
 //
-// Multiple "status" requests may be in flight at once (due
-// to multi-tunnel, asynchronous final status retry, and
-// aggressive status requests for pre-registered tunnels),
-// To avoid duplicate reporting, persistent stats records are
-// "taken-out" by a status request and then "put back" in
-// case the request fails.
+// The bytes/duration fields reflect the size and download time for the _last
+// chunk only_ in the case of a resumed download. The purpose of these fields
+// is to calculate rough data transfer rates. Both bytes and duration are
+// included in the log, to allow for filtering out of small transfers which
+// may not produce accurate rate numbers.
 //
-// Duplicate reporting may also occur when a server receives and
-// processes a status request but the client fails to receive
-// the response.
+// Multiple "status" requests may be in flight at once (due to multi-tunnel,
+// asynchronous final status retry, and aggressive status requests for
+// pre-registered tunnels), To avoid duplicate reporting, persistent stats
+// records are "taken-out" by a status request and then "put back" in case the
+// request fails.
+//
+// Duplicate reporting may also occur when a server receives and processes a
+// status request but the client fails to receive the response.
 func RecordRemoteServerListStat(
 	config *Config,
 	tunneled bool,
 	url string,
 	etag string,
+	bytes int64,
+	duration time.Duration,
 	authenticated bool) error {
 
 	if !config.GetClientParameters().Get().WeightedCoinFlip(
@@ -655,6 +661,11 @@ func RecordRemoteServerListStat(
 	params["tunneled"] = tunneledStr
 	params["url"] = url
 	params["etag"] = etag
+	params["bytes"] = fmt.Sprintf("%d", bytes)
+
+	// duration is nanoseconds; report milliseconds
+	params["duration"] = fmt.Sprintf("%d", duration/time.Millisecond)
+
 	authenticatedStr := "0"
 	if authenticated {
 		authenticatedStr = "1"
@@ -964,8 +975,8 @@ func getBaseAPIParameters(
 			params["egress_region"] = config.EgressRegion
 		}
 
-		// dialParams.DialDuration is nanoseconds; divide to get to milliseconds
-		params["dial_duration"] = fmt.Sprintf("%d", dialParams.DialDuration/1000000)
+		// dialParams.DialDuration is nanoseconds; report milliseconds
+		params["dial_duration"] = fmt.Sprintf("%d", dialParams.DialDuration/time.Millisecond)
 
 		params["candidate_number"] = strconv.Itoa(dialParams.CandidateNumber)
 

+ 7 - 0
psiphon/tactics.go

@@ -177,8 +177,15 @@ func fetchTactics(
 		return tacticsProtocols[index], true
 	}
 
+	// No upstreamProxyErrorCallback is set: for tunnel establishment, the
+	// tactics head start is short, and tunnel connections will eventually post
+	// NoticeUpstreamProxyError for any persistent upstream proxy error
+	// conditions. Non-tunnel establishment cases, such as SendFeedback, which
+	// use tactics are not currently expected to post NoticeUpstreamProxyError.
+
 	dialParams, err := MakeDialParameters(
 		config,
+		nil,
 		canReplay,
 		selectProtocol,
 		serverEntry,

+ 102 - 0
psiphon/tactics_test.go

@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package psiphon
+
+import (
+	"context"
+	"encoding/json"
+	"io/ioutil"
+	"os"
+	"sync/atomic"
+	"testing"
+	"time"
+)
+
+func TestStandAloneGetTactics(t *testing.T) {
+
+	testDataDirName, err := ioutil.TempDir("", "psiphon-tactics-test")
+	if err != nil {
+		t.Fatalf("TempDir failed: %s\n", err)
+	}
+	defer os.RemoveAll(testDataDirName)
+
+	configJSON, err := ioutil.ReadFile("controller_test.config")
+	if err != nil {
+		// Skip, don't fail, if config file is not present
+		t.Skipf("error loading configuration file: %s", err)
+	}
+
+	var modifyConfig map[string]interface{}
+	json.Unmarshal(configJSON, &modifyConfig)
+	modifyConfig["DataRootDirectory"] = testDataDirName
+
+	configJSON, _ = json.Marshal(modifyConfig)
+
+	config, err := LoadConfig(configJSON)
+	if err != nil {
+		t.Fatalf("error processing configuration file: %s", err)
+	}
+
+	err = config.Commit(false)
+	if err != nil {
+		t.Fatalf("error committing configuration file: %s", err)
+	}
+
+	gotTactics := int32(0)
+
+	SetNoticeWriter(NewNoticeReceiver(
+		func(notice []byte) {
+			noticeType, _, err := GetNotice(notice)
+			if err != nil {
+				return
+			}
+			switch noticeType {
+			case "RequestedTactics":
+				atomic.StoreInt32(&gotTactics, 1)
+			}
+		}))
+
+	ctx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second)
+	defer cancelFunc()
+
+	err = OpenDataStore(config)
+	if err != nil {
+		t.Fatalf("error committing initializing datastore: %s", err)
+	}
+
+	untunneledDialConfig := &DialConfig{
+		UpstreamProxyURL: config.UpstreamProxyURL,
+	}
+
+	err = FetchCommonRemoteServerList(ctx, config, 0, nil, untunneledDialConfig)
+	if err != nil {
+		t.Fatalf("error cfetching remote server list: %s", err)
+	}
+
+	// Close the datastore to exercise the OpenDatastore/CloseDatastore
+	// operations in GetTactics.
+	CloseDataStore()
+
+	GetTactics(ctx, config)
+
+	if atomic.LoadInt32(&gotTactics) != 1 {
+		t.Fatalf("failed to get tactics")
+	}
+}

+ 19 - 2
psiphon/upstreamproxy/proxy_http.go

@@ -54,6 +54,7 @@ import (
 	"net/url"
 	"time"
 
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"golang.org/x/net/proxy"
 )
 
@@ -131,7 +132,7 @@ type proxyConn struct {
 func (pc *proxyConn) handshake(addr, username, password string) error {
 	// HACK: prefix addr of the form 'hostname:port' with a 'http' scheme
 	// so it could be parsed by url.Parse
-	reqURL, err := url.Parse("http://" + addr)
+	reqURL, err := common.SafeParseURL("http://" + addr)
 	if err != nil {
 		pc.httpClientConn.Close()
 		pc.authState = HTTP_AUTH_STATE_FAILURE
@@ -206,7 +207,7 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 		if username == "" {
 			pc.httpClientConn.Close()
 			pc.authState = HTTP_AUTH_STATE_FAILURE
-			return proxyError(fmt.Errorf("ho username credentials provided for proxy auth"))
+			return proxyError(fmt.Errorf("no username credentials provided for proxy auth"))
 		}
 		if err == errPersistEOF {
 			// The server may send Connection: close,
@@ -218,6 +219,22 @@ func (pc *proxyConn) handshake(addr, username, password string) error {
 				return err
 			}
 		}
+
+		headers := resp.Header[http.CanonicalHeaderKey("proxy-connection")]
+		for _, header := range headers {
+			if header == "close" {
+				// The server has signaled that it will close the
+				// connection. Create a new ClientConn and continue the
+				// handshake.
+				err = pc.makeNewClientConn()
+				if err != nil {
+					// Already wrapped in proxyError
+					return err
+				}
+				break
+			}
+		}
+
 		return nil
 	}
 	pc.authState = HTTP_AUTH_STATE_FAILURE

+ 4 - 4
psiphon/upstreamproxy/upstreamproxy.go

@@ -23,8 +23,8 @@ import (
 	"fmt"
 	"net"
 	"net/http"
-	"net/url"
 
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
 	"golang.org/x/net/proxy"
 )
 
@@ -58,17 +58,17 @@ func NewProxyDialFunc(config *UpstreamProxyConfig) DialFunc {
 	if config.ProxyURIString == "" {
 		return config.ForwardDialFunc
 	}
-	proxyURI, err := url.Parse(config.ProxyURIString)
+	proxyURI, err := common.SafeParseURL(config.ProxyURIString)
 	if err != nil {
 		return func(network, addr string) (net.Conn, error) {
-			return nil, proxyError(fmt.Errorf("proxyURI url.Parse: %v", err))
+			return nil, proxyError(fmt.Errorf("NewProxyDialFunc: SafeParseURL failed: %v", err))
 		}
 	}
 
 	dialer, err := proxy.FromURL(proxyURI, config)
 	if err != nil {
 		return func(network, addr string) (net.Conn, error) {
-			return nil, proxyError(fmt.Errorf("proxy.FromURL: %v", err))
+			return nil, proxyError(fmt.Errorf("NewProxyDialFunc: proxy.FromURL: %v", err))
 		}
 	}
 	return dialer.Dial

+ 59 - 12
vendor/github.com/elazarl/goproxy/README.md

@@ -2,14 +2,15 @@
 
 [![GoDoc](https://godoc.org/github.com/elazarl/goproxy?status.svg)](https://godoc.org/github.com/elazarl/goproxy)
 [![Join the chat at https://gitter.im/elazarl/goproxy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/elazarl/goproxy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+![Status](https://github.com/elazarl/goproxy/workflows/Go/badge.svg)
 
 Package goproxy provides a customizable HTTP proxy library for Go (golang),
 
 It supports regular HTTP proxy, HTTPS through CONNECT, and "hijacking" HTTPS
 connection using "Man in the Middle" style attack.
 
-The intent of the proxy, is to be usable with reasonable amount of traffic
-yet, customizable and programmable.
+The intent of the proxy is to be usable with reasonable amount of traffic,
+yet customizable and programmable.
 
 The proxy itself is simply a `net/http` handler.
 
@@ -22,7 +23,7 @@ For example, the URL you should use as proxy when running `./bin/basic` is
 
 ## Mailing List
 
-New features would be discussed on the [mailing list](https://groups.google.com/forum/#!forum/goproxy-dev)
+New features will be discussed on the [mailing list](https://groups.google.com/forum/#!forum/goproxy-dev)
 before their development.
 
 ## Latest Stable Release
@@ -32,13 +33,13 @@ Get the latest goproxy from `gopkg.in/elazarl/goproxy.v1`.
 # Why not Fiddler2?
 
 Fiddler is an excellent software with similar intent. However, Fiddler is not
-as customizable as goproxy intend to be. The main difference is, Fiddler is not
+as customizable as goproxy intends to be. The main difference is, Fiddler is not
 intended to be used as a real proxy.
 
 A possible use case that suits goproxy but
-not Fiddler, is, gathering statistics on page load times for a certain website over a week.
+not Fiddler, is gathering statistics on page load times for a certain website over a week.
 With goproxy you could ask all your users to set their proxy to a dedicated machine running a
-goproxy server. Fiddler is a GUI app not designed to be ran like a server for multiple users.
+goproxy server. Fiddler is a GUI app not designed to be run like a server for multiple users.
 
 # A taste of goproxy
 
@@ -90,16 +91,62 @@ proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
 })
 ```
 
-`DstHostIs` returns a `ReqCondition`, that is a function receiving a `Request` and returning a boolean
-we will only process requests that matches the condition. `DstHostIs("www.reddit.com")` will return
+`DstHostIs` returns a `ReqCondition`, that is a function receiving a `Request` and returning a boolean.
+We will only process requests that match the condition. `DstHostIs("www.reddit.com")` will return
 a `ReqCondition` accepting only requests directed to "www.reddit.com".
 
 `DoFunc` will receive a function that will preprocess the request. We can change the request, or
-return a response. If the time is between 8:00am and 17:00pm, we will neglect the request, and
+return a response. If the time is between 8:00am and 17:00pm, we will reject the request, and
 return a precanned text response saying "do not waste your time".
 
 See additional examples in the examples directory.
 
+
+# Type of handlers for manipulating connect/req/resp behavior
+
+There are 3 kinds of useful handlers to manipulate the behavior, as follows:
+
+```go
+// handler called after receiving HTTP CONNECT from the client, and before proxy establish connection 
+// with destination host
+httpsHandlers   []HttpsHandler
+    
+// handler called before proxy send HTTP request to destination host
+reqHandlers     []ReqHandler 
+    
+// handler called after proxy receives HTTP Response from destination host, and before proxy forward 
+// the Response to the client.
+respHandlers    []RespHandler 
+```
+
+Depending on what you want to manipulate, the ways to add handlers to each handler list are:
+
+```go
+// Add handlers to httpsHandlers 
+proxy.OnRequest(Some ReqConditions).HandleConnect(YourHandlerFunc())
+
+// Add handlers to reqHandlers
+proxy.OnRequest(Some ReqConditions).Do(YourReqHandlerFunc())
+
+// Add handlers to respHandlers
+proxy.OnResponse(Some RespConditions).Do(YourRespHandlerFunc())
+```
+
+For example:
+
+```go
+// This rejects the HTTPS request to *.reddit.com during HTTP CONNECT phase
+proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("reddit.*:443$"))).HandleConnect(goproxy.RejectConnect)
+
+// This will NOT reject the HTTPS request with URL ending with gif, due to the fact that proxy 
+// only got the URL.Hostname and URL.Port during the HTTP CONNECT phase if the scheme is HTTPS, which is
+// quiet common these days.
+proxy.OnRequest(goproxy.UrlMatches(regexp.MustCompile(`.*gif$`))).HandleConnect(goproxy.RejectConnect)
+
+// The correct way to manipulate the HTTP request using URL.Path as condition is:
+proxy.OnRequest(goproxy.UrlMatches(regexp.MustCompile(`.*gif$`))).Do(YourReqHandlerFunc())
+```
+
 # What's New
 
 1. Ability to `Hijack` CONNECT requests. See
@@ -108,14 +155,14 @@ See additional examples in the examples directory.
 
 # License
 
-I put the software temporarily under the Go-compatible BSD license,
-if this prevents someone from using the software, do let me know and I'll consider changing it.
+I put the software temporarily under the Go-compatible BSD license.
+If this prevents someone from using the software, do let me know and I'll consider changing it.
 
 At any rate, user feedback is very important for me, so I'll be delighted to know if you're using this package.
 
 # Beta Software
 
-I've received a positive feedback from a few people who use goproxy in production settings.
+I've received positive feedback from a few people who use goproxy in production settings.
 I believe it is good enough for usage.
 
 I'll try to keep reasonable backwards compatibility. In case of a major API change,

+ 6 - 1
vendor/github.com/elazarl/goproxy/counterecryptor.go

@@ -3,6 +3,7 @@ package goproxy
 import (
 	"crypto/aes"
 	"crypto/cipher"
+	"crypto/ecdsa"
 	"crypto/rsa"
 	"crypto/sha256"
 	"crypto/x509"
@@ -21,8 +22,12 @@ func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncr
 	switch key := key.(type) {
 	case *rsa.PrivateKey:
 		keyBytes = x509.MarshalPKCS1PrivateKey(key)
+	case *ecdsa.PrivateKey:
+		if keyBytes, err = x509.MarshalECPrivateKey(key); err != nil {
+			return
+		}
 	default:
-		err = errors.New("only RSA keys supported")
+		err = errors.New("only RSA and ECDSA keys supported")
 		return
 	}
 	h := sha256.New()

+ 11 - 5
vendor/github.com/elazarl/goproxy/ctx.go

@@ -1,6 +1,7 @@
 package goproxy
 
 import (
+	"crypto/tls"
 	"net/http"
 	"regexp"
 )
@@ -19,14 +20,19 @@ type ProxyCtx struct {
 	// call of RespHandler
 	UserData interface{}
 	// Will connect a request to a response
-	Session int64
-	proxy   *ProxyHttpServer
+	Session   int64
+	certStore CertStorage
+	Proxy     *ProxyHttpServer
 }
 
 type RoundTripper interface {
 	RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
 }
 
+type CertStorage interface {
+	Fetch(hostname string, gen func() (*tls.Certificate, error)) (*tls.Certificate, error)
+}
+
 type RoundTripperFunc func(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
 
 func (f RoundTripperFunc) RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error) {
@@ -37,11 +43,11 @@ func (ctx *ProxyCtx) RoundTrip(req *http.Request) (*http.Response, error) {
 	if ctx.RoundTripper != nil {
 		return ctx.RoundTripper.RoundTrip(req, ctx)
 	}
-	return ctx.proxy.Tr.RoundTrip(req)
+	return ctx.Proxy.Tr.RoundTrip(req)
 }
 
 func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
-	ctx.proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...)
+	ctx.Proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...)
 }
 
 // Logf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
@@ -53,7 +59,7 @@ func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
 //		return r, nil
 //	})
 func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) {
-	if ctx.proxy.Verbose {
+	if ctx.Proxy.Verbose {
 		ctx.printf("INFO: "+msg, argv...)
 	}
 }

+ 16 - 0
vendor/github.com/elazarl/goproxy/dispatcher.go

@@ -161,6 +161,22 @@ func ContentTypeIs(typ string, types ...string) RespCondition {
 	})
 }
 
+// StatusCodeIs returns a RespCondition, testing whether or not the HTTP status
+// code is one of the given ints
+func StatusCodeIs(codes ...int) RespCondition {
+	codeSet := make(map[int]bool)
+	for _, c := range codes {
+		codeSet[c] = true
+	}
+	return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
+		if resp == nil {
+			return false
+		}
+		_, codeMatch := codeSet[resp.StatusCode]
+		return codeMatch
+	})
+}
+
 // ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
 // You will use the ReqProxyConds struct to register a ReqHandler, that would filter
 // the request, only if all the given ReqCondition matched.

+ 1 - 1
vendor/github.com/elazarl/goproxy/doc.go

@@ -60,7 +60,7 @@ Finally, we have convenience function to throw a quick response
 
 	proxy.OnResponse(hasGoProxyHeader).DoFunc(func(r*http.Response,ctx *goproxy.ProxyCtx)*http.Response {
 		r.Body.Close()
-		return goproxy.ForbiddenTextResponse(ctx.Req,"Can't see response with X-GoProxy header!")
+		return goproxy.NewResponse(ctx.Req, goproxy.ContentTypeText, http.StatusForbidden, "Can't see response with X-GoProxy header!")
 	})
 
 we close the body of the original repsonse, and return a new 403 response with a short message.

+ 79 - 0
vendor/github.com/elazarl/goproxy/ext/auth/basic.go

@@ -0,0 +1,79 @@
+package auth
+
+import (
+	"bytes"
+	"encoding/base64"
+	"io/ioutil"
+	"net/http"
+	"strings"
+
+	"github.com/elazarl/goproxy"
+)
+
+var unauthorizedMsg = []byte("407 Proxy Authentication Required")
+
+func BasicUnauthorized(req *http.Request, realm string) *http.Response {
+	// TODO(elazar): verify realm is well formed
+	return &http.Response{
+		StatusCode: 407,
+		ProtoMajor: 1,
+		ProtoMinor: 1,
+		Request:    req,
+		Header: http.Header{
+			"Proxy-Authenticate": []string{"Basic realm=" + realm},
+			"Proxy-Connection":   []string{"close"},
+		},
+		Body:          ioutil.NopCloser(bytes.NewBuffer(unauthorizedMsg)),
+		ContentLength: int64(len(unauthorizedMsg)),
+	}
+}
+
+var proxyAuthorizationHeader = "Proxy-Authorization"
+
+func auth(req *http.Request, f func(user, passwd string) bool) bool {
+	authheader := strings.SplitN(req.Header.Get(proxyAuthorizationHeader), " ", 2)
+	req.Header.Del(proxyAuthorizationHeader)
+	if len(authheader) != 2 || authheader[0] != "Basic" {
+		return false
+	}
+	userpassraw, err := base64.StdEncoding.DecodeString(authheader[1])
+	if err != nil {
+		return false
+	}
+	userpass := strings.SplitN(string(userpassraw), ":", 2)
+	if len(userpass) != 2 {
+		return false
+	}
+	return f(userpass[0], userpass[1])
+}
+
+// Basic returns a basic HTTP authentication handler for requests
+//
+// You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities
+func Basic(realm string, f func(user, passwd string) bool) goproxy.ReqHandler {
+	return goproxy.FuncReqHandler(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
+		if !auth(req, f) {
+			return nil, BasicUnauthorized(req, realm)
+		}
+		return req, nil
+	})
+}
+
+// BasicConnect returns a basic HTTP authentication handler for CONNECT requests
+//
+// You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities
+func BasicConnect(realm string, f func(user, passwd string) bool) goproxy.HttpsHandler {
+	return goproxy.FuncHttpsHandler(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
+		if !auth(ctx.Req, f) {
+			ctx.Resp = BasicUnauthorized(ctx.Req, realm)
+			return goproxy.RejectConnect, host
+		}
+		return goproxy.OkConnect, host
+	})
+}
+
+// ProxyBasic will force HTTP authentication before any request to the proxy is processed
+func ProxyBasic(proxy *goproxy.ProxyHttpServer, realm string, f func(user, passwd string) bool) {
+	proxy.OnRequest().Do(Basic(realm, f))
+	proxy.OnRequest().HandleConnect(BasicConnect(realm, f))
+}

+ 3 - 0
vendor/github.com/elazarl/goproxy/go.mod

@@ -0,0 +1,3 @@
+module github.com/elazarl/goproxy
+
+require github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2

+ 3 - 0
vendor/github.com/elazarl/goproxy/go.sum

@@ -0,0 +1,3 @@
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
+github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
+github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=

+ 51 - 12
vendor/github.com/elazarl/goproxy/https.go

@@ -36,6 +36,10 @@ var (
 	httpsRegexp     = regexp.MustCompile(`^https:\/\/`)
 )
 
+// ConnectAction enables the caller to override the standard connect flow.
+// When Action is ConnectHijack, it is up to the implementer to send the
+// HTTP 200, or any other valid http response back to the client from within the
+// Hijack func
 type ConnectAction struct {
 	Action    ConnectActionLiteral
 	Hijack    func(req *http.Request, client net.Conn, ctx *ProxyCtx)
@@ -64,8 +68,16 @@ func (proxy *ProxyHttpServer) connectDial(network, addr string) (c net.Conn, err
 	return proxy.ConnectDial(network, addr)
 }
 
+type halfClosable interface {
+	net.Conn
+	CloseWrite() error
+	CloseRead() error
+}
+
+var _ halfClosable = (*net.TCPConn)(nil)
+
 func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request) {
-	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}
+	ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy, certStore: proxy.CertStore}
 
 	hij, ok := w.(http.Hijacker)
 	if !ok {
@@ -102,8 +114,8 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
 		ctx.Logf("Accepting CONNECT to %s", host)
 		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
 
-		targetTCP, targetOK := targetSiteCon.(*net.TCPConn)
-		proxyClientTCP, clientOK := proxyClient.(*net.TCPConn)
+		targetTCP, targetOK := targetSiteCon.(halfClosable)
+		proxyClientTCP, clientOK := proxyClient.(halfClosable)
 		if targetOK && clientOK {
 			go copyAndClose(ctx, targetTCP, proxyClientTCP)
 			go copyAndClose(ctx, proxyClientTCP, targetTCP)
@@ -121,8 +133,6 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
 		}
 
 	case ConnectHijack:
-		ctx.Logf("Hijacking CONNECT to %s", host)
-		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
 		todo.Hijack(r, proxyClient, ctx)
 	case ConnectHTTPMitm:
 		proxyClient.Write([]byte("HTTP/1.0 200 OK\r\n\r\n"))
@@ -188,7 +198,7 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
 			clientTlsReader := bufio.NewReader(rawClientTls)
 			for !isEof(clientTlsReader) {
 				req, err := http.ReadRequest(clientTlsReader)
-				var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}
+				var ctx = &ProxyCtx{Req: req, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy, UserData: ctx.UserData}
 				if err != nil && err != io.EOF {
 					return
 				}
@@ -209,6 +219,11 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
 
 				req, resp := proxy.filterRequest(req, ctx)
 				if resp == nil {
+					if isWebSocketRequest(req) {
+						ctx.Logf("Request looks like websocket upgrade.")
+						proxy.serveWebsocketTLS(ctx, w, req, tlsConfig, rawClientTls)
+						return
+					}
 					if err != nil {
 						ctx.Warnf("Illegal URL %s", "https://"+r.Host+req.URL.Path)
 						return
@@ -293,7 +308,7 @@ func copyOrWarn(ctx *ProxyCtx, dst io.Writer, src io.Reader, wg *sync.WaitGroup)
 	wg.Done()
 }
 
-func copyAndClose(ctx *ProxyCtx, dst, src *net.TCPConn) {
+func copyAndClose(ctx *ProxyCtx, dst, src halfClosable) {
 	if _, err := io.Copy(dst, src); err != nil {
 		ctx.Warnf("Error copying to client: %s", err)
 	}
@@ -314,6 +329,10 @@ func dialerFromEnv(proxy *ProxyHttpServer) func(network, addr string) (net.Conn,
 }
 
 func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(network, addr string) (net.Conn, error) {
+	return proxy.NewConnectDialToProxyWithHandler(https_proxy, nil)
+}
+
+func (proxy *ProxyHttpServer) NewConnectDialToProxyWithHandler(https_proxy string, connectReqHandler func(req *http.Request)) func(network, addr string) (net.Conn, error) {
 	u, err := url.Parse(https_proxy)
 	if err != nil {
 		return nil
@@ -329,6 +348,9 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net
 				Host:   addr,
 				Header: make(http.Header),
 			}
+			if connectReqHandler != nil {
+				connectReqHandler(connectReq)
+			}
 			c, err := proxy.dial(network, u.Host)
 			if err != nil {
 				return nil, err
@@ -355,7 +377,7 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net
 			return c, nil
 		}
 	}
-	if u.Scheme == "https" {
+	if u.Scheme == "https" || u.Scheme == "wss" {
 		if strings.IndexRune(u.Host, ':') == -1 {
 			u.Host += ":443"
 		}
@@ -371,6 +393,9 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net
 				Host:   addr,
 				Header: make(http.Header),
 			}
+			if connectReqHandler != nil {
+				connectReqHandler(connectReq)
+			}
 			connectReq.Write(c)
 			// Read response.
 			// Okay to use and discard buffered reader here, because
@@ -398,14 +423,28 @@ func (proxy *ProxyHttpServer) NewConnectDialToProxy(https_proxy string) func(net
 
 func TLSConfigFromCA(ca *tls.Certificate) func(host string, ctx *ProxyCtx) (*tls.Config, error) {
 	return func(host string, ctx *ProxyCtx) (*tls.Config, error) {
-		config := *defaultTLSConfig
+		var err error
+		var cert *tls.Certificate
+
+		hostname := stripPort(host)
+		config := defaultTLSConfig.Clone()
 		ctx.Logf("signing for %s", stripPort(host))
-		cert, err := signHost(*ca, []string{stripPort(host)})
+
+		genCert := func() (*tls.Certificate, error) {
+			return signHost(*ca, []string{hostname})
+		}
+		if ctx.certStore != nil {
+			cert, err = ctx.certStore.Fetch(hostname, genCert)
+		} else {
+			cert, err = genCert()
+		}
+
 		if err != nil {
 			ctx.Warnf("Cannot sign host certificate with provided CA: %s", err)
 			return nil, err
 		}
-		config.Certificates = append(config.Certificates, cert)
-		return &config, nil
+
+		config.Certificates = append(config.Certificates, *cert)
+		return config, nil
 	}
 }

+ 5 - 0
vendor/github.com/elazarl/goproxy/logger.go

@@ -0,0 +1,5 @@
+package goproxy
+
+type Logger interface {
+	Printf(format string, v ...interface{})
+}

+ 60 - 18
vendor/github.com/elazarl/goproxy/proxy.go

@@ -16,9 +16,11 @@ type ProxyHttpServer struct {
 	// session variable must be aligned in i386
 	// see http://golang.org/src/pkg/sync/atomic/doc.go#L41
 	sess int64
+	// KeepDestinationHeaders indicates the proxy should retain any headers present in the http.Response before proxying
+	KeepDestinationHeaders bool
 	// setting Verbose to true will log information on each request sent to the proxy
 	Verbose         bool
-	Logger          *log.Logger
+	Logger          Logger
 	NonproxyHandler http.Handler
 	reqHandlers     []ReqHandler
 	respHandlers    []RespHandler
@@ -27,13 +29,17 @@ type ProxyHttpServer struct {
 	// ConnectDial will be used to create TCP connections for CONNECT requests
 	// if nil Tr.Dial will be used
 	ConnectDial func(network string, addr string) (net.Conn, error)
+	CertStore   CertStorage
+	KeepHeader  bool
 }
 
 var hasPort = regexp.MustCompile(`:\d+$`)
 
-func copyHeaders(dst, src http.Header) {
-	for k, _ := range dst {
-		dst.Del(k)
+func copyHeaders(dst, src http.Header, keepDestHeaders bool) {
+	if !keepDestHeaders {
+		for k := range dst {
+			dst.Del(k)
+		}
 	}
 	for k, vs := range src {
 		for _, v := range vs {
@@ -88,6 +94,16 @@ func removeProxyHeaders(ctx *ProxyCtx, r *http.Request) {
 	//   The Connection general-header field allows the sender to specify
 	//   options that are desired for that particular connection and MUST NOT
 	//   be communicated by proxies over further connections.
+
+	// When server reads http request it sets req.Close to true if
+	// "Connection" header contains "close".
+	// https://github.com/golang/go/blob/master/src/net/http/request.go#L1080
+	// Later, transfer.go adds "Connection: close" back when req.Close is true
+	// https://github.com/golang/go/blob/master/src/net/http/transfer.go#L275
+	// That's why tests that checks "Connection: close" removal fail
+	if r.Header.Get("Connection") == "close" {
+		r.Close = false
+	}
 	r.Header.Del("Connection")
 }
 
@@ -97,7 +113,7 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	if r.Method == "CONNECT" {
 		proxy.handleHttps(w, r)
 	} else {
-		ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), proxy: proxy}
+		ctx := &ProxyCtx{Req: r, Session: atomic.AddInt64(&proxy.sess, 1), Proxy: proxy}
 
 		var err error
 		ctx.Logf("Got request %v %v %v %v", r.URL.Path, r.Host, r.Method, r.URL.String())
@@ -108,22 +124,47 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		r, resp := proxy.filterRequest(r, ctx)
 
 		if resp == nil {
-			removeProxyHeaders(ctx, r)
+			if isWebSocketRequest(r) {
+				ctx.Logf("Request looks like websocket upgrade.")
+				proxy.serveWebsocket(ctx, w, r)
+			}
+
+			if !proxy.KeepHeader {
+				removeProxyHeaders(ctx, r)
+			}
 			resp, err = ctx.RoundTrip(r)
 			if err != nil {
 				ctx.Error = err
 				resp = proxy.filterResponse(nil, ctx)
-				if resp == nil {
-					ctx.Logf("error read response %v %v:", r.URL.Host, err.Error())
-					http.Error(w, err.Error(), 500)
-					return
-				}
+
+			}
+			if resp != nil {
+				ctx.Logf("Received response %v", resp.Status)
 			}
-			ctx.Logf("Received response %v", resp.Status)
 		}
-		origBody := resp.Body
+
+		var origBody io.ReadCloser
+
+		if resp != nil {
+			origBody = resp.Body
+			defer origBody.Close()
+		}
+
 		resp = proxy.filterResponse(resp, ctx)
-		defer origBody.Close()
+
+		if resp == nil {
+			var errorString string
+			if ctx.Error != nil {
+				errorString = "error read response " + r.URL.Host + " : " + ctx.Error.Error()
+				ctx.Logf(errorString)
+				http.Error(w, ctx.Error.Error(), 500)
+			} else {
+				errorString = "error read response " + r.URL.Host
+				ctx.Logf(errorString)
+				http.Error(w, errorString, 500)
+			}
+			return
+		}
 		ctx.Logf("Copying response to client %v [%d]", resp.Status, resp.StatusCode)
 		// http.ResponseWriter will take care of filling the correct response length
 		// Setting it now, might impose wrong value, contradicting the actual new
@@ -134,7 +175,7 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		if origBody != resp.Body {
 			resp.Header.Del("Content-Length")
 		}
-		copyHeaders(w.Header(), resp.Header)
+		copyHeaders(w.Header(), resp.Header, proxy.KeepDestinationHeaders)
 		w.WriteHeader(resp.StatusCode)
 		nr, err := io.Copy(w, resp.Body)
 		if err := resp.Body.Close(); err != nil {
@@ -144,7 +185,7 @@ func (proxy *ProxyHttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request)
 	}
 }
 
-// New proxy server, logs to StdErr by default
+// NewProxyHttpServer creates and returns a proxy server, logging to stderr by default
 func NewProxyHttpServer() *ProxyHttpServer {
 	proxy := ProxyHttpServer{
 		Logger:        log.New(os.Stderr, "", log.LstdFlags),
@@ -154,9 +195,10 @@ func NewProxyHttpServer() *ProxyHttpServer {
 		NonproxyHandler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 			http.Error(w, "This is a proxy server. Does not respond to non-proxy requests.", 500)
 		}),
-		Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify,
-			Proxy: http.ProxyFromEnvironment},
+		Tr: &http.Transport{TLSClientConfig: tlsClientSkipVerify, Proxy: http.ProxyFromEnvironment},
 	}
+
 	proxy.ConnectDial = dialerFromEnv(&proxy)
+
 	return &proxy
 }

+ 1 - 0
vendor/github.com/elazarl/goproxy/responses.go

@@ -21,6 +21,7 @@ func NewResponse(r *http.Request, contentType string, status int, body string) *
 	resp.Header = make(http.Header)
 	resp.Header.Add("Content-Type", contentType)
 	resp.StatusCode = status
+	resp.Status = http.StatusText(status)
 	buf := bytes.NewBufferString(body)
 	resp.ContentLength = int64(buf.Len())
 	resp.Body = ioutil.NopCloser(buf)

+ 32 - 9
vendor/github.com/elazarl/goproxy/signer.go

@@ -1,12 +1,17 @@
 package goproxy
 
 import (
+	"crypto"
+	"crypto/ecdsa"
+	"crypto/elliptic"
 	"crypto/rsa"
 	"crypto/sha1"
 	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509/pkix"
+	"fmt"
 	"math/big"
+	"math/rand"
 	"net"
 	"runtime"
 	"sort"
@@ -32,7 +37,7 @@ func hashSortedBigInt(lst []string) *big.Int {
 
 var goproxySignerVersion = ":goroxy1"
 
-func signHost(ca tls.Certificate, hosts []string) (cert tls.Certificate, err error) {
+func signHost(ca tls.Certificate, hosts []string) (cert *tls.Certificate, err error) {
 	var x509ca *x509.Certificate
 
 	// Use the provided ca and not the global GoproxyCa for certificate generation.
@@ -44,9 +49,8 @@ func signHost(ca tls.Certificate, hosts []string) (cert tls.Certificate, err err
 	if err != nil {
 		panic(err)
 	}
-	hash := hashSorted(append(hosts, goproxySignerVersion, ":"+runtime.Version()))
-	serial := new(big.Int)
-	serial.SetBytes(hash)
+
+	serial := big.NewInt(rand.Int63())
 	template := x509.Certificate{
 		// TODO(elazar): instead of this ugly hack, just encode the certificate and hash the binary form.
 		SerialNumber: serial,
@@ -66,22 +70,41 @@ func signHost(ca tls.Certificate, hosts []string) (cert tls.Certificate, err err
 			template.IPAddresses = append(template.IPAddresses, ip)
 		} else {
 			template.DNSNames = append(template.DNSNames, h)
+			template.Subject.CommonName = h
 		}
 	}
+
+	hash := hashSorted(append(hosts, goproxySignerVersion, ":"+runtime.Version()))
 	var csprng CounterEncryptorRand
 	if csprng, err = NewCounterEncryptorRandFromKey(ca.PrivateKey, hash); err != nil {
 		return
 	}
-	var certpriv *rsa.PrivateKey
-	if certpriv, err = rsa.GenerateKey(&csprng, 1024); err != nil {
-		return
+
+	var certpriv crypto.Signer
+	switch ca.PrivateKey.(type) {
+	case *rsa.PrivateKey:
+		if certpriv, err = rsa.GenerateKey(&csprng, 2048); err != nil {
+			return
+		}
+	case *ecdsa.PrivateKey:
+		if certpriv, err = ecdsa.GenerateKey(elliptic.P256(), &csprng); err != nil {
+			return
+		}
+	default:
+		err = fmt.Errorf("unsupported key type %T", ca.PrivateKey)
 	}
+
 	var derBytes []byte
-	if derBytes, err = x509.CreateCertificate(&csprng, &template, x509ca, &certpriv.PublicKey, ca.PrivateKey); err != nil {
+	if derBytes, err = x509.CreateCertificate(&csprng, &template, x509ca, certpriv.Public(), ca.PrivateKey); err != nil {
 		return
 	}
-	return tls.Certificate{
+	return &tls.Certificate{
 		Certificate: [][]byte{derBytes, ca.Certificate[0]},
 		PrivateKey:  certpriv,
 	}, nil
 }
+
+func init() {
+	// Avoid deterministic random numbers
+	rand.Seed(time.Now().UnixNano())
+}

+ 121 - 0
vendor/github.com/elazarl/goproxy/websocket.go

@@ -0,0 +1,121 @@
+package goproxy
+
+import (
+	"bufio"
+	"crypto/tls"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+func headerContains(header http.Header, name string, value string) bool {
+	for _, v := range header[name] {
+		for _, s := range strings.Split(v, ",") {
+			if strings.EqualFold(value, strings.TrimSpace(s)) {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func isWebSocketRequest(r *http.Request) bool {
+	return headerContains(r.Header, "Connection", "upgrade") &&
+		headerContains(r.Header, "Upgrade", "websocket")
+}
+
+func (proxy *ProxyHttpServer) serveWebsocketTLS(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request, tlsConfig *tls.Config, clientConn *tls.Conn) {
+	targetURL := url.URL{Scheme: "wss", Host: req.URL.Host, Path: req.URL.Path}
+
+	// Connect to upstream
+	targetConn, err := tls.Dial("tcp", targetURL.Host, tlsConfig)
+	if err != nil {
+		ctx.Warnf("Error dialing target site: %v", err)
+		return
+	}
+	defer targetConn.Close()
+
+	// Perform handshake
+	if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil {
+		ctx.Warnf("Websocket handshake error: %v", err)
+		return
+	}
+
+	// Proxy wss connection
+	proxy.proxyWebsocket(ctx, targetConn, clientConn)
+}
+
+func (proxy *ProxyHttpServer) serveWebsocket(ctx *ProxyCtx, w http.ResponseWriter, req *http.Request) {
+	targetURL := url.URL{Scheme: "ws", Host: req.URL.Host, Path: req.URL.Path}
+
+	targetConn, err := proxy.connectDial("tcp", targetURL.Host)
+	if err != nil {
+		ctx.Warnf("Error dialing target site: %v", err)
+		return
+	}
+	defer targetConn.Close()
+
+	// Connect to Client
+	hj, ok := w.(http.Hijacker)
+	if !ok {
+		panic("httpserver does not support hijacking")
+	}
+	clientConn, _, err := hj.Hijack()
+	if err != nil {
+		ctx.Warnf("Hijack error: %v", err)
+		return
+	}
+
+	// Perform handshake
+	if err := proxy.websocketHandshake(ctx, req, targetConn, clientConn); err != nil {
+		ctx.Warnf("Websocket handshake error: %v", err)
+		return
+	}
+
+	// Proxy ws connection
+	proxy.proxyWebsocket(ctx, targetConn, clientConn)
+}
+
+func (proxy *ProxyHttpServer) websocketHandshake(ctx *ProxyCtx, req *http.Request, targetSiteConn io.ReadWriter, clientConn io.ReadWriter) error {
+	// write handshake request to target
+	err := req.Write(targetSiteConn)
+	if err != nil {
+		ctx.Warnf("Error writing upgrade request: %v", err)
+		return err
+	}
+
+	targetTLSReader := bufio.NewReader(targetSiteConn)
+
+	// Read handshake response from target
+	resp, err := http.ReadResponse(targetTLSReader, req)
+	if err != nil {
+		ctx.Warnf("Error reading handhsake response  %v", err)
+		return err
+	}
+
+	// Run response through handlers
+	resp = proxy.filterResponse(resp, ctx)
+
+	// Proxy handshake back to client
+	err = resp.Write(clientConn)
+	if err != nil {
+		ctx.Warnf("Error writing handshake response: %v", err)
+		return err
+	}
+	return nil
+}
+
+func (proxy *ProxyHttpServer) proxyWebsocket(ctx *ProxyCtx, dest io.ReadWriter, source io.ReadWriter) {
+	errChan := make(chan error, 2)
+	cp := func(dst io.Writer, src io.Reader) {
+		_, err := io.Copy(dst, src)
+		ctx.Warnf("Websocket error: %v", err)
+		errChan <- err
+	}
+
+	// Start proxying websocket data
+	go cp(dest, source)
+	go cp(source, dest)
+	<-errChan
+}

+ 10 - 0
vendor/github.com/florianl/go-nfqueue/LICENSE

@@ -0,0 +1,10 @@
+MIT License
+===========
+
+Copyright (C) 2018  Florian Lehner <dev@der-flo.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 14 - 0
vendor/github.com/florianl/go-nfqueue/README.md

@@ -0,0 +1,14 @@
+go-nfqueue [![PkgGoDev](https://pkg.go.dev/badge/github.com/florianl/go-nfqueue)](https://pkg.go.dev/github.com/florianl/go-nfqueue) [![Build Status](https://travis-ci.org/florianl/go-nfqueue.svg?branch=master)](https://travis-ci.org/florianl/go-nfqueue) [![Go Report Card](https://goreportcard.com/badge/github.com/florianl/go-nfqueue)](https://goreportcard.com/report/github.com/florianl/go-nfqueue)
+============
+
+This is `go-nfqueue` and it is written in [golang](https://golang.org/). It provides a [C](https://en.wikipedia.org/wiki/C_(programming_language))-binding free API to the netfilter based queue subsystem of the [Linux kernel](https://www.kernel.org).
+
+Privileges
+----------
+
+This package processes information directly from the kernel and therefore it requires special privileges. You can provide this privileges by adjusting the `CAP_NET_ADMIN` capabilities.
+```
+	setcap 'cap_net_admin=+ep' /your/executable
+```
+
+For documentation and more examples please take a look at [![GoDoc](https://godoc.org/github.com/florianl/go-nfqueue?status.svg)](https://godoc.org/github.com/florianl/go-nfqueue)

+ 113 - 0
vendor/github.com/florianl/go-nfqueue/attribute.go

@@ -0,0 +1,113 @@
+package nfqueue
+
+import (
+	"bytes"
+	"encoding/binary"
+	"log"
+	"time"
+
+	"github.com/florianl/go-nfqueue/internal/unix"
+
+	"github.com/mdlayher/netlink"
+)
+
+func extractAttribute(log *log.Logger, a *Attribute, data []byte) error {
+	ad, err := netlink.NewAttributeDecoder(data)
+	if err != nil {
+		return err
+	}
+	ad.ByteOrder = binary.BigEndian
+	for ad.Next() {
+		switch ad.Type() {
+		case nfQaPacketHdr:
+			packetID := binary.BigEndian.Uint32(ad.Bytes()[:4])
+			a.PacketID = &packetID
+			hwProto := binary.BigEndian.Uint16(ad.Bytes()[4:6])
+			a.HwProtocol = &hwProto
+			hook := uint8(ad.Bytes()[6])
+			a.Hook = &hook
+		case nfQaMark:
+			mark := ad.Uint32()
+			a.Mark = &mark
+		case nfQaTimestamp:
+			var sec, usec int64
+			r := bytes.NewReader(ad.Bytes()[:8])
+			if err := binary.Read(r, binary.BigEndian, &sec); err != nil {
+				return err
+			}
+			r = bytes.NewReader(ad.Bytes()[8:])
+			if err := binary.Read(r, binary.BigEndian, &usec); err != nil {
+				return err
+			}
+			timestamp := time.Unix(sec, usec*1000)
+			a.Timestamp = &timestamp
+		case nfQaIfIndexInDev:
+			inDev := ad.Uint32()
+			a.InDev = &inDev
+		case nfQaIfIndexOutDev:
+			outDev := ad.Uint32()
+			a.OutDev = &outDev
+		case nfQaIfIndexPhysInDev:
+			physInDev := ad.Uint32()
+			a.PhysInDev = &physInDev
+		case nfQaIfIndexPhysOutDev:
+			physOutDev := ad.Uint32()
+			a.PhysOutDev = &physOutDev
+		case nfQaHwAddr:
+			hwAddrLen := binary.BigEndian.Uint16(ad.Bytes()[:2])
+			hwAddr := (ad.Bytes())[4 : 4+hwAddrLen]
+			a.HwAddr = &hwAddr
+		case nfQaPayload:
+			payload := ad.Bytes()
+			a.Payload = &payload
+		case nfQaCt:
+			ct := ad.Bytes()
+			a.Ct = &ct
+		case nfQaCtInfo:
+			ctInfo := ad.Uint32()
+			a.CtInfo = &ctInfo
+		case nfQaCapLen:
+			capLen := ad.Uint32()
+			a.CapLen = &capLen
+		case nfQaSkbInfo:
+			skbInfo := ad.Bytes()
+			a.SkbInfo = &skbInfo
+		case nfQaExp:
+			exp := ad.Bytes()
+			a.Exp = &exp
+		case nfQaUID:
+			uid := ad.Uint32()
+			a.UID = &uid
+		case nfQaGID:
+			gid := ad.Uint32()
+			a.GID = &gid
+		case nfQaSecCtx:
+			secCtx := ad.String()
+			a.SecCtx = &secCtx
+		case nfQaL2HDR:
+			l2hdr := ad.Bytes()
+			a.L2Hdr = &l2hdr
+		default:
+			log.Printf("Unknown attribute Type: 0x%x\tData: %v\n", ad.Type(), ad.Bytes())
+		}
+	}
+
+	return ad.Err()
+}
+
+func checkHeader(data []byte) int {
+	if (data[0] == unix.AF_INET || data[0] == unix.AF_INET6) && data[1] == unix.NFNETLINK_V0 {
+		return 4
+	}
+	return 0
+}
+
+func extractAttributes(log *log.Logger, msg []byte) (Attribute, error) {
+	attrs := Attribute{}
+
+	offset := checkHeader(msg[:2])
+	if err := extractAttribute(log, &attrs, msg[offset:]); err != nil {
+		return attrs, err
+	}
+	return attrs, nil
+}

+ 10 - 0
vendor/github.com/florianl/go-nfqueue/doc.go

@@ -0,0 +1,10 @@
+/*
+Package nfqueue provides an API to interact with the nfqueue subsystem of the netfilter family from the linux kernel.
+
+This package processes information directly from the kernel and therefore it requires special privileges. You
+can provide this privileges by adjusting the CAP_NET_ADMIN capabilities.
+
+	setcap 'cap_net_admin=+ep' /your/executable
+
+*/
+package nfqueue

+ 9 - 0
vendor/github.com/florianl/go-nfqueue/go.mod

@@ -0,0 +1,9 @@
+module github.com/florianl/go-nfqueue
+
+require (
+	github.com/mdlayher/netlink v1.1.0
+	github.com/pkg/errors v0.9.1
+	golang.org/x/sys v0.0.0-20200828194041-157a740278f4
+)
+
+go 1.13

+ 36 - 0
vendor/github.com/florianl/go-nfqueue/go.sum

@@ -0,0 +1,36 @@
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
+github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
+github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
+github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
+github.com/mdlayher/netlink v1.0.0 h1:vySPY5Oxnn/8lxAPn2cK6kAzcZzYJl3KriSLO46OT18=
+github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
+github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkfg=
+github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
+golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200828194041-157a740278f4 h1:kCCpuwSAoYJPkNc6x0xT9yTtV4oKtARo4RGBQWOfg9E=
+golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

+ 3 - 0
vendor/github.com/florianl/go-nfqueue/internal/unix/doc.go

@@ -0,0 +1,3 @@
+// Package unix maps constants from golang.org/x/sys/unix to local constant
+// and makes them available for other platforms as well.
+package unix

+ 16 - 0
vendor/github.com/florianl/go-nfqueue/internal/unix/types_linux.go

@@ -0,0 +1,16 @@
+// +build linux
+
+package unix
+
+import (
+	linux "golang.org/x/sys/unix"
+)
+
+// various constants
+const (
+	AF_INET           = linux.AF_INET
+	AF_INET6          = linux.AF_INET6
+	AF_UNSPEC         = linux.AF_UNSPEC
+	NFNETLINK_V0      = linux.NFNETLINK_V0
+	NETLINK_NETFILTER = linux.NETLINK_NETFILTER
+)

+ 11 - 0
vendor/github.com/florianl/go-nfqueue/internal/unix/types_other.go

@@ -0,0 +1,11 @@
+// +build !linux
+
+package unix
+
+const (
+	AF_INET           = 0x2
+	AF_INET6          = 0xa
+	AF_UNSPEC         = 0x0
+	NFNETLINK_V0      = 0x0
+	NETLINK_NETFILTER = 0xc
+)

+ 197 - 0
vendor/github.com/florianl/go-nfqueue/nfqueue.go

@@ -0,0 +1,197 @@
+package nfqueue
+
+import (
+	"context"
+	"encoding/binary"
+	"log"
+
+	"github.com/florianl/go-nfqueue/internal/unix"
+
+	"github.com/mdlayher/netlink"
+	"github.com/pkg/errors"
+)
+
+// devNull satisfies io.Writer, in case *log.Logger is not provided
+type devNull struct{}
+
+func (devNull) Write(p []byte) (int, error) {
+	return 0, nil
+}
+
+// Close the connection to the netfilter queue subsystem
+func (nfqueue *Nfqueue) Close() error {
+	return nfqueue.Con.Close()
+}
+
+// SetVerdictWithMark signals the kernel the next action and the mark for a specified package id
+func (nfqueue *Nfqueue) SetVerdictWithMark(id uint32, verdict, mark int) error {
+	buf := make([]byte, 4)
+	binary.BigEndian.PutUint32(buf, uint32(mark))
+	attributes, err := netlink.MarshalAttributes([]netlink.Attribute{{
+		Type: nfQaMark,
+		Data: buf,
+	}})
+	if err != nil {
+		return err
+	}
+	return nfqueue.setVerdict(id, verdict, false, attributes)
+}
+
+// SetVerdictModPacket signals the kernel the next action for an altered packet
+func (nfqueue *Nfqueue) SetVerdictModPacket(id uint32, verdict int, packet []byte) error {
+	data, err := netlink.MarshalAttributes([]netlink.Attribute{{
+		Type: nfQaPayload,
+		Data: packet,
+	}})
+	if err != nil {
+		return err
+	}
+	return nfqueue.setVerdict(id, verdict, false, data)
+}
+
+// SetVerdictModPacketWithMark signals the kernel the next action and mark for an altered packet
+func (nfqueue *Nfqueue) SetVerdictModPacketWithMark(id uint32, verdict, mark int, packet []byte) error {
+	buf := make([]byte, 4)
+	binary.BigEndian.PutUint32(buf, uint32(mark))
+	data, err := netlink.MarshalAttributes([]netlink.Attribute{{
+		Type: nfQaPayload,
+		Data: packet,
+	},
+		{Type: nfQaMark,
+			Data: buf}})
+	if err != nil {
+		return err
+	}
+	return nfqueue.setVerdict(id, verdict, false, data)
+}
+
+// SetVerdict signals the kernel the next action for a specified package id
+func (nfqueue *Nfqueue) SetVerdict(id uint32, verdict int) error {
+	return nfqueue.setVerdict(id, verdict, false, []byte{})
+}
+
+// SetVerdictBatch signals the kernel the next action for a batch of packages till id
+func (nfqueue *Nfqueue) SetVerdictBatch(id uint32, verdict int) error {
+	return nfqueue.setVerdict(id, verdict, true, []byte{})
+}
+
+// Register your own function as callback for a netfilter queue
+func (nfqueue *Nfqueue) Register(ctx context.Context, fn HookFunc) error {
+	return nfqueue.RegisterWithErrorFunc(ctx, fn, func(err error) int {
+		if opError, ok := err.(*netlink.OpError); ok {
+			if opError.Timeout() || opError.Temporary() {
+				return 0
+			}
+		}
+		nfqueue.logger.Printf("Could not receive message: %v\n", err)
+		return 1
+	})
+}
+
+// RegisterWithErrorFunc is like Register but allows custom error handling
+// for errors encountered when reading from the underlying netlink socket.
+func (nfqueue *Nfqueue) RegisterWithErrorFunc(ctx context.Context, fn HookFunc, errfn ErrorFunc) error {
+	// unbinding existing handler (if any)
+	seq, err := nfqueue.setConfig(unix.AF_UNSPEC, 0, 0, []netlink.Attribute{
+		{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdPfUnbind, 0x0, 0x0, byte(nfqueue.family)}},
+	})
+	if err != nil {
+		return errors.Wrapf(err, "Could not unbind existing handlers (if any)")
+	}
+
+	// binding to family
+	_, err = nfqueue.setConfig(unix.AF_UNSPEC, seq, 0, []netlink.Attribute{
+		{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdPfBind, 0x0, 0x0, byte(nfqueue.family)}},
+	})
+	if err != nil {
+		return errors.Wrapf(err, "Could not bind to family %d", nfqueue.family)
+	}
+
+	// binding to the requested queue
+	_, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
+		{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdBind, 0x0, 0x0, byte(nfqueue.family)}},
+	})
+	if err != nil {
+		return errors.Wrapf(err, "Could not bind to requested queue %d", nfqueue.queue)
+	}
+
+	// set copy mode and buffer size
+	data := append(nfqueue.maxPacketLen, nfqueue.copymode)
+	_, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
+		{Type: nfQaCfgParams, Data: data},
+	})
+	if err != nil {
+		return err
+	}
+
+	var attrs []netlink.Attribute
+	if nfqueue.flags[0] != 0 || nfqueue.flags[1] != 0 || nfqueue.flags[2] != 0 || nfqueue.flags[3] != 0 {
+		// set flags
+		attrs = append(attrs, netlink.Attribute{Type: nfQaCfgFlags, Data: nfqueue.flags})
+		attrs = append(attrs, netlink.Attribute{Type: nfQaCfgMask, Data: nfqueue.flags})
+	}
+	attrs = append(attrs, netlink.Attribute{Type: nfQaCfgQueueMaxLen, Data: nfqueue.maxQueueLen})
+
+	_, err = nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, attrs)
+	if err != nil {
+		return err
+	}
+
+	go nfqueue.socketCallback(ctx, fn, errfn, seq)
+
+	return nil
+}
+
+// /include/uapi/linux/netfilter/nfnetlink.h:struct nfgenmsg{} res_id is Big Endian
+func putExtraHeader(familiy, version uint8, resid uint16) []byte {
+	buf := make([]byte, 2)
+	binary.BigEndian.PutUint16(buf, resid)
+	return append([]byte{familiy, version}, buf...)
+}
+
+func (nfqueue *Nfqueue) setConfig(afFamily uint8, oseq uint32, resid uint16, attrs []netlink.Attribute) (uint32, error) {
+	cmd, err := netlink.MarshalAttributes(attrs)
+	if err != nil {
+		return 0, err
+	}
+	data := putExtraHeader(afFamily, unix.NFNETLINK_V0, resid)
+	data = append(data, cmd...)
+	req := netlink.Message{
+		Header: netlink.Header{
+			Type:     netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgConfig),
+			Flags:    netlink.Request | netlink.Acknowledge,
+			Sequence: oseq,
+		},
+		Data: data,
+	}
+	return nfqueue.execute(req)
+}
+
+func (nfqueue *Nfqueue) execute(req netlink.Message) (uint32, error) {
+	var seq uint32
+
+	reply, e := nfqueue.Con.Execute(req)
+	if e != nil {
+		return 0, e
+	}
+
+	if e := netlink.Validate(req, reply); e != nil {
+		return 0, e
+	}
+	for _, msg := range reply {
+		if seq != 0 {
+			return 0, errors.Wrapf(ErrUnexpMsg, "Number of received messages: %d", len(reply))
+		}
+		seq = msg.Header.Sequence
+	}
+
+	return seq, nil
+}
+
+func parseMsg(log *log.Logger, msg netlink.Message) (Attribute, error) {
+	a, err := extractAttributes(log, msg.Data)
+	if err != nil {
+		return a, err
+	}
+	return a, nil
+}

+ 169 - 0
vendor/github.com/florianl/go-nfqueue/nfqueue_gteq_1.12.go

@@ -0,0 +1,169 @@
+//+build go1.12
+
+package nfqueue
+
+import (
+	"context"
+	"encoding/binary"
+	"log"
+	"time"
+
+	"github.com/florianl/go-nfqueue/internal/unix"
+
+	"github.com/mdlayher/netlink"
+)
+
+// Nfqueue represents a netfilter queue handler
+type Nfqueue struct {
+	// Con is the pure representation of a netlink socket
+	Con *netlink.Conn
+
+	logger *log.Logger
+
+	flags           []byte // uint32
+	maxPacketLen    []byte // uint32
+	family          uint8
+	queue           uint16
+	maxQueueLen     []byte // uint32
+	copymode        uint8
+	setReadTimeout  func() error
+	setWriteTimeout func() error
+}
+
+// Open a connection to the netfilter queue subsystem
+func Open(config *Config) (*Nfqueue, error) {
+	var nfqueue Nfqueue
+
+	if config.Flags >= nfQaCfgFlagMax {
+		return nil, ErrInvFlag
+	}
+
+	con, err := netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{NetNS: config.NetNS})
+	if err != nil {
+		return nil, err
+	}
+	nfqueue.Con = con
+	// default size of copied packages to userspace
+	nfqueue.maxPacketLen = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.maxPacketLen, config.MaxPacketLen)
+	nfqueue.flags = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.flags, config.Flags)
+	nfqueue.queue = config.NfQueue
+	nfqueue.family = config.AfFamily
+	nfqueue.maxQueueLen = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.maxQueueLen, config.MaxQueueLen)
+	if config.Logger == nil {
+		nfqueue.logger = log.New(new(devNull), "", 0)
+	} else {
+		nfqueue.logger = config.Logger
+	}
+	nfqueue.copymode = config.Copymode
+
+	if config.ReadTimeout > 0 {
+		nfqueue.setReadTimeout = func() error {
+			deadline := time.Now().Add(config.ReadTimeout)
+			return nfqueue.Con.SetReadDeadline(deadline)
+		}
+	} else {
+		nfqueue.setReadTimeout = func() error { return nil }
+	}
+	if config.WriteTimeout > 0 {
+		nfqueue.setWriteTimeout = func() error {
+			deadline := time.Now().Add(config.WriteTimeout)
+			return nfqueue.Con.SetWriteDeadline(deadline)
+		}
+	} else {
+		nfqueue.setWriteTimeout = func() error { return nil }
+	}
+
+	return &nfqueue, nil
+}
+
+func (nfqueue *Nfqueue) setVerdict(id uint32, verdict int, batch bool, attributes []byte) error {
+	/*
+		struct nfqnl_msg_verdict_hdr {
+			__be32 verdict;
+			__be32 id;
+		};
+	*/
+
+	if verdict != NfDrop && verdict != NfAccept && verdict != NfStolen && verdict != NfQeueue && verdict != NfRepeat {
+		return ErrInvalidVerdict
+	}
+
+	buf := make([]byte, 4)
+	binary.BigEndian.PutUint32(buf, uint32(id))
+	verdictData := append([]byte{0x0, 0x0, 0x0, byte(verdict)}, buf...)
+	cmd, err := netlink.MarshalAttributes([]netlink.Attribute{
+		{Type: nfQaVerdictHdr, Data: verdictData},
+	})
+	if err != nil {
+		return err
+	}
+	data := putExtraHeader(nfqueue.family, unix.NFNETLINK_V0, nfqueue.queue)
+	data = append(data, cmd...)
+	data = append(data, attributes...)
+	req := netlink.Message{
+		Header: netlink.Header{
+			Flags:    netlink.Request,
+			Sequence: 0,
+		},
+		Data: data,
+	}
+	if batch {
+		req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdictBatch)
+	} else {
+		req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdict)
+	}
+
+	if err := nfqueue.setWriteTimeout(); err != nil {
+		nfqueue.logger.Printf("could not set write timeout: %v\n", err)
+	}
+	_, sErr := nfqueue.Con.Send(req)
+	return sErr
+
+}
+
+func (nfqueue *Nfqueue) socketCallback(ctx context.Context, fn HookFunc, errfn ErrorFunc, seq uint32) {
+	defer func() {
+		// unbinding from queue
+		_, err := nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
+			{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdUnbind, 0x0, 0x0, byte(nfqueue.family)}},
+		})
+		if err != nil {
+			nfqueue.logger.Printf("Could not unbind from queue: %v\n", err)
+			return
+		}
+	}()
+	for {
+		if err := ctx.Err(); err != nil {
+			nfqueue.logger.Printf("Stop receiving nfqueue messages: %v\n", err)
+			return
+		}
+		if err := nfqueue.setReadTimeout(); err != nil {
+			nfqueue.logger.Printf("could not set read timeout: %v\n", err)
+		}
+		replys, err := nfqueue.Con.Receive()
+		if err != nil {
+			if ret := errfn(err); ret != 0 {
+				return
+			}
+			continue
+		}
+		for _, msg := range replys {
+			if msg.Header.Type == netlink.Done {
+				// this is the last message of a batch
+				// continue to receive messages
+				break
+			}
+			m, err := parseMsg(nfqueue.logger, msg)
+			if err != nil {
+				nfqueue.logger.Printf("Could not parse message: %v", err)
+				continue
+			}
+			if ret := fn(m); ret != 0 {
+				return
+			}
+		}
+	}
+}

+ 173 - 0
vendor/github.com/florianl/go-nfqueue/nfqueue_lt_1.12.go

@@ -0,0 +1,173 @@
+//+build !go1.12
+
+package nfqueue
+
+import (
+	"context"
+	"encoding/binary"
+	"log"
+	"sync"
+
+	"github.com/florianl/go-nfqueue/internal/unix"
+
+	"github.com/mdlayher/netlink"
+)
+
+type verdict struct {
+	sync.Mutex
+	data []netlink.Message
+}
+
+// Nfqueue represents a netfilter queue handler
+type Nfqueue struct {
+	// Con is the pure representation of a netlink socket
+	Con *netlink.Conn
+
+	logger *log.Logger
+
+	flags        []byte // uint32
+	maxPacketLen []byte // uint32
+	family       uint8
+	queue        uint16
+	maxQueueLen  []byte // uint32
+	copymode     uint8
+
+	verdicts verdict
+}
+
+// Open a connection to the netfilter queue subsystem
+func Open(config *Config) (*Nfqueue, error) {
+	var nfqueue Nfqueue
+
+	if config.Flags >= nfQaCfgFlagMax {
+		return nil, ErrInvFlag
+	}
+
+	con, err := netlink.Dial(unix.NETLINK_NETFILTER, &netlink.Config{NetNS: config.NetNS})
+	if err != nil {
+		return nil, err
+	}
+	nfqueue.Con = con
+	// default size of copied packages to userspace
+	nfqueue.maxPacketLen = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.maxPacketLen, config.MaxPacketLen)
+	nfqueue.flags = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.flags, config.Flags)
+	nfqueue.queue = config.NfQueue
+	nfqueue.family = config.AfFamily
+	nfqueue.maxQueueLen = []byte{0x00, 0x00, 0x00, 0x00}
+	binary.BigEndian.PutUint32(nfqueue.maxQueueLen, config.MaxQueueLen)
+	if config.Logger == nil {
+		nfqueue.logger = log.New(new(devNull), "", 0)
+	} else {
+		nfqueue.logger = config.Logger
+	}
+	nfqueue.copymode = config.Copymode
+
+	return &nfqueue, nil
+}
+
+func (nfqueue *Nfqueue) setVerdict(id uint32, verdict int, batch bool, attributes []byte) error {
+	/*
+		struct nfqnl_msg_verdict_hdr {
+			__be32 verdict;
+			__be32 id;
+		};
+	*/
+
+	if verdict != NfDrop && verdict != NfAccept && verdict != NfStolen && verdict != NfQeueue && verdict != NfRepeat {
+		return ErrInvalidVerdict
+	}
+
+	buf := make([]byte, 4)
+	binary.BigEndian.PutUint32(buf, uint32(id))
+	verdictData := append([]byte{0x0, 0x0, 0x0, byte(verdict)}, buf...)
+	cmd, err := netlink.MarshalAttributes([]netlink.Attribute{
+		{Type: nfQaVerdictHdr, Data: verdictData},
+	})
+	if err != nil {
+		return err
+	}
+	data := putExtraHeader(nfqueue.family, unix.NFNETLINK_V0, nfqueue.queue)
+	data = append(data, cmd...)
+	data = append(data, attributes...)
+	req := netlink.Message{
+		Header: netlink.Header{
+			Flags:    netlink.Request,
+			Sequence: 0,
+		},
+		Data: data,
+	}
+	if batch {
+		req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdictBatch)
+	} else {
+		req.Header.Type = netlink.HeaderType((nfnlSubSysQueue << 8) | nfQnlMsgVerdict)
+	}
+
+	nfqueue.verdicts.Lock()
+	nfqueue.verdicts.data = append(nfqueue.verdicts.data, req)
+	nfqueue.verdicts.Unlock()
+
+	return nil
+
+}
+
+func (nfqueue *Nfqueue) sendVerdicts() error {
+	nfqueue.verdicts.Lock()
+	defer nfqueue.verdicts.Unlock()
+	if len(nfqueue.verdicts.data) == 0 {
+		return nil
+	}
+	_, err := nfqueue.Con.SendMessages(nfqueue.verdicts.data)
+	if err != nil {
+		nfqueue.logger.Printf("Could not send verdict: %v\n", err)
+		return err
+	}
+	nfqueue.verdicts.data = []netlink.Message{}
+
+	return nil
+}
+
+func (nfqueue *Nfqueue) socketCallback(ctx context.Context, fn HookFunc, errfn ErrorFunc, seq uint32) {
+	defer func() {
+		// unbinding from queue
+		_, err := nfqueue.setConfig(uint8(unix.AF_UNSPEC), seq, nfqueue.queue, []netlink.Attribute{
+			{Type: nfQaCfgCmd, Data: []byte{nfUlnlCfgCmdUnbind, 0x0, 0x0, byte(nfqueue.family)}},
+		})
+		if err != nil {
+			nfqueue.logger.Printf("Could not unbind from queue: %v\n", err)
+			return
+		}
+	}()
+	for {
+		if err := ctx.Err(); err != nil {
+			nfqueue.logger.Printf("Stop receiving nfqueue messages: %v\n", err)
+			return
+		}
+		if err := nfqueue.sendVerdicts(); err != nil {
+			nfqueue.logger.Printf("Could not send verdict(s): %v\n", err)
+		}
+		replys, err := nfqueue.Con.Receive()
+		if err != nil {
+			if ret := errfn(err); ret != 0 {
+				return
+			}
+			continue
+		}
+		for _, msg := range replys {
+			if msg.Header.Type == netlink.Done {
+				// this is the last message of a batch
+				// continue to receive messages
+				break
+			}
+			m, err := parseMsg(nfqueue.logger, msg)
+			if err != nil {
+				nfqueue.logger.Printf("Could not parse message: %v\n", err)
+				continue
+			}
+			if ret := fn(m); ret != 0 {
+				return
+			}
+		}
+	}
+}

+ 162 - 0
vendor/github.com/florianl/go-nfqueue/types.go

@@ -0,0 +1,162 @@
+package nfqueue
+
+import (
+	"errors"
+	"log"
+	"time"
+)
+
+// Attribute contains various elements for nfqueue elements.
+// As not every value is contained in every nfqueue message,
+// the elements inside Attribute are pointers to these values
+// or nil, if not present.
+type Attribute struct {
+	PacketID   *uint32
+	Hook       *uint8
+	Timestamp  *time.Time
+	Mark       *uint32
+	InDev      *uint32
+	PhysInDev  *uint32
+	OutDev     *uint32
+	PhysOutDev *uint32
+	Payload    *[]byte
+	CapLen     *uint32
+	UID        *uint32
+	GID        *uint32
+	SecCtx     *string
+	L2Hdr      *[]byte
+	HwAddr     *[]byte
+	HwProtocol *uint16
+	Ct         *[]byte
+	CtInfo     *uint32
+	SkbInfo    *[]byte
+	Exp        *[]byte
+}
+
+// HookFunc is a function, that receives events from a Netlinkgroup
+// To stop receiving messages on this HookFunc, return something different than 0.
+type HookFunc func(a Attribute) int
+
+// ErrorFunc is a function that receives all errors that happen while reading
+// from a Netlinkgroup. To stop receiving messages return something different than 0.
+type ErrorFunc func(e error) int
+
+// Config contains options for a Conn.
+type Config struct {
+	// Network namespace the Nfqueue needs to operate in. If set to 0 (default),
+	// no network namespace will be entered.
+	NetNS int
+
+	// Queue this Nfqueue socket will be assigned to
+	NfQueue uint16
+	// Maximum number of packages within the Nfqueue.
+	MaxQueueLen uint32
+
+	// Only used in combination with NfQnlCopyPacket.
+	MaxPacketLen uint32
+
+	// Specifies how the kernel handles a packet in the nfqueue queue.
+	Copymode uint8
+
+	// Optional flags for this Nfqueue socket.
+	Flags uint32
+
+	// AfFamily for this Nfqueue socket.
+	AfFamily uint8
+
+	// Time till a read action times out - only available for Go >= 1.12
+	ReadTimeout time.Duration
+
+	// Time till a write action times out - only available for Go >= 1.12
+	WriteTimeout time.Duration
+
+	// Interface to log internals.
+	Logger *log.Logger
+}
+
+// Various errors
+var (
+	ErrRecvMsg        = errors.New("received error message")
+	ErrUnexpMsg       = errors.New("received unexpected message from kernel")
+	ErrInvFlag        = errors.New("invalid Flag")
+	ErrNotLinux       = errors.New("not implemented for OS other than linux")
+	ErrInvalidVerdict = errors.New("invalid verdict")
+)
+
+// nfLogSubSysQueue the netlink subsystem we will query
+const nfnlSubSysQueue = 0x03
+
+const (
+	nfQaUnspec = iota
+	nfQaPacketHdr
+	nfQaVerdictHdr        /* nfqnl_msg_verdict_hrd */
+	nfQaMark              /* __u32 nfmark */
+	nfQaTimestamp         /* nfqnl_msg_packet_timestamp */
+	nfQaIfIndexInDev      /* __u32 ifindex */
+	nfQaIfIndexOutDev     /* __u32 ifindex */
+	nfQaIfIndexPhysInDev  /* __u32 ifindex */
+	nfQaIfIndexPhysOutDev /* __u32 ifindex */
+	nfQaHwAddr            /* nfqnl_msg_packet_hw */
+	nfQaPayload           /* opaque data payload */
+	nfQaCt                /* nf_conntrack_netlink.h */
+	nfQaCtInfo            /* enum ip_conntrack_info */
+	nfQaCapLen            /* __u32 length of captured packet */
+	nfQaSkbInfo           /* __u32 skb meta information */
+	nfQaExp               /* nf_conntrack_netlink.h */
+	nfQaUID               /* __u32 sk uid */
+	nfQaGID               /* __u32 sk gid */
+	nfQaSecCtx            /* security context string */
+	nfQaVLAN              /* nested attribute: packet vlan info */
+	nfQaL2HDR             /* full L2 header */
+)
+
+const (
+	_                  = iota
+	nfQaCfgCmd         /* nfqnl_msg_config_cmd */
+	nfQaCfgParams      /* nfqnl_msg_config_params */
+	nfQaCfgQueueMaxLen /* __u32 */
+	nfQaCfgMask        /* identify which flags to change */
+	nfQaCfgFlags       /* value of these flags (__u32) */
+)
+
+const (
+	_ = iota
+	nfUlnlCfgCmdBind
+	nfUlnlCfgCmdUnbind
+	nfUlnlCfgCmdPfBind
+	nfUlnlCfgCmdPfUnbind
+)
+
+const (
+	nfQnlMsgPacket       = iota
+	nfQnlMsgVerdict      /* verdict from userspace to kernel */
+	nfQnlMsgConfig       /* connect to a particular queue */
+	nfQnlMsgVerdictBatch /* batch from userspace to kernel */
+
+)
+
+// Various configuration flags
+const (
+	NfQaCfgFlagFailOpen  = (1 << iota)
+	NfQaCfgFlagConntrack = (1 << iota)
+	NfQaCfgFlagGSO       = (1 << iota)
+	NfQaCfgFlagUIDGid    = (1 << iota)
+	NfQaCfgFlagSecCx     = (1 << iota)
+	nfQaCfgFlagMax       = (1 << iota)
+)
+
+// copy modes
+const (
+	NfQnlCopyNone = iota
+	NfQnlCopyMeta
+	NfQnlCopyPacket
+)
+
+// Verdicts
+const (
+	NfDrop = iota
+	NfAccept
+	NfStolen
+	NfQeueue
+	NfRepeat
+)

+ 54 - 0
vendor/github.com/google/gopacket/AUTHORS

@@ -0,0 +1,54 @@
+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>
+Christian Mäder <christian.maeder@nine.ch>
+Gernot Vormayr <gvormayr@gmail.com>
+Vitor Garcia Graveto <victor.graveto@gmail.com>
+Elias Chavarria Reyes <elchavar@cisco.com>
+Daniel Rittweiler <ripx80@protonmail.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>
+Jose Selvi <jselvi@pentester.es>
+Yerden Zhumabekov <yerden.zhumabekov@gmail.com>
+Jensen Hwa <jensenhwa@gmail.com>
+
+-----------------------------------------------
+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>

+ 215 - 0
vendor/github.com/google/gopacket/CONTRIBUTING.md

@@ -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.

+ 28 - 0
vendor/github.com/google/gopacket/LICENSE

@@ -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.

+ 12 - 0
vendor/github.com/google/gopacket/README.md

@@ -0,0 +1,12 @@
+# 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)
+
+Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.9 due to x/sys/unix dependencies.
+
+Originally forked from the gopcap project written by Andreas
+Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).

+ 178 - 0
vendor/github.com/google/gopacket/base.go

@@ -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 err
+	}
+	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 err
+	}
+	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
+}

+ 157 - 0
vendor/github.com/google/gopacket/decode.go

@@ -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")
+}

+ 432 - 0
vendor/github.com/google/gopacket/doc.go

@@ -0,0 +1,432 @@
+// 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.
+
+Minimum go version required is 1.5 except for pcapgo/EthernetHandle, afpacket,
+and bsdbpf which need at least 1.7 due to x/sys/unix dependencies.
+
+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.FlowFromEndpoints(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, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)})
+
+ // Implement my layer
+ type MyLayer struct {
+   StrangeHeader []byte
+   payload []byte
+ }
+ func (m MyLayer) LayerType() gopacket.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() {
+     if err := parser.DecodeLayers(packetData, &decoded); err != nil {
+       fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
+       continue
+     }
+     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.
+
+Faster And Customized Decoding with DecodingLayerContainer
+
+By default, DecodingLayerParser uses native map to store and search for a layer
+to decode. Though being versatile, in some cases this solution may be not so
+optimal. For example, if you have only few layers faster operations may be
+provided by sparse array indexing or linear array scan.
+
+To accomodate these scenarios, DecodingLayerContainer interface is introduced
+along with its implementations: DecodingLayerSparse, DecodingLayerArray and
+DecodingLayerMap. You can specify a container implementation to
+DecodingLayerParser with SetDecodingLayerContainer method. Example:
+
+ dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet)
+ dlp.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
+ var eth layers.Ethernet
+ dlp.AddDecodingLayer(&eth)
+ // ... add layers and use DecodingLayerParser as usual...
+
+To skip one level of indirection (though sacrificing some capabilities) you may
+also use DecodingLayerContainer as a decoding tool as it is. In this case you have to
+handle unknown layer types and layer panics by yourself. Example:
+
+ func main() {
+   var eth layers.Ethernet
+   var ip4 layers.IPv4
+   var ip6 layers.IPv6
+   var tcp layers.TCP
+   dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
+   dlc = dlc.Put(&eth)
+   dlc = dlc.Put(&ip4)
+   dlc = dlc.Put(&ip6)
+   dlc = dlc.Put(&tcp)
+   // you may specify some meaningful DecodeFeedback
+   decoder := dlc.LayersDecoder(LayerTypeEthernet, gopacket.NilDecodeFeedback)
+   decoded := make([]gopacket.LayerType, 0, 20)
+   for packetData := range somehowGetPacketData() {
+     lt, err := decoder(packetData, &decoded)
+     if err != nil {
+       fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
+       continue
+     }
+     if lt != gopacket.LayerTypeZero {
+       fmt.Fprintf(os.Stderr, "unknown layer type: %v\n", lt)
+       continue
+     }
+     for _, layerType := range decoded {
+       // examine decoded layertypes just as already shown above
+     }
+   }
+ }
+
+DecodingLayerSparse is the fastest but most effective when LayerType values
+that layers in use can decode are not large because otherwise that would lead
+to bigger memory footprint. DecodingLayerArray is very compact and primarily
+usable if the number of decoding layers is not big (up to ~10-15, but please do
+your own benchmarks). DecodingLayerMap is the most versatile one and used by
+DecodingLayerParser by default. Please refer to tests and benchmarks in layers
+subpackage to further examine usage examples and performance measurements.
+
+You may also choose to implement your own DecodingLayerContainer if you want to
+make use of your own internal packet decoding logic.
+
+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

+ 236 - 0
vendor/github.com/google/gopacket/flows.go

@@ -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 lexicographically.
+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)

+ 288 - 0
vendor/github.com/google/gopacket/gc

@@ -0,0 +1,288 @@
+#!/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
+  go run gen2.go | gofmt > enums_generated.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/afpacket
+  go build
+  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
+pushd ip4defrag
+go test ./...
+popd
+pushd defrag
+go test ./...
+popd
+
+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

+ 9 - 0
vendor/github.com/google/gopacket/go.mod

@@ -0,0 +1,9 @@
+module github.com/google/gopacket
+
+go 1.12
+
+require (
+	golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
+	golang.org/x/net v0.0.0-20190620200207-3b0461eec859
+	golang.org/x/sys v0.0.0-20190412213103-97732733099d
+)

+ 19 - 0
vendor/github.com/google/gopacket/go.sum

@@ -0,0 +1,19 @@
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
+golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

+ 107 - 0
vendor/github.com/google/gopacket/layerclass.go

@@ -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)
+}

+ 118 - 0
vendor/github.com/google/gopacket/layers/arp.go

@@ -0,0 +1,118 @@
+// 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"
+)
+
+// 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 {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return fmt.Errorf("ARP length %d too short", len(data))
+	}
+	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])
+	arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
+	if len(data) < int(arpLength) {
+		df.SetTruncated()
+		return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength)
+	}
+	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]
+
+	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)
+}

+ 166 - 0
vendor/github.com/google/gopacket/layers/asf.go

@@ -0,0 +1,166 @@
+// Copyright 2019 The GoPacket Authors. 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
+
+// This file implements the ASF RMCP payload specified in section 3.2.2.3 of
+// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
+
+import (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP.
+	ASFRMCPEnterprise uint32 = 4542
+)
+
+// ASFDataIdentifier encapsulates fields used to uniquely identify the format of
+// the data block.
+//
+// While the enterprise number is almost always 4542 (ASF-RMCP), we support
+// registering layers using structs of this type as a key in case any users are
+// using OEM-extensions.
+type ASFDataIdentifier struct {
+
+	// Enterprise is the IANA Enterprise Number associated with the entity that
+	// defines the message type. A list can be found at
+	// https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
+	// This can be thought of as the namespace for the message type.
+	Enterprise uint32
+
+	// Type is the message type, defined by the entity associated with the
+	// enterprise above. No pressure, but in the context of EN 4542, 1 byte is
+	// the difference between sending a ping and telling a machine to do an
+	// unconditional power down (0x80 and 0x12 respectively).
+	Type uint8
+}
+
+// LayerType returns the payload layer type corresponding to an ASF message
+// type.
+func (a ASFDataIdentifier) LayerType() gopacket.LayerType {
+	if lt := asfDataLayerTypes[a]; lt != 0 {
+		return lt
+	}
+
+	// some layer types don't have a payload, e.g. ASF-RMCP Presence Ping.
+	return gopacket.LayerTypePayload
+}
+
+// RegisterASFLayerType allows specifying that the data block of ASF packets
+// with a given enterprise number and type should be processed by a given layer
+// type. This overrides any existing registrations, including defaults.
+func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) {
+	asfDataLayerTypes[a] = l
+}
+
+var (
+	// ASFDataIdentifierPresencePong is the message type of the response to a
+	// Presence Ping message. It indicates the sender is ASF-RMCP-aware.
+	ASFDataIdentifierPresencePong = ASFDataIdentifier{
+		Enterprise: ASFRMCPEnterprise,
+		Type:       0x40,
+	}
+
+	// ASFDataIdentifierPresencePing is a message type sent to a managed client
+	// to solicit a Presence Pong response. Clients may ignore this if the RMCP
+	// version is unsupported. Sending this message with a sequence number <255
+	// is the recommended way of finding out whether an implementation sends
+	// RMCP ACKs (e.g. iDRAC does, Super Micro does not).
+	//
+	// Systems implementing IPMI must respond to this ping to conform to the
+	// spec, so it is a good substitute for an ICMP ping.
+	ASFDataIdentifierPresencePing = ASFDataIdentifier{
+		Enterprise: ASFRMCPEnterprise,
+		Type:       0x80,
+	}
+
+	// asfDataLayerTypes is used to find the next layer for a given ASF header.
+	asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{
+		ASFDataIdentifierPresencePong: LayerTypeASFPresencePong,
+	}
+)
+
+// ASF defines ASF's generic RMCP message Data block format. See section
+// 3.2.2.3.
+type ASF struct {
+	BaseLayer
+	ASFDataIdentifier
+
+	// Tag is used to match request/response pairs. The tag of a response is set
+	// to that of the message it is responding to. If a message is
+	// unidirectional, i.e. not part of a request/response pair, this is set to
+	// 255.
+	Tag uint8
+
+	// 1 byte reserved, set to 0x00.
+
+	// Length is the length of this layer's payload in bytes.
+	Length uint8
+}
+
+// LayerType returns LayerTypeASF. It partially satisfies Layer and
+// SerializableLayer.
+func (*ASF) LayerType() gopacket.LayerType {
+	return LayerTypeASF
+}
+
+// CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer.
+func (a *ASF) CanDecode() gopacket.LayerClass {
+	return a.LayerType()
+}
+
+// DecodeFromBytes makes the layer represent the provided bytes. It partially
+// satisfies DecodingLayer.
+func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return fmt.Errorf("invalid ASF data header, length %v less than 8",
+			len(data))
+	}
+
+	a.BaseLayer.Contents = data[:8]
+	a.BaseLayer.Payload = data[8:]
+
+	a.Enterprise = binary.BigEndian.Uint32(data[:4])
+	a.Type = uint8(data[4])
+	a.Tag = uint8(data[5])
+	// 1 byte reserved
+	a.Length = uint8(data[7])
+	return nil
+}
+
+// NextLayerType returns the layer type corresponding to the message type of
+// this ASF data layer. This partially satisfies DecodingLayer.
+func (a *ASF) NextLayerType() gopacket.LayerType {
+	return a.ASFDataIdentifier.LayerType()
+}
+
+// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
+// partially satisfying SerializableLayer.
+func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	payload := b.Bytes()
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+	binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
+	bytes[4] = uint8(a.Type)
+	bytes[5] = a.Tag
+	bytes[6] = 0x00
+	if opts.FixLengths {
+		a.Length = uint8(len(payload))
+	}
+	bytes[7] = a.Length
+	return nil
+}
+
+// decodeASF decodes the byte slice into an RMCP-ASF data struct.
+func decodeASF(data []byte, p gopacket.PacketBuilder) error {
+	return decodingLayerDecoder(&ASF{}, data, p)
+}

+ 194 - 0
vendor/github.com/google/gopacket/layers/asf_presencepong.go

@@ -0,0 +1,194 @@
+// Copyright 2019 The GoPacket Authors. 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
+
+// This file implements the RMCP ASF Presence Pong message, specified in section
+// 3.2.4.3 of
+// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf. It
+// also contains non-competing elements from IPMI v2.0, specified in section
+// 13.2.4 of
+// https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf.
+
+import (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+type (
+	// ASFEntity is the type of individual entities that a Presence Pong
+	// response can indicate support of. The entities currently implemented by
+	// the spec are IPMI and ASFv1.
+	ASFEntity uint8
+
+	// ASFInteraction is the type of individual interactions that a Presence
+	// Pong response can indicate support for. The interactions currently
+	// implemented by the spec are RMCP security extensions. Although not
+	// specified, IPMI uses this field to indicate support for DASH, which is
+	// supported as well.
+	ASFInteraction uint8
+)
+
+const (
+	// ASFDCMIEnterprise is the IANA-assigned Enterprise Number of the Data
+	// Center Manageability Interface Forum. The Presence Pong response's
+	// Enterprise field being set to this value indicates support for DCMI. The
+	// DCMI spec regards the OEM field as reserved, so these should be null.
+	ASFDCMIEnterprise uint32 = 36465
+
+	// ASFPresencePongEntityIPMI ANDs with Presence Pong's supported entities
+	// field if the managed system supports IPMI.
+	ASFPresencePongEntityIPMI ASFEntity = 1 << 7
+
+	// ASFPresencePongEntityASFv1 ANDs with Presence Pong's supported entities
+	// field if the managed system supports ASF v1.0.
+	ASFPresencePongEntityASFv1 ASFEntity = 1
+
+	// ASFPresencePongInteractionSecurityExtensions ANDs with Presence Pong's
+	// supported interactions field if the managed system supports RMCP v2.0
+	// security extensions. See section 3.2.3.
+	ASFPresencePongInteractionSecurityExtensions ASFInteraction = 1 << 7
+
+	// ASFPresencePongInteractionDASH ANDs with Presence Pong's supported
+	// interactions field if the managed system supports DMTF DASH. See
+	// https://www.dmtf.org/standards/dash.
+	ASFPresencePongInteractionDASH ASFInteraction = 1 << 5
+)
+
+// ASFPresencePong defines the structure of a Presence Pong message's payload.
+// See section 3.2.4.3.
+type ASFPresencePong struct {
+	BaseLayer
+
+	// Enterprise is the IANA Enterprise Number of an entity that has defined
+	// OEM-specific capabilities for the managed client. If no such capabilities
+	// exist, this is set to ASF's IANA Enterprise Number.
+	Enterprise uint32
+
+	// OEM identifies OEM-specific capabilities. Its structure is defined by the
+	// OEM. This is set to 0s if no OEM-specific capabilities exist. This
+	// implementation does not change byte order from the wire for this field.
+	OEM [4]byte
+
+	// We break out entities and interactions into separate booleans as
+	// discovery is the entire point of this type of message, so we assume they
+	// are accessed. It also makes gopacket's default layer printing more
+	// useful.
+
+	// IPMI is true if IPMI is supported by the managed system. There is no
+	// explicit version in the specification, however given the dates, this is
+	// assumed to be IPMI v1.0.  Support for IPMI is contained in the "supported
+	// entities" field of the presence pong payload.
+	IPMI bool
+
+	// ASFv1 indicates support for ASF v1.0. This seems somewhat redundant as
+	// ASF must be supported in order to receive a response. This is contained
+	// in the "supported entities" field of the presence pong payload.
+	ASFv1 bool
+
+	// SecurityExtensions indicates support for RMCP Security Extensions,
+	// specified in ASF v2.0. This will always be false for v1.x
+	// implementations. This is contained in the "supported interactions" field
+	// of the presence pong payload. This field is defined in ASF v1.0, but has
+	// no useful value.
+	SecurityExtensions bool
+
+	// DASH is true if DMTF DASH is supported. This is not specified in ASF
+	// v2.0, but in IPMI v2.0, however the former does not preclude it, so we
+	// support it.
+	DASH bool
+
+	// 6 bytes reserved after the entities and interactions fields, set to 0s.
+}
+
+// SupportsDCMI returns whether the Presence Pong message indicates support for
+// the Data Center Management Interface, which is an extension of IPMI v2.0.
+func (a *ASFPresencePong) SupportsDCMI() bool {
+	return a.Enterprise == ASFDCMIEnterprise && a.IPMI && a.ASFv1
+}
+
+// LayerType returns LayerTypeASFPresencePong. It partially satisfies Layer and
+// SerializableLayer.
+func (*ASFPresencePong) LayerType() gopacket.LayerType {
+	return LayerTypeASFPresencePong
+}
+
+// CanDecode returns LayerTypeASFPresencePong. It partially satisfies
+// DecodingLayer.
+func (a *ASFPresencePong) CanDecode() gopacket.LayerClass {
+	return a.LayerType()
+}
+
+// DecodeFromBytes makes the layer represent the provided bytes. It partially
+// satisfies DecodingLayer.
+func (a *ASFPresencePong) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 16 {
+		df.SetTruncated()
+		return fmt.Errorf("invalid ASF presence pong payload, length %v less than 16",
+			len(data))
+	}
+
+	a.BaseLayer.Contents = data[:16]
+	a.BaseLayer.Payload = data[16:]
+
+	a.Enterprise = binary.BigEndian.Uint32(data[:4])
+	copy(a.OEM[:], data[4:8]) // N.B. no byte order change
+	a.IPMI = data[8]&uint8(ASFPresencePongEntityIPMI) != 0
+	a.ASFv1 = data[8]&uint8(ASFPresencePongEntityASFv1) != 0
+	a.SecurityExtensions = data[9]&uint8(ASFPresencePongInteractionSecurityExtensions) != 0
+	a.DASH = data[9]&uint8(ASFPresencePongInteractionDASH) != 0
+	// ignore remaining 6 bytes; should be set to 0s
+	return nil
+}
+
+// NextLayerType returns LayerTypePayload, as there are no further layers to
+// decode. This partially satisfies DecodingLayer.
+func (a *ASFPresencePong) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
+// partially satisfying SerializableLayer.
+func (a *ASFPresencePong) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(16)
+	if err != nil {
+		return err
+	}
+
+	binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
+
+	copy(bytes[4:8], a.OEM[:])
+
+	bytes[8] = 0
+	if a.IPMI {
+		bytes[8] |= uint8(ASFPresencePongEntityIPMI)
+	}
+	if a.ASFv1 {
+		bytes[8] |= uint8(ASFPresencePongEntityASFv1)
+	}
+
+	bytes[9] = 0
+	if a.SecurityExtensions {
+		bytes[9] |= uint8(ASFPresencePongInteractionSecurityExtensions)
+	}
+	if a.DASH {
+		bytes[9] |= uint8(ASFPresencePongInteractionDASH)
+	}
+
+	// zero-out remaining 6 bytes
+	for i := 10; i < len(bytes); i++ {
+		bytes[i] = 0x00
+	}
+
+	return nil
+}
+
+// decodeASFPresencePong decodes the byte slice into an RMCP-ASF Presence Pong
+// struct.
+func decodeASFPresencePong(data []byte, p gopacket.PacketBuilder) error {
+	return decodingLayerDecoder(&ASFPresencePong{}, data, p)
+}

+ 52 - 0
vendor/github.com/google/gopacket/layers/base.go

@@ -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

+ 481 - 0
vendor/github.com/google/gopacket/layers/bfd.go

@@ -0,0 +1,481 @@
+// Copyright 2017 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"
+)
+
+// BFD Control Packet Format
+// -------------------------
+// The current version of BFD's RFC (RFC 5880) contains the following
+// diagram for the BFD Control packet 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
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |Vers |  Diag   |Sta|P|F|C|A|D|M|  Detect Mult  |    Length     |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                       My Discriminator                        |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                      Your Discriminator                       |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                    Desired Min TX Interval                    |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                   Required Min RX Interval                    |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                 Required Min Echo RX Interval                 |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//     An optional Authentication Section MAY be present:
+//
+//      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
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |   Auth Type   |   Auth Len    |    Authentication Data...     |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//
+//     Simple Password Authentication Section 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
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |   Auth Type   |   Auth Len    |  Auth Key ID  |  Password...  |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                              ...                              |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//
+//     Keyed MD5 and Meticulous Keyed MD5 Authentication Section 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
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |   Auth Type   |   Auth Len    |  Auth Key ID  |   Reserved    |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                        Sequence Number                        |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                      Auth Key/Digest...                       |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                              ...                              |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//
+//     Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section 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
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |   Auth Type   |   Auth Len    |  Auth Key ID  |   Reserved    |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                        Sequence Number                        |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                       Auth Key/Hash...                        |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                              ...                              |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+//     From https://tools.ietf.org/rfc/rfc5880.txt
+const bfdMinimumRecordSizeInBytes int = 24
+
+// BFDVersion represents the version as decoded from the BFD control message
+type BFDVersion uint8
+
+// BFDDiagnostic represents diagnostic infomation about a BFD session
+type BFDDiagnostic uint8
+
+// constants that define BFDDiagnostic flags
+const (
+	BFDDiagnosticNone               BFDDiagnostic = 0 // No Diagnostic
+	BFDDiagnosticTimeExpired        BFDDiagnostic = 1 // Control Detection Time Expired
+	BFDDiagnosticEchoFailed         BFDDiagnostic = 2 // Echo Function Failed
+	BFDDiagnosticNeighborSignalDown BFDDiagnostic = 3 // Neighbor Signaled Session Down
+	BFDDiagnosticForwardPlaneReset  BFDDiagnostic = 4 // Forwarding Plane Reset
+	BFDDiagnosticPathDown           BFDDiagnostic = 5 // Path Down
+	BFDDiagnosticConcatPathDown     BFDDiagnostic = 6 // Concatenated Path Down
+	BFDDiagnosticAdminDown          BFDDiagnostic = 7 // Administratively Down
+	BFDDiagnosticRevConcatPathDown  BFDDiagnostic = 8 // Reverse Concatenated Path Dow
+)
+
+// String returns a string version of BFDDiagnostic
+func (bd BFDDiagnostic) String() string {
+	switch bd {
+	default:
+		return "Unknown"
+	case BFDDiagnosticNone:
+		return "None"
+	case BFDDiagnosticTimeExpired:
+		return "Control Detection Time Expired"
+	case BFDDiagnosticEchoFailed:
+		return "Echo Function Failed"
+	case BFDDiagnosticNeighborSignalDown:
+		return "Neighbor Signaled Session Down"
+	case BFDDiagnosticForwardPlaneReset:
+		return "Forwarding Plane Reset"
+	case BFDDiagnosticPathDown:
+		return "Path Down"
+	case BFDDiagnosticConcatPathDown:
+		return "Concatenated Path Down"
+	case BFDDiagnosticAdminDown:
+		return "Administratively Down"
+	case BFDDiagnosticRevConcatPathDown:
+		return "Reverse Concatenated Path Down"
+	}
+}
+
+// BFDState represents the state of a BFD session
+type BFDState uint8
+
+// constants that define BFDState
+const (
+	BFDStateAdminDown BFDState = 0
+	BFDStateDown      BFDState = 1
+	BFDStateInit      BFDState = 2
+	BFDStateUp        BFDState = 3
+)
+
+// String returns a string version of BFDState
+func (s BFDState) String() string {
+	switch s {
+	default:
+		return "Unknown"
+	case BFDStateAdminDown:
+		return "Admin Down"
+	case BFDStateDown:
+		return "Down"
+	case BFDStateInit:
+		return "Init"
+	case BFDStateUp:
+		return "Up"
+	}
+}
+
+// BFDDetectMultiplier represents the negotiated transmit interval,
+// multiplied by this value, provides the Detection Time for the
+// receiving system in Asynchronous mode.
+type BFDDetectMultiplier uint8
+
+// BFDDiscriminator is a unique, nonzero discriminator value used
+// to demultiplex multiple BFD sessions between the same pair of systems.
+type BFDDiscriminator uint32
+
+// BFDTimeInterval represents a time interval in microseconds
+type BFDTimeInterval uint32
+
+// BFDAuthType represents the authentication used in the BFD session
+type BFDAuthType uint8
+
+// constants that define the BFDAuthType
+const (
+	BFDAuthTypeNone                BFDAuthType = 0 // No Auth
+	BFDAuthTypePassword            BFDAuthType = 1 // Simple Password
+	BFDAuthTypeKeyedMD5            BFDAuthType = 2 // Keyed MD5
+	BFDAuthTypeMeticulousKeyedMD5  BFDAuthType = 3 // Meticulous Keyed MD5
+	BFDAuthTypeKeyedSHA1           BFDAuthType = 4 // Keyed SHA1
+	BFDAuthTypeMeticulousKeyedSHA1 BFDAuthType = 5 // Meticulous Keyed SHA1
+)
+
+// String returns a string version of BFDAuthType
+func (at BFDAuthType) String() string {
+	switch at {
+	default:
+		return "Unknown"
+	case BFDAuthTypeNone:
+		return "No Authentication"
+	case BFDAuthTypePassword:
+		return "Simple Password"
+	case BFDAuthTypeKeyedMD5:
+		return "Keyed MD5"
+	case BFDAuthTypeMeticulousKeyedMD5:
+		return "Meticulous Keyed MD5"
+	case BFDAuthTypeKeyedSHA1:
+		return "Keyed SHA1"
+	case BFDAuthTypeMeticulousKeyedSHA1:
+		return "Meticulous Keyed SHA1"
+	}
+}
+
+// BFDAuthKeyID represents the authentication key ID in use for
+// this packet.  This allows multiple keys to be active simultaneously.
+type BFDAuthKeyID uint8
+
+// BFDAuthSequenceNumber represents the sequence number for this packet.
+// For Keyed Authentication, this value is incremented occasionally.  For
+// Meticulous Keyed Authentication, this value is incremented for each
+// successive packet transmitted for a session.  This provides protection
+// against replay attacks.
+type BFDAuthSequenceNumber uint32
+
+// BFDAuthData represents the authentication key or digest
+type BFDAuthData []byte
+
+// BFDAuthHeader represents authentication data used in the BFD session
+type BFDAuthHeader struct {
+	AuthType       BFDAuthType
+	KeyID          BFDAuthKeyID
+	SequenceNumber BFDAuthSequenceNumber
+	Data           BFDAuthData
+}
+
+// Length returns the data length of the BFDAuthHeader based on the
+// authentication type
+func (h *BFDAuthHeader) Length() int {
+	switch h.AuthType {
+	case BFDAuthTypePassword:
+		return 3 + len(h.Data)
+	case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
+		return 8 + len(h.Data)
+	case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
+		return 8 + len(h.Data)
+	default:
+		return 0
+	}
+}
+
+// BFD represents a BFD control message packet whose payload contains
+// the control information required to for a BFD session.
+//
+// References
+// ----------
+//
+// Wikipedia's BFD entry:
+//     https://en.wikipedia.org/wiki/Bidirectional_Forwarding_Detection
+//     This is the best place to get an overview of BFD.
+//
+// RFC 5880 "Bidirectional Forwarding Detection (BFD)" (2010)
+//     https://tools.ietf.org/html/rfc5880
+//     This is the original BFD specification.
+//
+// RFC 5881 "Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop)" (2010)
+//     https://tools.ietf.org/html/rfc5881
+//     Describes the use of the Bidirectional Forwarding Detection (BFD)
+//     protocol over IPv4 and IPv6 for single IP hops.
+type BFD struct {
+	BaseLayer // Stores the packet bytes and payload bytes.
+
+	Version                   BFDVersion          // Version of the BFD protocol.
+	Diagnostic                BFDDiagnostic       // Diagnostic code for last state change
+	State                     BFDState            // Current state
+	Poll                      bool                // Requesting verification
+	Final                     bool                // Responding to a received BFD Control packet that had the Poll (P) bit set.
+	ControlPlaneIndependent   bool                // BFD implementation does not share fate with its control plane
+	AuthPresent               bool                // Authentication Section is present and the session is to be authenticated
+	Demand                    bool                // Demand mode is active
+	Multipoint                bool                // For future point-to-multipoint extensions. Must always be zero
+	DetectMultiplier          BFDDetectMultiplier // Detection time multiplier
+	MyDiscriminator           BFDDiscriminator    // A unique, nonzero discriminator value
+	YourDiscriminator         BFDDiscriminator    // discriminator received from the remote system.
+	DesiredMinTxInterval      BFDTimeInterval     // Minimum interval, in microseconds,  the local system would like to use when transmitting BFD Control packets
+	RequiredMinRxInterval     BFDTimeInterval     // Minimum interval, in microseconds, between received BFD Control packets that this system is capable of supporting
+	RequiredMinEchoRxInterval BFDTimeInterval     // Minimum interval, in microseconds, between received BFD Echo packets that this system is capable of supporting
+	AuthHeader                *BFDAuthHeader      // Authentication data, variable length.
+}
+
+// Length returns the data length of a BFD Control message which
+// changes based on the presence and type of authentication
+// contained in the message
+func (d *BFD) Length() int {
+	if d.AuthPresent && (d.AuthHeader != nil) {
+		return bfdMinimumRecordSizeInBytes + d.AuthHeader.Length()
+	}
+
+	return bfdMinimumRecordSizeInBytes
+}
+
+// LayerType returns the layer type of the BFD object, which is LayerTypeBFD.
+func (d *BFD) LayerType() gopacket.LayerType {
+	return LayerTypeBFD
+}
+
+// decodeBFD analyses a byte slice and attempts to decode it as a BFD
+// control 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 BFD layer.
+func decodeBFD(data []byte, p gopacket.PacketBuilder) error {
+
+	// Attempt to decode the byte slice.
+	d := &BFD{}
+	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 a BFD
+// control packet.
+//
+// Upon succeeds, it loads the BFD object with information about the packet
+// and returns nil.
+// Upon failure, it returns an error (non nil).
+func (d *BFD) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+
+	// If the data block is too short to be a BFD record, then return an error.
+	if len(data) < bfdMinimumRecordSizeInBytes {
+		df.SetTruncated()
+		return errors.New("BFD packet too short")
+	}
+
+	pLen := uint8(data[3])
+	if len(data) != int(pLen) {
+		return errors.New("BFD packet length does not match")
+	}
+
+	// BFD 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 BFD 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.
+	d.Version = BFDVersion(((data[0] & 0xE0) >> 5))
+	d.Diagnostic = BFDDiagnostic(data[0] & 0x1F)
+	data = data[1:]
+
+	d.State = BFDState((data[0] & 0xC0) >> 6)
+	d.Poll = data[0]&0x20 != 0
+	d.Final = data[0]&0x10 != 0
+	d.ControlPlaneIndependent = data[0]&0x08 != 0
+	d.AuthPresent = data[0]&0x04 != 0
+	d.Demand = data[0]&0x02 != 0
+	d.Multipoint = data[0]&0x01 != 0
+	data = data[1:]
+
+	data, d.DetectMultiplier = data[1:], BFDDetectMultiplier(data[0])
+	data, _ = data[1:], uint8(data[0]) // Consume length
+
+	// The remaining fields can just be copied in big endian order.
+	data, d.MyDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
+	data, d.YourDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
+	data, d.DesiredMinTxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
+	data, d.RequiredMinRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
+	data, d.RequiredMinEchoRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
+
+	if d.AuthPresent && (len(data) > 2) {
+		d.AuthHeader = &BFDAuthHeader{}
+		data, d.AuthHeader.AuthType = data[1:], BFDAuthType(data[0])
+		data, _ = data[1:], uint8(data[0]) // Consume length
+		data, d.AuthHeader.KeyID = data[1:], BFDAuthKeyID(data[0])
+
+		switch d.AuthHeader.AuthType {
+		case BFDAuthTypePassword:
+			d.AuthHeader.Data = BFDAuthData(data)
+		case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
+			// Skipped reserved byte
+			data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
+			d.AuthHeader.Data = BFDAuthData(data)
+		case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
+			// Skipped reserved byte
+			data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
+			d.AuthHeader.Data = BFDAuthData(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 (d *BFD) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	data, err := b.PrependBytes(bfdMinimumRecordSizeInBytes)
+	if err != nil {
+		return err
+	}
+
+	// Pack the first few fields into the first 32 bits.
+	data[0] = byte(byte(d.Version<<5) | byte(d.Diagnostic))
+	h := uint8(0)
+	h |= (uint8(d.State) << 6)
+	h |= (uint8(bool2uint8(d.Poll)) << 5)
+	h |= (uint8(bool2uint8(d.Final)) << 4)
+	h |= (uint8(bool2uint8(d.ControlPlaneIndependent)) << 3)
+	h |= (uint8(bool2uint8(d.AuthPresent)) << 2)
+	h |= (uint8(bool2uint8(d.Demand)) << 1)
+	h |= uint8(bool2uint8(d.Multipoint))
+	data[1] = byte(h)
+	data[2] = byte(d.DetectMultiplier)
+	data[3] = byte(d.Length())
+
+	// The remaining fields can just be copied in big endian order.
+	binary.BigEndian.PutUint32(data[4:], uint32(d.MyDiscriminator))
+	binary.BigEndian.PutUint32(data[8:], uint32(d.YourDiscriminator))
+	binary.BigEndian.PutUint32(data[12:], uint32(d.DesiredMinTxInterval))
+	binary.BigEndian.PutUint32(data[16:], uint32(d.RequiredMinRxInterval))
+	binary.BigEndian.PutUint32(data[20:], uint32(d.RequiredMinEchoRxInterval))
+
+	if d.AuthPresent && (d.AuthHeader != nil) {
+		auth, err := b.AppendBytes(int(d.AuthHeader.Length()))
+		if err != nil {
+			return err
+		}
+
+		auth[0] = byte(d.AuthHeader.AuthType)
+		auth[1] = byte(d.AuthHeader.Length())
+		auth[2] = byte(d.AuthHeader.KeyID)
+
+		switch d.AuthHeader.AuthType {
+		case BFDAuthTypePassword:
+			copy(auth[3:], d.AuthHeader.Data)
+		case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
+			auth[3] = byte(0)
+			binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
+			copy(auth[8:], d.AuthHeader.Data)
+		case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
+			auth[3] = byte(0)
+			binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
+			copy(auth[8:], d.AuthHeader.Data)
+		}
+	}
+
+	return nil
+}
+
+// CanDecode returns a set of layers that BFD objects can decode.
+// As BFD objects can only decide the BFD layer, we can return just that layer.
+// Apparently a single layer type implements LayerClass.
+func (d *BFD) CanDecode() gopacket.LayerClass {
+	return LayerTypeBFD
+}
+
+// NextLayerType specifies the next layer that GoPacket should attempt to
+// analyse after this (BFD) layer. As BFD packets do not contain any payload
+// bytes, there are no further layers to analyse.
+func (d *BFD) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// Payload returns an empty byte slice as BFD packets do not carry a payload
+func (d *BFD) Payload() []byte {
+	return nil
+}
+
+// bool2uint8 converts a bool to uint8
+func bool2uint8(b bool) uint8 {
+	if b {
+		return 1
+	}
+	return 0
+}

+ 659 - 0
vendor/github.com/google/gopacket/layers/cdp.go

@@ -0,0 +1,659 @@
+// 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"
+	"errors"
+	"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:], p)
+	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, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) {
+	for len(data) > 0 {
+		if len(data) < 4 {
+			p.SetTruncated()
+			return nil, errors.New("CDP TLV < 4 bytes")
+		}
+		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
+		} else if len(data) < int(val.Length) {
+			p.SetTruncated()
+			return nil, fmt.Errorf("CDP TLV < length %d", val.Length)
+		}
+		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, p)
+	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
+}

+ 109 - 0
vendor/github.com/google/gopacket/layers/ctp.go

@@ -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)
+}

+ 592 - 0
vendor/github.com/google/gopacket/layers/dhcpv4.go

@@ -0,0 +1,592 @@
+// 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"
+	"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 {
+	if len(data) < 240 {
+		df.SetTruncated()
+		return fmt.Errorf("DHCPv4 length %d too short", len(data))
+	}
+	d.Options = d.Options[:0]
+	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 InvalidMagicCookie
+	}
+
+	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
+		}
+	}
+
+	d.Contents = data
+
+	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:
+		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 DecOptionNotEnoughData
+	}
+	o.Type = DHCPOpt(data[0])
+	switch o.Type {
+	case DHCPOptPad, DHCPOptEnd:
+		o.Data = nil
+	default:
+		if len(data) < 2 {
+			return DecOptionNotEnoughData
+		}
+		o.Length = data[1]
+		if int(o.Length) > len(data[2:]) {
+			return DecOptionMalformed
+		}
+		o.Data = data[2 : 2+int(o.Length)]
+	}
+	return nil
+}
+
+// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
+type DHCPv4Error string
+
+// DHCPv4Error implements error interface.
+func (d DHCPv4Error) Error() string {
+	return string(d)
+}
+
+const (
+	// DecOptionNotEnoughData is returned when there is not enough data during option's decode process
+	DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
+	// DecOptionMalformed is returned when the option is malformed
+	DecOptionMalformed = DHCPv4Error("Option is malformed")
+	// InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
+	InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
+)

+ 360 - 0
vendor/github.com/google/gopacket/layers/dhcpv6.go

@@ -0,0 +1,360 @@
+// Copyright 2018 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"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+// DHCPv6MsgType represents a DHCPv6 operation
+type DHCPv6MsgType byte
+
+// Constants that represent DHCP operations
+const (
+	DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
+	DHCPv6MsgTypeSolicit
+	DHCPv6MsgTypeAdverstise
+	DHCPv6MsgTypeRequest
+	DHCPv6MsgTypeConfirm
+	DHCPv6MsgTypeRenew
+	DHCPv6MsgTypeRebind
+	DHCPv6MsgTypeReply
+	DHCPv6MsgTypeRelease
+	DHCPv6MsgTypeDecline
+	DHCPv6MsgTypeReconfigure
+	DHCPv6MsgTypeInformationRequest
+	DHCPv6MsgTypeRelayForward
+	DHCPv6MsgTypeRelayReply
+)
+
+// String returns a string version of a DHCPv6MsgType.
+func (o DHCPv6MsgType) String() string {
+	switch o {
+	case DHCPv6MsgTypeUnspecified:
+		return "Unspecified"
+	case DHCPv6MsgTypeSolicit:
+		return "Solicit"
+	case DHCPv6MsgTypeAdverstise:
+		return "Adverstise"
+	case DHCPv6MsgTypeRequest:
+		return "Request"
+	case DHCPv6MsgTypeConfirm:
+		return "Confirm"
+	case DHCPv6MsgTypeRenew:
+		return "Renew"
+	case DHCPv6MsgTypeRebind:
+		return "Rebind"
+	case DHCPv6MsgTypeReply:
+		return "Reply"
+	case DHCPv6MsgTypeRelease:
+		return "Release"
+	case DHCPv6MsgTypeDecline:
+		return "Decline"
+	case DHCPv6MsgTypeReconfigure:
+		return "Reconfigure"
+	case DHCPv6MsgTypeInformationRequest:
+		return "InformationRequest"
+	case DHCPv6MsgTypeRelayForward:
+		return "RelayForward"
+	case DHCPv6MsgTypeRelayReply:
+		return "RelayReply"
+	default:
+		return "Unknown"
+	}
+}
+
+// DHCPv6 contains data for a single DHCP packet.
+type DHCPv6 struct {
+	BaseLayer
+	MsgType       DHCPv6MsgType
+	HopCount      uint8
+	LinkAddr      net.IP
+	PeerAddr      net.IP
+	TransactionID []byte
+	Options       DHCPv6Options
+}
+
+// LayerType returns gopacket.LayerTypeDHCPv6
+func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("DHCPv6 length %d too short", len(data))
+	}
+	d.BaseLayer = BaseLayer{Contents: data}
+	d.Options = d.Options[:0]
+	d.MsgType = DHCPv6MsgType(data[0])
+
+	offset := 0
+	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
+		if len(data) < 34 {
+			df.SetTruncated()
+			return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType)
+		}
+		d.HopCount = data[1]
+		d.LinkAddr = net.IP(data[2:18])
+		d.PeerAddr = net.IP(data[18:34])
+		offset = 34
+	} else {
+		d.TransactionID = data[1:4]
+		offset = 4
+	}
+
+	stop := len(data)
+	for offset < stop {
+		o := DHCPv6Option{}
+		if err := o.decode(data[offset:]); err != nil {
+			return err
+		}
+		d.Options = append(d.Options, o)
+		offset += int(o.Length) + 4 // 2 from option code, 2 from option length
+	}
+
+	return nil
+}
+
+// Len returns the length of a DHCPv6 packet.
+func (d *DHCPv6) Len() int {
+	n := 1
+	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
+		n += 33
+	} else {
+		n += 3
+	}
+
+	for _, o := range d.Options {
+		n += int(o.Length) + 4 // 2 from option code, 2 from option length
+	}
+
+	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 *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	plen := int(d.Len())
+
+	data, err := b.PrependBytes(plen)
+	if err != nil {
+		return err
+	}
+
+	offset := 0
+	data[0] = byte(d.MsgType)
+	if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
+		data[1] = byte(d.HopCount)
+		copy(data[2:18], d.LinkAddr.To16())
+		copy(data[18:34], d.PeerAddr.To16())
+		offset = 34
+	} else {
+		copy(data[1:4], d.TransactionID)
+		offset = 4
+	}
+
+	if len(d.Options) > 0 {
+		for _, o := range d.Options {
+			if err := o.encode(data[offset:], opts); err != nil {
+				return err
+			}
+			offset += int(o.Length) + 4 // 2 from option code, 2 from option length
+		}
+	}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (d *DHCPv6) CanDecode() gopacket.LayerClass {
+	return LayerTypeDHCPv6
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (d *DHCPv6) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
+	dhcp := &DHCPv6{}
+	err := dhcp.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(dhcp)
+	return p.NextDecoder(gopacket.LayerTypePayload)
+}
+
+// DHCPv6StatusCode represents a DHCP status code - RFC-3315
+type DHCPv6StatusCode uint16
+
+// Constants for the DHCPv6StatusCode.
+const (
+	DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
+	DHCPv6StatusCodeUnspecFail
+	DHCPv6StatusCodeNoAddrsAvail
+	DHCPv6StatusCodeNoBinding
+	DHCPv6StatusCodeNotOnLink
+	DHCPv6StatusCodeUseMulticast
+)
+
+// String returns a string version of a DHCPv6StatusCode.
+func (o DHCPv6StatusCode) String() string {
+	switch o {
+	case DHCPv6StatusCodeSuccess:
+		return "Success"
+	case DHCPv6StatusCodeUnspecFail:
+		return "UnspecifiedFailure"
+	case DHCPv6StatusCodeNoAddrsAvail:
+		return "NoAddressAvailable"
+	case DHCPv6StatusCodeNoBinding:
+		return "NoBinding"
+	case DHCPv6StatusCodeNotOnLink:
+		return "NotOnLink"
+	case DHCPv6StatusCodeUseMulticast:
+		return "UseMulticast"
+	default:
+		return "Unknown"
+	}
+}
+
+// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
+type DHCPv6DUIDType uint16
+
+// Constants for the DHCPv6DUIDType.
+const (
+	DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
+	DHCPv6DUIDTypeEN
+	DHCPv6DUIDTypeLL
+)
+
+// String returns a string version of a DHCPv6DUIDType.
+func (o DHCPv6DUIDType) String() string {
+	switch o {
+	case DHCPv6DUIDTypeLLT:
+		return "LLT"
+	case DHCPv6DUIDTypeEN:
+		return "EN"
+	case DHCPv6DUIDTypeLL:
+		return "LL"
+	default:
+		return "Unknown"
+	}
+}
+
+// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
+type DHCPv6DUID struct {
+	Type DHCPv6DUIDType
+	// LLT, LL
+	HardwareType []byte
+	// EN
+	EnterpriseNumber []byte
+	// LLT
+	Time []byte
+	// LLT, LL
+	LinkLayerAddress net.HardwareAddr
+	// EN
+	Identifier []byte
+}
+
+// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
+func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
+	if len(data) < 2 {
+		return fmt.Errorf("Not enough bytes to decode: %d", len(data))
+	}
+
+	d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
+	if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
+		if len(data) < 4 {
+			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
+		}
+		d.HardwareType = data[2:4]
+	}
+
+	if d.Type == DHCPv6DUIDTypeLLT {
+		if len(data) < 8 {
+			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
+		}
+		d.Time = data[4:8]
+		d.LinkLayerAddress = net.HardwareAddr(data[8:])
+	} else if d.Type == DHCPv6DUIDTypeEN {
+		if len(data) < 6 {
+			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
+		}
+		d.EnterpriseNumber = data[2:6]
+		d.Identifier = data[6:]
+	} else { // DHCPv6DUIDTypeLL
+		if len(data) < 4 {
+			return fmt.Errorf("Not enough bytes to decode: %d", len(data))
+		}
+		d.LinkLayerAddress = net.HardwareAddr(data[4:])
+	}
+
+	return nil
+}
+
+// Encode encodes the DHCPv6DUID in a slice of bytes
+func (d *DHCPv6DUID) Encode() []byte {
+	length := d.Len()
+	data := make([]byte, length)
+	binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
+
+	if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
+		copy(data[2:4], d.HardwareType)
+	}
+
+	if d.Type == DHCPv6DUIDTypeLLT {
+		copy(data[4:8], d.Time)
+		copy(data[8:], d.LinkLayerAddress)
+	} else if d.Type == DHCPv6DUIDTypeEN {
+		copy(data[2:6], d.EnterpriseNumber)
+		copy(data[6:], d.Identifier)
+	} else {
+		copy(data[4:], d.LinkLayerAddress)
+	}
+
+	return data
+}
+
+// Len returns the length of the DHCPv6DUID, respecting the type
+func (d *DHCPv6DUID) Len() int {
+	length := 2 // d.Type
+	if d.Type == DHCPv6DUIDTypeLLT {
+		length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
+	} else if d.Type == DHCPv6DUIDTypeEN {
+		length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
+	} else { // LL
+		length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
+	}
+
+	return length
+}
+
+func (d *DHCPv6DUID) String() string {
+	duid := "Type: " + d.Type.String() + ", "
+	if d.Type == DHCPv6DUIDTypeLLT {
+		duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
+	} else if d.Type == DHCPv6DUIDTypeEN {
+		duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
+	} else { // DHCPv6DUIDTypeLL
+		duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
+	}
+	return duid
+}
+
+func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
+	duid := &DHCPv6DUID{}
+	err := duid.DecodeFromBytes(data)
+	if err != nil {
+		return nil, err
+	}
+	return duid, nil
+}

+ 621 - 0
vendor/github.com/google/gopacket/layers/dhcpv6_options.go

@@ -0,0 +1,621 @@
+// Copyright 2018 The GoPacket Authors. 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"
+	"github.com/google/gopacket"
+)
+
+// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
+type DHCPv6Opt uint16
+
+// Constants for the DHCPv6Opt options.
+const (
+	DHCPv6OptClientID           DHCPv6Opt = 1
+	DHCPv6OptServerID           DHCPv6Opt = 2
+	DHCPv6OptIANA               DHCPv6Opt = 3
+	DHCPv6OptIATA               DHCPv6Opt = 4
+	DHCPv6OptIAAddr             DHCPv6Opt = 5
+	DHCPv6OptOro                DHCPv6Opt = 6
+	DHCPv6OptPreference         DHCPv6Opt = 7
+	DHCPv6OptElapsedTime        DHCPv6Opt = 8
+	DHCPv6OptRelayMessage       DHCPv6Opt = 9
+	DHCPv6OptAuth               DHCPv6Opt = 11
+	DHCPv6OptUnicast            DHCPv6Opt = 12
+	DHCPv6OptStatusCode         DHCPv6Opt = 13
+	DHCPv6OptRapidCommit        DHCPv6Opt = 14
+	DHCPv6OptUserClass          DHCPv6Opt = 15
+	DHCPv6OptVendorClass        DHCPv6Opt = 16
+	DHCPv6OptVendorOpts         DHCPv6Opt = 17
+	DHCPv6OptInterfaceID        DHCPv6Opt = 18
+	DHCPv6OptReconfigureMessage DHCPv6Opt = 19
+	DHCPv6OptReconfigureAccept  DHCPv6Opt = 20
+
+	// RFC 3319 Session Initiation Protocol (SIP)
+	DHCPv6OptSIPServersDomainList  DHCPv6Opt = 21
+	DHCPv6OptSIPServersAddressList DHCPv6Opt = 22
+
+	// RFC 3646 DNS Configuration
+	DHCPv6OptDNSServers DHCPv6Opt = 23
+	DHCPv6OptDomainList DHCPv6Opt = 24
+
+	// RFC 3633 Prefix Delegation
+	DHCPv6OptIAPD     DHCPv6Opt = 25
+	DHCPv6OptIAPrefix DHCPv6Opt = 26
+
+	// RFC 3898 Network Information Service (NIS)
+	DHCPv6OptNISServers     DHCPv6Opt = 27
+	DHCPv6OptNISPServers    DHCPv6Opt = 28
+	DHCPv6OptNISDomainName  DHCPv6Opt = 29
+	DHCPv6OptNISPDomainName DHCPv6Opt = 30
+
+	// RFC 4075 Simple Network Time Protocol (SNTP)
+	DHCPv6OptSNTPServers DHCPv6Opt = 31
+
+	// RFC 4242 Information Refresh Time Option
+	DHCPv6OptInformationRefreshTime DHCPv6Opt = 32
+
+	// RFC 4280 Broadcast and Multicast Control Servers
+	DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33
+	DHCPv6OptBCMCSServerAddressList    DHCPv6Opt = 34
+
+	// RFC 4776 Civic Address ConfigurationOption
+	DHCPv6OptGeoconfCivic DHCPv6Opt = 36
+
+	// RFC 4649 Relay Agent Remote-ID
+	DHCPv6OptRemoteID DHCPv6Opt = 37
+
+	// RFC 4580 Relay Agent Subscriber-ID
+	DHCPv6OptSubscriberID DHCPv6Opt = 38
+
+	// RFC 4704 Client Full Qualified Domain Name (FQDN)
+	DHCPv6OptClientFQDN DHCPv6Opt = 39
+
+	// RFC 5192 Protocol for Carrying Authentication for Network Access (PANA)
+	DHCPv6OptPanaAgent DHCPv6Opt = 40
+
+	// RFC 4833 Timezone Options
+	DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41
+	DHCPv6OptNewTZDBTimezone  DHCPv6Opt = 42
+
+	// RFC 4994 Relay Agent Echo Request
+	DHCPv6OptEchoRequestOption DHCPv6Opt = 43
+
+	// RFC 5007 Leasequery
+	DHCPv6OptLQQuery      DHCPv6Opt = 44
+	DHCPv6OptCLTTime      DHCPv6Opt = 45
+	DHCPv6OptClientData   DHCPv6Opt = 46
+	DHCPv6OptLQRelayData  DHCPv6Opt = 47
+	DHCPv6OptLQClientLink DHCPv6Opt = 48
+
+	// RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6)
+	DHCPv6OptMIP6HNIDF DHCPv6Opt = 49
+	DHCPv6OptMIP6VDINF DHCPv6Opt = 50
+	DHCPv6OptMIP6IDINF DHCPv6Opt = 69
+	DHCPv6OptMIP6UDINF DHCPv6Opt = 70
+	DHCPv6OptMIP6HNP   DHCPv6Opt = 71
+	DHCPv6OptMIP6HAA   DHCPv6Opt = 72
+	DHCPv6OptMIP6HAF   DHCPv6Opt = 73
+
+	// RFC 5223 Discovering Location-to-Service Translation (LoST) Servers
+	DHCPv6OptV6LOST DHCPv6Opt = 51
+
+	// RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP)
+	DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52
+
+	// RFC 5460 Bulk Leasequery
+	DHCPv6OptRelayID DHCPv6Opt = 53
+
+	// RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery
+	DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54
+	DHCPv6OptIPv6FQDNMoS    DHCPv6Opt = 55
+
+	// RFC 5908 NTP Server Option
+	DHCPv6OptNTPServer DHCPv6Opt = 56
+
+	// RFC 5986 Discovering the Local Location Information Server (LIS)
+	DHCPv6OptV6AccessDomain DHCPv6Opt = 57
+
+	// RFC 5986 SIP User Agent
+	DHCPv6OptSIPUACSList DHCPv6Opt = 58
+
+	// RFC 5970 Options for Network Boot
+	DHCPv6OptBootFileURL    DHCPv6Opt = 59
+	DHCPv6OptBootFileParam  DHCPv6Opt = 60
+	DHCPv6OptClientArchType DHCPv6Opt = 61
+	DHCPv6OptNII            DHCPv6Opt = 62
+
+	// RFC 6225 Coordinate-Based Location Configuration Information
+	DHCPv6OptGeolocation DHCPv6Opt = 63
+
+	// RFC 6334 Dual-Stack Lite
+	DHCPv6OptAFTRName DHCPv6Opt = 64
+
+	// RFC 6440 EAP Re-authentication Protocol (ERP)
+	DHCPv6OptERPLocalDomainName DHCPv6Opt = 65
+
+	// RFC 6422 Relay-Supplied DHCP Options
+	DHCPv6OptRSOO DHCPv6Opt = 66
+
+	// RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation
+	DHCPv6OptPDExclude DHCPv6Opt = 67
+
+	// RFC 6607 Virtual Subnet Selection
+	DHCPv6OptVSS DHCPv6Opt = 68
+
+	// RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
+	DHCPv6OptRDNSSSelection DHCPv6Opt = 74
+
+	// RFC 6784 Kerberos Options for DHCPv6
+	DHCPv6OptKRBPrincipalName DHCPv6Opt = 75
+	DHCPv6OptKRBRealmName     DHCPv6Opt = 76
+	DHCPv6OptKRBKDC           DHCPv6Opt = 77
+
+	// RFC 6939 Client Link-Layer Address Option
+	DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79
+
+	// RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents
+	DHCPv6OptLinkAddress DHCPv6Opt = 80
+
+	// RFC 7037 RADIUS Option for the DHCPv6 Relay Agent
+	DHCPv6OptRADIUS DHCPv6Opt = 81
+
+	// RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT
+	DHCPv6OptSolMaxRt DHCPv6Opt = 82
+	DHCPv6OptInfMaxRt DHCPv6Opt = 83
+
+	// RFC 7078 Distributing Address Selection Policy
+	DHCPv6OptAddrSel      DHCPv6Opt = 84
+	DHCPv6OptAddrSelTable DHCPv6Opt = 85
+
+	// RFC 7291 DHCP Options for the Port Control Protocol (PCP)
+	DHCPv6OptV6PCPServer DHCPv6Opt = 86
+
+	// RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport
+	DHCPv6OptDHCPv4Message          DHCPv6Opt = 87
+	DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88
+
+	// RFC 7598 Configuration of Softwire Address and Port-Mapped Clients
+	DHCPv6OptS46Rule           DHCPv6Opt = 89
+	DHCPv6OptS46BR             DHCPv6Opt = 90
+	DHCPv6OptS46DMR            DHCPv6Opt = 91
+	DHCPv6OptS46V4V4Bind       DHCPv6Opt = 92
+	DHCPv6OptS46PortParameters DHCPv6Opt = 93
+	DHCPv6OptS46ContMAPE       DHCPv6Opt = 94
+	DHCPv6OptS46ContMAPT       DHCPv6Opt = 95
+	DHCPv6OptS46ContLW         DHCPv6Opt = 96
+
+	// RFC 7600 IPv4 Residual Deployment via IPv6
+	DHCPv6Opt4RD           DHCPv6Opt = 97
+	DHCPv6Opt4RDMapRule    DHCPv6Opt = 98
+	DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99
+
+	// RFC 7653 Active Leasequery
+	DHCPv6OptLQBaseTime  DHCPv6Opt = 100
+	DHCPv6OptLQStartTime DHCPv6Opt = 101
+	DHCPv6OptLQEndTime   DHCPv6Opt = 102
+
+	// RFC 7710 Captive-Portal Identification
+	DHCPv6OptCaptivePortal DHCPv6Opt = 103
+
+	// RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration
+	DHCPv6OptMPLParameters DHCPv6Opt = 104
+
+	// RFC 7839 Access-Network-Identifier (ANI)
+	DHCPv6OptANIATT           DHCPv6Opt = 105
+	DHCPv6OptANINetworkName   DHCPv6Opt = 106
+	DHCPv6OptANIAPName        DHCPv6Opt = 107
+	DHCPv6OptANIAPBSSID       DHCPv6Opt = 108
+	DHCPv6OptANIOperatorID    DHCPv6Opt = 109
+	DHCPv6OptANIOperatorRealm DHCPv6Opt = 110
+
+	// RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE)
+	DHCPv6OptS46Priority DHCPv6Opt = 111
+
+	// draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD)
+	DHCPv6OptMUDURLV6 DHCPv6Opt = 112
+
+	// RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes
+	DHCPv6OptV6Prefix64 DHCPv6Opt = 113
+
+	// RFC 8156 DHCPv6 Failover Protocol
+	DHCPv6OptFBindingStatus           DHCPv6Opt = 114
+	DHCPv6OptFConnectFlags            DHCPv6Opt = 115
+	DHCPv6OptFDNSRemovalInfo          DHCPv6Opt = 116
+	DHCPv6OptFDNSHostName             DHCPv6Opt = 117
+	DHCPv6OptFDNSZoneName             DHCPv6Opt = 118
+	DHCPv6OptFDNSFlags                DHCPv6Opt = 119
+	DHCPv6OptFExpirationTime          DHCPv6Opt = 120
+	DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121
+	DHCPv6OptFMCLT                    DHCPv6Opt = 122
+	DHCPv6OptFPartnerLifetime         DHCPv6Opt = 123
+	DHCPv6OptFPartnerLifetimeSent     DHCPv6Opt = 124
+	DHCPv6OptFPartnerDownTime         DHCPv6Opt = 125
+	DHCPv6OptFPartnerRawCltTime       DHCPv6Opt = 126
+	DHCPv6OptFProtocolVersion         DHCPv6Opt = 127
+	DHCPv6OptFKeepaliveTime           DHCPv6Opt = 128
+	DHCPv6OptFReconfigureData         DHCPv6Opt = 129
+	DHCPv6OptFRelationshipName        DHCPv6Opt = 130
+	DHCPv6OptFServerFlags             DHCPv6Opt = 131
+	DHCPv6OptFServerState             DHCPv6Opt = 132
+	DHCPv6OptFStartTimeOfState        DHCPv6Opt = 133
+	DHCPv6OptFStateExpirationTime     DHCPv6Opt = 134
+
+	// RFC 8357 Generalized UDP Source Port for DHCP Relay
+	DHCPv6OptRelayPort DHCPv6Opt = 135
+
+	// draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices
+	DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136
+
+	// RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery
+	DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143
+)
+
+// String returns a string version of a DHCPv6Opt.
+func (o DHCPv6Opt) String() string {
+	switch o {
+	case DHCPv6OptClientID:
+		return "ClientID"
+	case DHCPv6OptServerID:
+		return "ServerID"
+	case DHCPv6OptIANA:
+		return "IA_NA"
+	case DHCPv6OptIATA:
+		return "IA_TA"
+	case DHCPv6OptIAAddr:
+		return "IAAddr"
+	case DHCPv6OptOro:
+		return "Oro"
+	case DHCPv6OptPreference:
+		return "Preference"
+	case DHCPv6OptElapsedTime:
+		return "ElapsedTime"
+	case DHCPv6OptRelayMessage:
+		return "RelayMessage"
+	case DHCPv6OptAuth:
+		return "Auth"
+	case DHCPv6OptUnicast:
+		return "Unicast"
+	case DHCPv6OptStatusCode:
+		return "StatusCode"
+	case DHCPv6OptRapidCommit:
+		return "RapidCommit"
+	case DHCPv6OptUserClass:
+		return "UserClass"
+	case DHCPv6OptVendorClass:
+		return "VendorClass"
+	case DHCPv6OptVendorOpts:
+		return "VendorOpts"
+	case DHCPv6OptInterfaceID:
+		return "InterfaceID"
+	case DHCPv6OptReconfigureMessage:
+		return "ReconfigureMessage"
+	case DHCPv6OptReconfigureAccept:
+		return "ReconfigureAccept"
+	case DHCPv6OptSIPServersDomainList:
+		return "SIPServersDomainList"
+	case DHCPv6OptSIPServersAddressList:
+		return "SIPServersAddressList"
+	case DHCPv6OptDNSServers:
+		return "DNSRecursiveNameServer"
+	case DHCPv6OptDomainList:
+		return "DomainSearchList"
+	case DHCPv6OptIAPD:
+		return "IdentityAssociationPrefixDelegation"
+	case DHCPv6OptIAPrefix:
+		return "IAPDPrefix"
+	case DHCPv6OptNISServers:
+		return "NISServers"
+	case DHCPv6OptNISPServers:
+		return "NISv2Servers"
+	case DHCPv6OptNISDomainName:
+		return "NISDomainName"
+	case DHCPv6OptNISPDomainName:
+		return "NISv2DomainName"
+	case DHCPv6OptSNTPServers:
+		return "SNTPServers"
+	case DHCPv6OptInformationRefreshTime:
+		return "InformationRefreshTime"
+	case DHCPv6OptBCMCSServerDomainNameList:
+		return "BCMCSControlServersDomainNameList"
+	case DHCPv6OptBCMCSServerAddressList:
+		return "BCMCSControlServersAddressList"
+	case DHCPv6OptGeoconfCivic:
+		return "CivicAddress"
+	case DHCPv6OptRemoteID:
+		return "RelayAgentRemoteID"
+	case DHCPv6OptSubscriberID:
+		return "RelayAgentSubscriberID"
+	case DHCPv6OptClientFQDN:
+		return "ClientFQDN"
+	case DHCPv6OptPanaAgent:
+		return "PANAAuthenticationAgent"
+	case DHCPv6OptNewPOSIXTimezone:
+		return "NewPOSIXTimezone"
+	case DHCPv6OptNewTZDBTimezone:
+		return "NewTZDBTimezone"
+	case DHCPv6OptEchoRequestOption:
+		return "EchoRequest"
+	case DHCPv6OptLQQuery:
+		return "LeasequeryQuery"
+	case DHCPv6OptClientData:
+		return "LeasequeryClientData"
+	case DHCPv6OptCLTTime:
+		return "LeasequeryClientLastTransactionTime"
+	case DHCPv6OptLQRelayData:
+		return "LeasequeryRelayData"
+	case DHCPv6OptLQClientLink:
+		return "LeasequeryClientLink"
+	case DHCPv6OptMIP6HNIDF:
+		return "MIPv6HomeNetworkIDFQDN"
+	case DHCPv6OptMIP6VDINF:
+		return "MIPv6VisitedHomeNetworkInformation"
+	case DHCPv6OptMIP6IDINF:
+		return "MIPv6IdentifiedHomeNetworkInformation"
+	case DHCPv6OptMIP6UDINF:
+		return "MIPv6UnrestrictedHomeNetworkInformation"
+	case DHCPv6OptMIP6HNP:
+		return "MIPv6HomeNetworkPrefix"
+	case DHCPv6OptMIP6HAA:
+		return "MIPv6HomeAgentAddress"
+	case DHCPv6OptMIP6HAF:
+		return "MIPv6HomeAgentFQDN"
+	case DHCPv6OptV6LOST:
+		return "LoST Server"
+	case DHCPv6OptCAPWAPACV6:
+		return "CAPWAPAccessControllerV6"
+	case DHCPv6OptRelayID:
+		return "LeasequeryRelayID"
+	case DHCPv6OptIPv6AddressMoS:
+		return "MoSIPv6Address"
+	case DHCPv6OptIPv6FQDNMoS:
+		return "MoSDomainNameList"
+	case DHCPv6OptNTPServer:
+		return "NTPServer"
+	case DHCPv6OptV6AccessDomain:
+		return "AccessNetworkDomainName"
+	case DHCPv6OptSIPUACSList:
+		return "SIPUserAgentConfigurationServiceDomains"
+	case DHCPv6OptBootFileURL:
+		return "BootFileURL"
+	case DHCPv6OptBootFileParam:
+		return "BootFileParameters"
+	case DHCPv6OptClientArchType:
+		return "ClientSystemArchitectureType"
+	case DHCPv6OptNII:
+		return "ClientNetworkInterfaceIdentifier"
+	case DHCPv6OptGeolocation:
+		return "Geolocation"
+	case DHCPv6OptAFTRName:
+		return "AFTRName"
+	case DHCPv6OptERPLocalDomainName:
+		return "AFTRName"
+	case DHCPv6OptRSOO:
+		return "RSOOption"
+	case DHCPv6OptPDExclude:
+		return "PrefixExclude"
+	case DHCPv6OptVSS:
+		return "VirtualSubnetSelection"
+	case DHCPv6OptRDNSSSelection:
+		return "RDNSSSelection"
+	case DHCPv6OptKRBPrincipalName:
+		return "KerberosPrincipalName"
+	case DHCPv6OptKRBRealmName:
+		return "KerberosRealmName"
+	case DHCPv6OptKRBKDC:
+		return "KerberosKDC"
+	case DHCPv6OptClientLinkLayerAddress:
+		return "ClientLinkLayerAddress"
+	case DHCPv6OptLinkAddress:
+		return "LinkAddress"
+	case DHCPv6OptRADIUS:
+		return "RADIUS"
+	case DHCPv6OptSolMaxRt:
+		return "SolMaxRt"
+	case DHCPv6OptInfMaxRt:
+		return "InfMaxRt"
+	case DHCPv6OptAddrSel:
+		return "AddressSelection"
+	case DHCPv6OptAddrSelTable:
+		return "AddressSelectionTable"
+	case DHCPv6OptV6PCPServer:
+		return "PCPServer"
+	case DHCPv6OptDHCPv4Message:
+		return "DHCPv4Message"
+	case DHCPv6OptDHCPv4OverDHCPv6Server:
+		return "DHCP4o6ServerAddress"
+	case DHCPv6OptS46Rule:
+		return "S46Rule"
+	case DHCPv6OptS46BR:
+		return "S46BR"
+	case DHCPv6OptS46DMR:
+		return "S46DMR"
+	case DHCPv6OptS46V4V4Bind:
+		return "S46IPv4IPv6AddressBinding"
+	case DHCPv6OptS46PortParameters:
+		return "S46PortParameters"
+	case DHCPv6OptS46ContMAPE:
+		return "S46MAPEContainer"
+	case DHCPv6OptS46ContMAPT:
+		return "S46MAPTContainer"
+	case DHCPv6OptS46ContLW:
+		return "S46Lightweight4Over6Container"
+	case DHCPv6Opt4RD:
+		return "4RD"
+	case DHCPv6Opt4RDMapRule:
+		return "4RDMapRule"
+	case DHCPv6Opt4RDNonMapRule:
+		return "4RDNonMapRule"
+	case DHCPv6OptLQBaseTime:
+		return "LQBaseTime"
+	case DHCPv6OptLQStartTime:
+		return "LQStartTime"
+	case DHCPv6OptLQEndTime:
+		return "LQEndTime"
+	case DHCPv6OptCaptivePortal:
+		return "CaptivePortal"
+	case DHCPv6OptMPLParameters:
+		return "MPLParameterConfiguration"
+	case DHCPv6OptANIATT:
+		return "ANIAccessTechnologyType"
+	case DHCPv6OptANINetworkName:
+		return "ANINetworkName"
+	case DHCPv6OptANIAPName:
+		return "ANIAccessPointName"
+	case DHCPv6OptANIAPBSSID:
+		return "ANIAccessPointBSSID"
+	case DHCPv6OptANIOperatorID:
+		return "ANIOperatorIdentifier"
+	case DHCPv6OptANIOperatorRealm:
+		return "ANIOperatorRealm"
+	case DHCPv6OptS46Priority:
+		return "S64Priority"
+	case DHCPv6OptMUDURLV6:
+		return "ManufacturerUsageDescriptionURL"
+	case DHCPv6OptV6Prefix64:
+		return "V6Prefix64"
+	case DHCPv6OptFBindingStatus:
+		return "FailoverBindingStatus"
+	case DHCPv6OptFConnectFlags:
+		return "FailoverConnectFlags"
+	case DHCPv6OptFDNSRemovalInfo:
+		return "FailoverDNSRemovalInfo"
+	case DHCPv6OptFDNSHostName:
+		return "FailoverDNSHostName"
+	case DHCPv6OptFDNSZoneName:
+		return "FailoverDNSZoneName"
+	case DHCPv6OptFDNSFlags:
+		return "FailoverDNSFlags"
+	case DHCPv6OptFExpirationTime:
+		return "FailoverExpirationTime"
+	case DHCPv6OptFMaxUnacknowledgedBNDUPD:
+		return "FailoverMaxUnacknowledgedBNDUPDMessages"
+	case DHCPv6OptFMCLT:
+		return "FailoverMaximumClientLeadTime"
+	case DHCPv6OptFPartnerLifetime:
+		return "FailoverPartnerLifetime"
+	case DHCPv6OptFPartnerLifetimeSent:
+		return "FailoverPartnerLifetimeSent"
+	case DHCPv6OptFPartnerDownTime:
+		return "FailoverPartnerDownTime"
+	case DHCPv6OptFPartnerRawCltTime:
+		return "FailoverPartnerRawClientLeadTime"
+	case DHCPv6OptFProtocolVersion:
+		return "FailoverProtocolVersion"
+	case DHCPv6OptFKeepaliveTime:
+		return "FailoverKeepaliveTime"
+	case DHCPv6OptFReconfigureData:
+		return "FailoverReconfigureData"
+	case DHCPv6OptFRelationshipName:
+		return "FailoverRelationshipName"
+	case DHCPv6OptFServerFlags:
+		return "FailoverServerFlags"
+	case DHCPv6OptFServerState:
+		return "FailoverServerState"
+	case DHCPv6OptFStartTimeOfState:
+		return "FailoverStartTimeOfState"
+	case DHCPv6OptFStateExpirationTime:
+		return "FailoverStateExpirationTime"
+	case DHCPv6OptRelayPort:
+		return "RelayPort"
+	case DHCPv6OptV6ZeroTouchRedirect:
+		return "ZeroTouch"
+	case DHCPv6OptIPV6AddressANDSF:
+		return "ANDSFIPv6Address"
+	default:
+		return fmt.Sprintf("Unknown(%d)", uint16(o))
+	}
+}
+
+// DHCPv6Options is used to get nicely printed option lists which would normally
+// be cut off after 5 options.
+type DHCPv6Options []DHCPv6Option
+
+// String returns a string version of the options list.
+func (o DHCPv6Options) 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()
+}
+
+// DHCPv6Option rerpresents a DHCP option.
+type DHCPv6Option struct {
+	Code   DHCPv6Opt
+	Length uint16
+	Data   []byte
+}
+
+// String returns a string version of a DHCP Option.
+func (o DHCPv6Option) String() string {
+	switch o.Code {
+	case DHCPv6OptClientID, DHCPv6OptServerID:
+		duid, err := decodeDHCPv6DUID(o.Data)
+		if err != nil {
+			return fmt.Sprintf("Option(%s:INVALID)", o.Code)
+		}
+		return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
+	case DHCPv6OptOro:
+		options := ""
+		for i := 0; i < int(o.Length); i += 2 {
+			if options != "" {
+				options += ","
+			}
+			option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
+			options += option.String()
+		}
+		return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
+	default:
+		return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
+	}
+}
+
+// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
+func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
+	o := DHCPv6Option{Code: code}
+	if data != nil {
+		o.Data = data
+		o.Length = uint16(len(data))
+	}
+
+	return o
+}
+
+func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
+	binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
+	if opts.FixLengths {
+		binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data)))
+	} else {
+		binary.BigEndian.PutUint16(b[2:4], o.Length)
+	}
+	copy(b[4:], o.Data)
+
+	return nil
+}
+
+func (o *DHCPv6Option) decode(data []byte) error {
+	if len(data) < 4 {
+		return errors.New("not enough data to decode")
+	}
+	o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
+	o.Length = binary.BigEndian.Uint16(data[2:4])
+	if len(data) < 4+int(o.Length) {
+		return fmt.Errorf("dhcpv6 option size < length %d", 4+o.Length)
+	}
+	o.Data = data[4 : 4+o.Length]
+	return nil
+}

+ 1098 - 0
vendor/github.com/google/gopacket/layers/dns.go

@@ -0,0 +1,1098 @@
+// Copyright 2014, 2018 GoPacket Authors. 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"
+)
+
+// 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]
+	DNSTypeOPT   DNSType = 41  // OPT Pseudo-RR [RFC6891]
+	DNSTypeURI   DNSType = 256 // URI RR [RFC7553]
+)
+
+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"
+	case DNSTypeOPT:
+		return "OPT"
+	case DNSTypeURI:
+		return "URI"
+	}
+}
+
+// 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]
+	DNSResponseCodeBadCookie DNSResponseCode = 23 // Bad/missing Server Cookie          [RFC7873]
+)
+
+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"
+	case DNSResponseCodeBadCookie:
+		return "Bad Cookie"
+	}
+}
+
+// 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 // Reserved 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 errDNSPacketTooShort
+	}
+
+	// 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
+		}
+		// extract extended RCODE from OPT RRs, RFC 6891 section 6.1.3
+		if d.Additionals[i].Type == DNSTypeOPT {
+			d.ResponseCode = DNSResponseCode(uint8(d.ResponseCode) | uint8(d.Additionals[i].TTL>>20&0xF0))
+		}
+	}
+
+	if uint16(len(d.Questions)) != d.QDCount {
+		return errDecodeQueryBadQDCount
+	} else if uint16(len(d.Answers)) != d.ANCount {
+		return errDecodeQueryBadANCount
+	} else if uint16(len(d.Authorities)) != d.NSCount {
+		return errDecodeQueryBadNSCount
+	} else if uint16(len(d.Additionals)) != d.ARCount {
+		return errDecodeQueryBadARCount
+	}
+	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
+	case DNSTypeURI:
+		return 4 + len(rr.URI.Target)
+	case DNSTypeOPT:
+		l := len(rr.OPT) * 4
+		for _, opt := range rr.OPT {
+			l += len(opt.Data)
+		}
+		return l
+	}
+
+	return 0
+}
+
+func computeSize(recs []DNSResourceRecord) int {
+	sz := 0
+	for _, rr := range recs {
+		v := len(rr.Name)
+
+		if v == 0 {
+			sz += v + 11
+		} else {
+			sz += v + 12
+		}
+
+		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
+}
+
+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, errDNSNameOffsetTooHigh
+	} else if offset < 0 {
+		return nil, 0, errDNSNameOffsetNegative
+	}
+	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, errDNSNameTooLong
+			} else if index2 < index+1 || index2 > len(data) {
+				return nil, 0, errDNSNameInvalidIndex
+			}
+			*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, errDNSPointerOffsetTooHigh
+			}
+			offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff)
+			if offsetp > len(data) {
+				return nil, 0, errDNSPointerOffsetTooHigh
+			}
+			// 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, errDNSIndexOutOfRange
+		}
+	}
+	if len(*buffer) <= start {
+		return (*buffer)[start:], index + 1, nil
+	}
+	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)
+	nSz := noff - offset
+	binary.BigEndian.PutUint16(data[noff:], uint16(q.Type))
+	binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class))
+	return nSz + 4
+}
+
+//  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
+	OPT            []DNSOPT // See RFC 6891, section 6.1.2
+	URI            DNSURI
+
+	// 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, errDecodeRecordLength
+	}
+	rr.Data = data[endq+10 : end]
+
+	if err = rr.decodeRData(data[:end], 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++
+		}
+	}
+
+	if len(name) == 0 {
+		data[offset] = 0x00 // terminal
+		return offset + 1
+	}
+
+	// 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)
+	nSz := noff - 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)
+	case DNSTypeURI:
+		binary.BigEndian.PutUint16(data[noff+10:], rr.URI.Priority)
+		binary.BigEndian.PutUint16(data[noff+12:], rr.URI.Weight)
+		copy(data[noff+14:], rr.URI.Target)
+	case DNSTypeOPT:
+		noff2 := noff + 10
+		for _, opt := range rr.OPT {
+			binary.BigEndian.PutUint16(data[noff2:], uint16(opt.Code))
+			binary.BigEndian.PutUint16(data[noff2+2:], uint16(len(opt.Data)))
+			copy(data[noff2+4:], opt.Data)
+			noff2 += 4 + len(opt.Data)
+		}
+	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 nSz + 10 + dSz, nil
+}
+
+func (rr *DNSResourceRecord) String() string {
+
+	if rr.Type == DNSTypeOPT {
+		opts := make([]string, len(rr.OPT))
+		for i, opt := range rr.OPT {
+			opts[i] = opt.String()
+		}
+		return "OPT " + strings.Join(opts, ",")
+	}
+	if rr.Type == DNSTypeURI {
+		return fmt.Sprintf("URI %d %d %s", rr.URI.Priority, rr.URI.Weight, string(rr.URI.Target))
+	}
+	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, errCharStringMissData
+		}
+		strings = append(strings, data[index+1:index2])
+	}
+	return strings, nil
+}
+
+func decodeOPTs(data []byte, offset int) ([]DNSOPT, error) {
+	allOPT := []DNSOPT{}
+	end := len(data)
+
+	if offset == end {
+		return allOPT, nil // There is no data to read
+	}
+
+	if offset+4 > end {
+		return allOPT, fmt.Errorf("DNSOPT record is of length %d, it should be at least length 4", end-offset)
+	}
+
+	for i := offset; i < end; {
+		opt := DNSOPT{}
+		if len(data) < i+4 {
+			return allOPT, fmt.Errorf("Malformed DNSOPT record.  Length %d < %d", len(data), i+4)
+		}
+		opt.Code = DNSOptionCode(binary.BigEndian.Uint16(data[i : i+2]))
+		l := binary.BigEndian.Uint16(data[i+2 : i+4])
+		if i+4+int(l) > end {
+			return allOPT, fmt.Errorf("Malformed DNSOPT record. The length (%d) field implies a packet larger than the one received", l)
+		}
+		opt.Data = data[i+4 : i+4+int(l)]
+		allOPT = append(allOPT, opt)
+		i += int(l) + 4
+	}
+	return allOPT, 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
+		}
+		if len(data) < endq+20 {
+			return errors.New("SOA too small")
+		}
+		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:
+		if len(data) < offset+2 {
+			return errors.New("MX too small")
+		}
+		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 DNSTypeURI:
+		if len(rr.Data) < 4 {
+			return errors.New("URI too small")
+		}
+		rr.URI.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
+		rr.URI.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
+		rr.URI.Target = rr.Data[4:]
+	case DNSTypeSRV:
+		if len(data) < offset+6 {
+			return errors.New("SRV too small")
+		}
+		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
+	case DNSTypeOPT:
+		allOPT, err := decodeOPTs(data, offset)
+		if err != nil {
+			return err
+		}
+		rr.OPT = allOPT
+	}
+	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
+}
+
+// DNSURI is a URI record, defining a target (URI) of a server/service
+type DNSURI struct {
+	Priority, Weight uint16
+	Target           []byte
+}
+
+// DNSOptionCode represents the code of a DNS Option, see RFC6891, section 6.1.2
+type DNSOptionCode uint16
+
+func (doc DNSOptionCode) String() string {
+	switch doc {
+	default:
+		return "Unknown"
+	case DNSOptionCodeNSID:
+		return "NSID"
+	case DNSOptionCodeDAU:
+		return "DAU"
+	case DNSOptionCodeDHU:
+		return "DHU"
+	case DNSOptionCodeN3U:
+		return "N3U"
+	case DNSOptionCodeEDNSClientSubnet:
+		return "EDNSClientSubnet"
+	case DNSOptionCodeEDNSExpire:
+		return "EDNSExpire"
+	case DNSOptionCodeCookie:
+		return "Cookie"
+	case DNSOptionCodeEDNSKeepAlive:
+		return "EDNSKeepAlive"
+	case DNSOptionCodePadding:
+		return "CodePadding"
+	case DNSOptionCodeChain:
+		return "CodeChain"
+	case DNSOptionCodeEDNSKeyTag:
+		return "CodeEDNSKeyTag"
+	case DNSOptionCodeEDNSClientTag:
+		return "EDNSClientTag"
+	case DNSOptionCodeEDNSServerTag:
+		return "EDNSServerTag"
+	case DNSOptionCodeDeviceID:
+		return "DeviceID"
+	}
+}
+
+// DNSOptionCode known values. See IANA
+const (
+	DNSOptionCodeNSID             DNSOptionCode = 3
+	DNSOptionCodeDAU              DNSOptionCode = 5
+	DNSOptionCodeDHU              DNSOptionCode = 6
+	DNSOptionCodeN3U              DNSOptionCode = 7
+	DNSOptionCodeEDNSClientSubnet DNSOptionCode = 8
+	DNSOptionCodeEDNSExpire       DNSOptionCode = 9
+	DNSOptionCodeCookie           DNSOptionCode = 10
+	DNSOptionCodeEDNSKeepAlive    DNSOptionCode = 11
+	DNSOptionCodePadding          DNSOptionCode = 12
+	DNSOptionCodeChain            DNSOptionCode = 13
+	DNSOptionCodeEDNSKeyTag       DNSOptionCode = 14
+	DNSOptionCodeEDNSClientTag    DNSOptionCode = 16
+	DNSOptionCodeEDNSServerTag    DNSOptionCode = 17
+	DNSOptionCodeDeviceID         DNSOptionCode = 26946
+)
+
+// DNSOPT is a DNS Option, see RFC6891, section 6.1.2
+type DNSOPT struct {
+	Code DNSOptionCode
+	Data []byte
+}
+
+func (opt DNSOPT) String() string {
+	return fmt.Sprintf("%s=%x", opt.Code, opt.Data)
+}
+
+var (
+	errMaxRecursion = errors.New("max DNS recursion level hit")
+
+	errDNSNameOffsetTooHigh    = errors.New("dns name offset too high")
+	errDNSNameOffsetNegative   = errors.New("dns name offset is negative")
+	errDNSPacketTooShort       = errors.New("DNS packet too short")
+	errDNSNameTooLong          = errors.New("dns name is too long")
+	errDNSNameInvalidIndex     = errors.New("dns name uncomputable: invalid index")
+	errDNSPointerOffsetTooHigh = errors.New("dns offset pointer too high")
+	errDNSIndexOutOfRange      = errors.New("dns index walked out of range")
+	errDNSNameHasNoData        = errors.New("no dns data found for name")
+
+	errCharStringMissData = errors.New("Insufficient data for a <character-string>")
+
+	errDecodeRecordLength = errors.New("resource record length exceeds data")
+
+	errDecodeQueryBadQDCount = errors.New("Invalid query decoding, not the right number of questions")
+	errDecodeQueryBadANCount = errors.New("Invalid query decoding, not the right number of answers")
+	errDecodeQueryBadNSCount = errors.New("Invalid query decoding, not the right number of authorities")
+	errDecodeQueryBadARCount = errors.New("Invalid query decoding, not the right number of additionals info")
+)

+ 61 - 0
vendor/github.com/google/gopacket/layers/doc.go

@@ -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

+ 2118 - 0
vendor/github.com/google/gopacket/layers/dot11.go

@@ -0,0 +1,2118 @@
+// 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.
+
+// See http://standards.ieee.org/findstds/standard/802.11-2012.html for info on
+// all of the layers in this file.
+
+package layers
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"hash/crc32"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+// Dot11Flags contains the set of 8 flags in the IEEE 802.11 frame control
+// header, all in one place.
+type Dot11Flags uint8
+
+const (
+	Dot11FlagsToDS Dot11Flags = 1 << iota
+	Dot11FlagsFromDS
+	Dot11FlagsMF
+	Dot11FlagsRetry
+	Dot11FlagsPowerManagement
+	Dot11FlagsMD
+	Dot11FlagsWEP
+	Dot11FlagsOrder
+)
+
+func (d Dot11Flags) ToDS() bool {
+	return d&Dot11FlagsToDS != 0
+}
+func (d Dot11Flags) FromDS() bool {
+	return d&Dot11FlagsFromDS != 0
+}
+func (d Dot11Flags) MF() bool {
+	return d&Dot11FlagsMF != 0
+}
+func (d Dot11Flags) Retry() bool {
+	return d&Dot11FlagsRetry != 0
+}
+func (d Dot11Flags) PowerManagement() bool {
+	return d&Dot11FlagsPowerManagement != 0
+}
+func (d Dot11Flags) MD() bool {
+	return d&Dot11FlagsMD != 0
+}
+func (d Dot11Flags) WEP() bool {
+	return d&Dot11FlagsWEP != 0
+}
+func (d Dot11Flags) Order() bool {
+	return d&Dot11FlagsOrder != 0
+}
+
+// String provides a human readable string for Dot11Flags.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11Flags value, not its string.
+func (a Dot11Flags) String() string {
+	var out bytes.Buffer
+	if a.ToDS() {
+		out.WriteString("TO-DS,")
+	}
+	if a.FromDS() {
+		out.WriteString("FROM-DS,")
+	}
+	if a.MF() {
+		out.WriteString("MF,")
+	}
+	if a.Retry() {
+		out.WriteString("Retry,")
+	}
+	if a.PowerManagement() {
+		out.WriteString("PowerManagement,")
+	}
+	if a.MD() {
+		out.WriteString("MD,")
+	}
+	if a.WEP() {
+		out.WriteString("WEP,")
+	}
+	if a.Order() {
+		out.WriteString("Order,")
+	}
+
+	if length := out.Len(); length > 0 {
+		return string(out.Bytes()[:length-1]) // strip final comma
+	}
+	return ""
+}
+
+type Dot11Reason uint16
+
+// TODO: Verify these reasons, and append more reasons if necessary.
+
+const (
+	Dot11ReasonReserved          Dot11Reason = 1
+	Dot11ReasonUnspecified       Dot11Reason = 2
+	Dot11ReasonAuthExpired       Dot11Reason = 3
+	Dot11ReasonDeauthStLeaving   Dot11Reason = 4
+	Dot11ReasonInactivity        Dot11Reason = 5
+	Dot11ReasonApFull            Dot11Reason = 6
+	Dot11ReasonClass2FromNonAuth Dot11Reason = 7
+	Dot11ReasonClass3FromNonAss  Dot11Reason = 8
+	Dot11ReasonDisasStLeaving    Dot11Reason = 9
+	Dot11ReasonStNotAuth         Dot11Reason = 10
+)
+
+// String provides a human readable string for Dot11Reason.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11Reason value, not its string.
+func (a Dot11Reason) String() string {
+	switch a {
+	case Dot11ReasonReserved:
+		return "Reserved"
+	case Dot11ReasonUnspecified:
+		return "Unspecified"
+	case Dot11ReasonAuthExpired:
+		return "Auth. expired"
+	case Dot11ReasonDeauthStLeaving:
+		return "Deauth. st. leaving"
+	case Dot11ReasonInactivity:
+		return "Inactivity"
+	case Dot11ReasonApFull:
+		return "Ap. full"
+	case Dot11ReasonClass2FromNonAuth:
+		return "Class2 from non auth."
+	case Dot11ReasonClass3FromNonAss:
+		return "Class3 from non ass."
+	case Dot11ReasonDisasStLeaving:
+		return "Disass st. leaving"
+	case Dot11ReasonStNotAuth:
+		return "St. not auth."
+	default:
+		return "Unknown reason"
+	}
+}
+
+type Dot11Status uint16
+
+const (
+	Dot11StatusSuccess                      Dot11Status = 0
+	Dot11StatusFailure                      Dot11Status = 1  // Unspecified failure
+	Dot11StatusCannotSupportAllCapabilities Dot11Status = 10 // Cannot support all requested capabilities in the Capability Information field
+	Dot11StatusInabilityExistsAssociation   Dot11Status = 11 // Reassociation denied due to inability to confirm that association exists
+	Dot11StatusAssociationDenied            Dot11Status = 12 // Association denied due to reason outside the scope of this standard
+	Dot11StatusAlgorithmUnsupported         Dot11Status = 13 // Responding station does not support the specified authentication algorithm
+	Dot11StatusOufOfExpectedSequence        Dot11Status = 14 // Received an Authentication frame with authentication transaction sequence number out of expected sequence
+	Dot11StatusChallengeFailure             Dot11Status = 15 // Authentication rejected because of challenge failure
+	Dot11StatusTimeout                      Dot11Status = 16 // Authentication rejected due to timeout waiting for next frame in sequence
+	Dot11StatusAPUnableToHandle             Dot11Status = 17 // Association denied because AP is unable to handle additional associated stations
+	Dot11StatusRateUnsupported              Dot11Status = 18 // Association denied due to requesting station not supporting all of the data rates in the BSSBasicRateSet parameter
+)
+
+// String provides a human readable string for Dot11Status.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11Status value, not its string.
+func (a Dot11Status) String() string {
+	switch a {
+	case Dot11StatusSuccess:
+		return "success"
+	case Dot11StatusFailure:
+		return "failure"
+	case Dot11StatusCannotSupportAllCapabilities:
+		return "cannot-support-all-capabilities"
+	case Dot11StatusInabilityExistsAssociation:
+		return "inability-exists-association"
+	case Dot11StatusAssociationDenied:
+		return "association-denied"
+	case Dot11StatusAlgorithmUnsupported:
+		return "algorithm-unsupported"
+	case Dot11StatusOufOfExpectedSequence:
+		return "out-of-expected-sequence"
+	case Dot11StatusChallengeFailure:
+		return "challenge-failure"
+	case Dot11StatusTimeout:
+		return "timeout"
+	case Dot11StatusAPUnableToHandle:
+		return "ap-unable-to-handle"
+	case Dot11StatusRateUnsupported:
+		return "rate-unsupported"
+	default:
+		return "unknown status"
+	}
+}
+
+type Dot11AckPolicy uint8
+
+const (
+	Dot11AckPolicyNormal     Dot11AckPolicy = 0
+	Dot11AckPolicyNone       Dot11AckPolicy = 1
+	Dot11AckPolicyNoExplicit Dot11AckPolicy = 2
+	Dot11AckPolicyBlock      Dot11AckPolicy = 3
+)
+
+// String provides a human readable string for Dot11AckPolicy.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11AckPolicy value, not its string.
+func (a Dot11AckPolicy) String() string {
+	switch a {
+	case Dot11AckPolicyNormal:
+		return "normal-ack"
+	case Dot11AckPolicyNone:
+		return "no-ack"
+	case Dot11AckPolicyNoExplicit:
+		return "no-explicit-ack"
+	case Dot11AckPolicyBlock:
+		return "block-ack"
+	default:
+		return "unknown-ack-policy"
+	}
+}
+
+type Dot11Algorithm uint16
+
+const (
+	Dot11AlgorithmOpen      Dot11Algorithm = 0
+	Dot11AlgorithmSharedKey Dot11Algorithm = 1
+)
+
+// String provides a human readable string for Dot11Algorithm.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11Algorithm value, not its string.
+func (a Dot11Algorithm) String() string {
+	switch a {
+	case Dot11AlgorithmOpen:
+		return "open"
+	case Dot11AlgorithmSharedKey:
+		return "shared-key"
+	default:
+		return "unknown-algorithm"
+	}
+}
+
+type Dot11InformationElementID uint8
+
+const (
+	Dot11InformationElementIDSSID                      Dot11InformationElementID = 0
+	Dot11InformationElementIDRates                     Dot11InformationElementID = 1
+	Dot11InformationElementIDFHSet                     Dot11InformationElementID = 2
+	Dot11InformationElementIDDSSet                     Dot11InformationElementID = 3
+	Dot11InformationElementIDCFSet                     Dot11InformationElementID = 4
+	Dot11InformationElementIDTIM                       Dot11InformationElementID = 5
+	Dot11InformationElementIDIBSSSet                   Dot11InformationElementID = 6
+	Dot11InformationElementIDCountryInfo               Dot11InformationElementID = 7
+	Dot11InformationElementIDHoppingPatternParam       Dot11InformationElementID = 8
+	Dot11InformationElementIDHoppingPatternTable       Dot11InformationElementID = 9
+	Dot11InformationElementIDRequest                   Dot11InformationElementID = 10
+	Dot11InformationElementIDQBSSLoadElem              Dot11InformationElementID = 11
+	Dot11InformationElementIDEDCAParamSet              Dot11InformationElementID = 12
+	Dot11InformationElementIDTrafficSpec               Dot11InformationElementID = 13
+	Dot11InformationElementIDTrafficClass              Dot11InformationElementID = 14
+	Dot11InformationElementIDSchedule                  Dot11InformationElementID = 15
+	Dot11InformationElementIDChallenge                 Dot11InformationElementID = 16
+	Dot11InformationElementIDPowerConst                Dot11InformationElementID = 32
+	Dot11InformationElementIDPowerCapability           Dot11InformationElementID = 33
+	Dot11InformationElementIDTPCRequest                Dot11InformationElementID = 34
+	Dot11InformationElementIDTPCReport                 Dot11InformationElementID = 35
+	Dot11InformationElementIDSupportedChannels         Dot11InformationElementID = 36
+	Dot11InformationElementIDSwitchChannelAnnounce     Dot11InformationElementID = 37
+	Dot11InformationElementIDMeasureRequest            Dot11InformationElementID = 38
+	Dot11InformationElementIDMeasureReport             Dot11InformationElementID = 39
+	Dot11InformationElementIDQuiet                     Dot11InformationElementID = 40
+	Dot11InformationElementIDIBSSDFS                   Dot11InformationElementID = 41
+	Dot11InformationElementIDERPInfo                   Dot11InformationElementID = 42
+	Dot11InformationElementIDTSDelay                   Dot11InformationElementID = 43
+	Dot11InformationElementIDTCLASProcessing           Dot11InformationElementID = 44
+	Dot11InformationElementIDHTCapabilities            Dot11InformationElementID = 45
+	Dot11InformationElementIDQOSCapability             Dot11InformationElementID = 46
+	Dot11InformationElementIDERPInfo2                  Dot11InformationElementID = 47
+	Dot11InformationElementIDRSNInfo                   Dot11InformationElementID = 48
+	Dot11InformationElementIDESRates                   Dot11InformationElementID = 50
+	Dot11InformationElementIDAPChannelReport           Dot11InformationElementID = 51
+	Dot11InformationElementIDNeighborReport            Dot11InformationElementID = 52
+	Dot11InformationElementIDRCPI                      Dot11InformationElementID = 53
+	Dot11InformationElementIDMobilityDomain            Dot11InformationElementID = 54
+	Dot11InformationElementIDFastBSSTrans              Dot11InformationElementID = 55
+	Dot11InformationElementIDTimeoutInt                Dot11InformationElementID = 56
+	Dot11InformationElementIDRICData                   Dot11InformationElementID = 57
+	Dot11InformationElementIDDSERegisteredLoc          Dot11InformationElementID = 58
+	Dot11InformationElementIDSuppOperatingClass        Dot11InformationElementID = 59
+	Dot11InformationElementIDExtChanSwitchAnnounce     Dot11InformationElementID = 60
+	Dot11InformationElementIDHTInfo                    Dot11InformationElementID = 61
+	Dot11InformationElementIDSecChanOffset             Dot11InformationElementID = 62
+	Dot11InformationElementIDBSSAverageAccessDelay     Dot11InformationElementID = 63
+	Dot11InformationElementIDAntenna                   Dot11InformationElementID = 64
+	Dot11InformationElementIDRSNI                      Dot11InformationElementID = 65
+	Dot11InformationElementIDMeasurePilotTrans         Dot11InformationElementID = 66
+	Dot11InformationElementIDBSSAvailAdmCapacity       Dot11InformationElementID = 67
+	Dot11InformationElementIDBSSACAccDelayWAPIParam    Dot11InformationElementID = 68
+	Dot11InformationElementIDTimeAdvertisement         Dot11InformationElementID = 69
+	Dot11InformationElementIDRMEnabledCapabilities     Dot11InformationElementID = 70
+	Dot11InformationElementIDMultipleBSSID             Dot11InformationElementID = 71
+	Dot11InformationElementID2040BSSCoExist            Dot11InformationElementID = 72
+	Dot11InformationElementID2040BSSIntChanReport      Dot11InformationElementID = 73
+	Dot11InformationElementIDOverlapBSSScanParam       Dot11InformationElementID = 74
+	Dot11InformationElementIDRICDescriptor             Dot11InformationElementID = 75
+	Dot11InformationElementIDManagementMIC             Dot11InformationElementID = 76
+	Dot11InformationElementIDEventRequest              Dot11InformationElementID = 78
+	Dot11InformationElementIDEventReport               Dot11InformationElementID = 79
+	Dot11InformationElementIDDiagnosticRequest         Dot11InformationElementID = 80
+	Dot11InformationElementIDDiagnosticReport          Dot11InformationElementID = 81
+	Dot11InformationElementIDLocationParam             Dot11InformationElementID = 82
+	Dot11InformationElementIDNonTransBSSIDCapability   Dot11InformationElementID = 83
+	Dot11InformationElementIDSSIDList                  Dot11InformationElementID = 84
+	Dot11InformationElementIDMultipleBSSIDIndex        Dot11InformationElementID = 85
+	Dot11InformationElementIDFMSDescriptor             Dot11InformationElementID = 86
+	Dot11InformationElementIDFMSRequest                Dot11InformationElementID = 87
+	Dot11InformationElementIDFMSResponse               Dot11InformationElementID = 88
+	Dot11InformationElementIDQOSTrafficCapability      Dot11InformationElementID = 89
+	Dot11InformationElementIDBSSMaxIdlePeriod          Dot11InformationElementID = 90
+	Dot11InformationElementIDTFSRequest                Dot11InformationElementID = 91
+	Dot11InformationElementIDTFSResponse               Dot11InformationElementID = 92
+	Dot11InformationElementIDWNMSleepMode              Dot11InformationElementID = 93
+	Dot11InformationElementIDTIMBroadcastRequest       Dot11InformationElementID = 94
+	Dot11InformationElementIDTIMBroadcastResponse      Dot11InformationElementID = 95
+	Dot11InformationElementIDCollInterferenceReport    Dot11InformationElementID = 96
+	Dot11InformationElementIDChannelUsage              Dot11InformationElementID = 97
+	Dot11InformationElementIDTimeZone                  Dot11InformationElementID = 98
+	Dot11InformationElementIDDMSRequest                Dot11InformationElementID = 99
+	Dot11InformationElementIDDMSResponse               Dot11InformationElementID = 100
+	Dot11InformationElementIDLinkIdentifier            Dot11InformationElementID = 101
+	Dot11InformationElementIDWakeupSchedule            Dot11InformationElementID = 102
+	Dot11InformationElementIDChannelSwitchTiming       Dot11InformationElementID = 104
+	Dot11InformationElementIDPTIControl                Dot11InformationElementID = 105
+	Dot11InformationElementIDPUBufferStatus            Dot11InformationElementID = 106
+	Dot11InformationElementIDInterworking              Dot11InformationElementID = 107
+	Dot11InformationElementIDAdvertisementProtocol     Dot11InformationElementID = 108
+	Dot11InformationElementIDExpBWRequest              Dot11InformationElementID = 109
+	Dot11InformationElementIDQOSMapSet                 Dot11InformationElementID = 110
+	Dot11InformationElementIDRoamingConsortium         Dot11InformationElementID = 111
+	Dot11InformationElementIDEmergencyAlertIdentifier  Dot11InformationElementID = 112
+	Dot11InformationElementIDMeshConfiguration         Dot11InformationElementID = 113
+	Dot11InformationElementIDMeshID                    Dot11InformationElementID = 114
+	Dot11InformationElementIDMeshLinkMetricReport      Dot11InformationElementID = 115
+	Dot11InformationElementIDCongestionNotification    Dot11InformationElementID = 116
+	Dot11InformationElementIDMeshPeeringManagement     Dot11InformationElementID = 117
+	Dot11InformationElementIDMeshChannelSwitchParam    Dot11InformationElementID = 118
+	Dot11InformationElementIDMeshAwakeWindows          Dot11InformationElementID = 119
+	Dot11InformationElementIDBeaconTiming              Dot11InformationElementID = 120
+	Dot11InformationElementIDMCCAOPSetupRequest        Dot11InformationElementID = 121
+	Dot11InformationElementIDMCCAOPSetupReply          Dot11InformationElementID = 122
+	Dot11InformationElementIDMCCAOPAdvertisement       Dot11InformationElementID = 123
+	Dot11InformationElementIDMCCAOPTeardown            Dot11InformationElementID = 124
+	Dot11InformationElementIDGateAnnouncement          Dot11InformationElementID = 125
+	Dot11InformationElementIDRootAnnouncement          Dot11InformationElementID = 126
+	Dot11InformationElementIDExtCapability             Dot11InformationElementID = 127
+	Dot11InformationElementIDAgereProprietary          Dot11InformationElementID = 128
+	Dot11InformationElementIDPathRequest               Dot11InformationElementID = 130
+	Dot11InformationElementIDPathReply                 Dot11InformationElementID = 131
+	Dot11InformationElementIDPathError                 Dot11InformationElementID = 132
+	Dot11InformationElementIDCiscoCCX1CKIPDeviceName   Dot11InformationElementID = 133
+	Dot11InformationElementIDCiscoCCX2                 Dot11InformationElementID = 136
+	Dot11InformationElementIDProxyUpdate               Dot11InformationElementID = 137
+	Dot11InformationElementIDProxyUpdateConfirmation   Dot11InformationElementID = 138
+	Dot11InformationElementIDAuthMeshPerringExch       Dot11InformationElementID = 139
+	Dot11InformationElementIDMIC                       Dot11InformationElementID = 140
+	Dot11InformationElementIDDestinationURI            Dot11InformationElementID = 141
+	Dot11InformationElementIDUAPSDCoexistence          Dot11InformationElementID = 142
+	Dot11InformationElementIDWakeupSchedule80211ad     Dot11InformationElementID = 143
+	Dot11InformationElementIDExtendedSchedule          Dot11InformationElementID = 144
+	Dot11InformationElementIDSTAAvailability           Dot11InformationElementID = 145
+	Dot11InformationElementIDDMGTSPEC                  Dot11InformationElementID = 146
+	Dot11InformationElementIDNextDMGATI                Dot11InformationElementID = 147
+	Dot11InformationElementIDDMSCapabilities           Dot11InformationElementID = 148
+	Dot11InformationElementIDCiscoUnknown95            Dot11InformationElementID = 149
+	Dot11InformationElementIDVendor2                   Dot11InformationElementID = 150
+	Dot11InformationElementIDDMGOperating              Dot11InformationElementID = 151
+	Dot11InformationElementIDDMGBSSParamChange         Dot11InformationElementID = 152
+	Dot11InformationElementIDDMGBeamRefinement         Dot11InformationElementID = 153
+	Dot11InformationElementIDChannelMeasFeedback       Dot11InformationElementID = 154
+	Dot11InformationElementIDAwakeWindow               Dot11InformationElementID = 157
+	Dot11InformationElementIDMultiBand                 Dot11InformationElementID = 158
+	Dot11InformationElementIDADDBAExtension            Dot11InformationElementID = 159
+	Dot11InformationElementIDNEXTPCPList               Dot11InformationElementID = 160
+	Dot11InformationElementIDPCPHandover               Dot11InformationElementID = 161
+	Dot11InformationElementIDDMGLinkMargin             Dot11InformationElementID = 162
+	Dot11InformationElementIDSwitchingStream           Dot11InformationElementID = 163
+	Dot11InformationElementIDSessionTransmission       Dot11InformationElementID = 164
+	Dot11InformationElementIDDynamicTonePairReport     Dot11InformationElementID = 165
+	Dot11InformationElementIDClusterReport             Dot11InformationElementID = 166
+	Dot11InformationElementIDRelayCapabilities         Dot11InformationElementID = 167
+	Dot11InformationElementIDRelayTransferParameter    Dot11InformationElementID = 168
+	Dot11InformationElementIDBeamlinkMaintenance       Dot11InformationElementID = 169
+	Dot11InformationElementIDMultipleMacSublayers      Dot11InformationElementID = 170
+	Dot11InformationElementIDUPID                      Dot11InformationElementID = 171
+	Dot11InformationElementIDDMGLinkAdaptionAck        Dot11InformationElementID = 172
+	Dot11InformationElementIDSymbolProprietary         Dot11InformationElementID = 173
+	Dot11InformationElementIDMCCAOPAdvertOverview      Dot11InformationElementID = 174
+	Dot11InformationElementIDQuietPeriodRequest        Dot11InformationElementID = 175
+	Dot11InformationElementIDQuietPeriodResponse       Dot11InformationElementID = 177
+	Dot11InformationElementIDECPACPolicy               Dot11InformationElementID = 182
+	Dot11InformationElementIDClusterTimeOffset         Dot11InformationElementID = 183
+	Dot11InformationElementIDAntennaSectorID           Dot11InformationElementID = 190
+	Dot11InformationElementIDVHTCapabilities           Dot11InformationElementID = 191
+	Dot11InformationElementIDVHTOperation              Dot11InformationElementID = 192
+	Dot11InformationElementIDExtendedBSSLoad           Dot11InformationElementID = 193
+	Dot11InformationElementIDWideBWChannelSwitch       Dot11InformationElementID = 194
+	Dot11InformationElementIDVHTTxPowerEnvelope        Dot11InformationElementID = 195
+	Dot11InformationElementIDChannelSwitchWrapper      Dot11InformationElementID = 196
+	Dot11InformationElementIDOperatingModeNotification Dot11InformationElementID = 199
+	Dot11InformationElementIDUPSIM                     Dot11InformationElementID = 200
+	Dot11InformationElementIDReducedNeighborReport     Dot11InformationElementID = 201
+	Dot11InformationElementIDTVHTOperation             Dot11InformationElementID = 202
+	Dot11InformationElementIDDeviceLocation            Dot11InformationElementID = 204
+	Dot11InformationElementIDWhiteSpaceMap             Dot11InformationElementID = 205
+	Dot11InformationElementIDFineTuningMeasureParams   Dot11InformationElementID = 206
+	Dot11InformationElementIDVendor                    Dot11InformationElementID = 221
+)
+
+// String provides a human readable string for Dot11InformationElementID.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the Dot11InformationElementID value,
+// not its string.
+func (a Dot11InformationElementID) String() string {
+	switch a {
+	case Dot11InformationElementIDSSID:
+		return "SSID parameter set"
+	case Dot11InformationElementIDRates:
+		return "Supported Rates"
+	case Dot11InformationElementIDFHSet:
+		return "FH Parameter set"
+	case Dot11InformationElementIDDSSet:
+		return "DS Parameter set"
+	case Dot11InformationElementIDCFSet:
+		return "CF Parameter set"
+	case Dot11InformationElementIDTIM:
+		return "Traffic Indication Map (TIM)"
+	case Dot11InformationElementIDIBSSSet:
+		return "IBSS Parameter set"
+	case Dot11InformationElementIDCountryInfo:
+		return "Country Information"
+	case Dot11InformationElementIDHoppingPatternParam:
+		return "Hopping Pattern Parameters"
+	case Dot11InformationElementIDHoppingPatternTable:
+		return "Hopping Pattern Table"
+	case Dot11InformationElementIDRequest:
+		return "Request"
+	case Dot11InformationElementIDQBSSLoadElem:
+		return "QBSS Load Element"
+	case Dot11InformationElementIDEDCAParamSet:
+		return "EDCA Parameter Set"
+	case Dot11InformationElementIDTrafficSpec:
+		return "Traffic Specification"
+	case Dot11InformationElementIDTrafficClass:
+		return "Traffic Classification"
+	case Dot11InformationElementIDSchedule:
+		return "Schedule"
+	case Dot11InformationElementIDChallenge:
+		return "Challenge text"
+	case Dot11InformationElementIDPowerConst:
+		return "Power Constraint"
+	case Dot11InformationElementIDPowerCapability:
+		return "Power Capability"
+	case Dot11InformationElementIDTPCRequest:
+		return "TPC Request"
+	case Dot11InformationElementIDTPCReport:
+		return "TPC Report"
+	case Dot11InformationElementIDSupportedChannels:
+		return "Supported Channels"
+	case Dot11InformationElementIDSwitchChannelAnnounce:
+		return "Channel Switch Announcement"
+	case Dot11InformationElementIDMeasureRequest:
+		return "Measurement Request"
+	case Dot11InformationElementIDMeasureReport:
+		return "Measurement Report"
+	case Dot11InformationElementIDQuiet:
+		return "Quiet"
+	case Dot11InformationElementIDIBSSDFS:
+		return "IBSS DFS"
+	case Dot11InformationElementIDERPInfo:
+		return "ERP Information"
+	case Dot11InformationElementIDTSDelay:
+		return "TS Delay"
+	case Dot11InformationElementIDTCLASProcessing:
+		return "TCLAS Processing"
+	case Dot11InformationElementIDHTCapabilities:
+		return "HT Capabilities (802.11n D1.10)"
+	case Dot11InformationElementIDQOSCapability:
+		return "QOS Capability"
+	case Dot11InformationElementIDERPInfo2:
+		return "ERP Information-2"
+	case Dot11InformationElementIDRSNInfo:
+		return "RSN Information"
+	case Dot11InformationElementIDESRates:
+		return "Extended Supported Rates"
+	case Dot11InformationElementIDAPChannelReport:
+		return "AP Channel Report"
+	case Dot11InformationElementIDNeighborReport:
+		return "Neighbor Report"
+	case Dot11InformationElementIDRCPI:
+		return "RCPI"
+	case Dot11InformationElementIDMobilityDomain:
+		return "Mobility Domain"
+	case Dot11InformationElementIDFastBSSTrans:
+		return "Fast BSS Transition"
+	case Dot11InformationElementIDTimeoutInt:
+		return "Timeout Interval"
+	case Dot11InformationElementIDRICData:
+		return "RIC Data"
+	case Dot11InformationElementIDDSERegisteredLoc:
+		return "DSE Registered Location"
+	case Dot11InformationElementIDSuppOperatingClass:
+		return "Supported Operating Classes"
+	case Dot11InformationElementIDExtChanSwitchAnnounce:
+		return "Extended Channel Switch Announcement"
+	case Dot11InformationElementIDHTInfo:
+		return "HT Information (802.11n D1.10)"
+	case Dot11InformationElementIDSecChanOffset:
+		return "Secondary Channel Offset (802.11n D1.10)"
+	case Dot11InformationElementIDBSSAverageAccessDelay:
+		return "BSS Average Access Delay"
+	case Dot11InformationElementIDAntenna:
+		return "Antenna"
+	case Dot11InformationElementIDRSNI:
+		return "RSNI"
+	case Dot11InformationElementIDMeasurePilotTrans:
+		return "Measurement Pilot Transmission"
+	case Dot11InformationElementIDBSSAvailAdmCapacity:
+		return "BSS Available Admission Capacity"
+	case Dot11InformationElementIDBSSACAccDelayWAPIParam:
+		return "BSS AC Access Delay/WAPI Parameter Set"
+	case Dot11InformationElementIDTimeAdvertisement:
+		return "Time Advertisement"
+	case Dot11InformationElementIDRMEnabledCapabilities:
+		return "RM Enabled Capabilities"
+	case Dot11InformationElementIDMultipleBSSID:
+		return "Multiple BSSID"
+	case Dot11InformationElementID2040BSSCoExist:
+		return "20/40 BSS Coexistence"
+	case Dot11InformationElementID2040BSSIntChanReport:
+		return "20/40 BSS Intolerant Channel Report"
+	case Dot11InformationElementIDOverlapBSSScanParam:
+		return "Overlapping BSS Scan Parameters"
+	case Dot11InformationElementIDRICDescriptor:
+		return "RIC Descriptor"
+	case Dot11InformationElementIDManagementMIC:
+		return "Management MIC"
+	case Dot11InformationElementIDEventRequest:
+		return "Event Request"
+	case Dot11InformationElementIDEventReport:
+		return "Event Report"
+	case Dot11InformationElementIDDiagnosticRequest:
+		return "Diagnostic Request"
+	case Dot11InformationElementIDDiagnosticReport:
+		return "Diagnostic Report"
+	case Dot11InformationElementIDLocationParam:
+		return "Location Parameters"
+	case Dot11InformationElementIDNonTransBSSIDCapability:
+		return "Non Transmitted BSSID Capability"
+	case Dot11InformationElementIDSSIDList:
+		return "SSID List"
+	case Dot11InformationElementIDMultipleBSSIDIndex:
+		return "Multiple BSSID Index"
+	case Dot11InformationElementIDFMSDescriptor:
+		return "FMS Descriptor"
+	case Dot11InformationElementIDFMSRequest:
+		return "FMS Request"
+	case Dot11InformationElementIDFMSResponse:
+		return "FMS Response"
+	case Dot11InformationElementIDQOSTrafficCapability:
+		return "QoS Traffic Capability"
+	case Dot11InformationElementIDBSSMaxIdlePeriod:
+		return "BSS Max Idle Period"
+	case Dot11InformationElementIDTFSRequest:
+		return "TFS Request"
+	case Dot11InformationElementIDTFSResponse:
+		return "TFS Response"
+	case Dot11InformationElementIDWNMSleepMode:
+		return "WNM-Sleep Mode"
+	case Dot11InformationElementIDTIMBroadcastRequest:
+		return "TIM Broadcast Request"
+	case Dot11InformationElementIDTIMBroadcastResponse:
+		return "TIM Broadcast Response"
+	case Dot11InformationElementIDCollInterferenceReport:
+		return "Collocated Interference Report"
+	case Dot11InformationElementIDChannelUsage:
+		return "Channel Usage"
+	case Dot11InformationElementIDTimeZone:
+		return "Time Zone"
+	case Dot11InformationElementIDDMSRequest:
+		return "DMS Request"
+	case Dot11InformationElementIDDMSResponse:
+		return "DMS Response"
+	case Dot11InformationElementIDLinkIdentifier:
+		return "Link Identifier"
+	case Dot11InformationElementIDWakeupSchedule:
+		return "Wakeup Schedule"
+	case Dot11InformationElementIDChannelSwitchTiming:
+		return "Channel Switch Timing"
+	case Dot11InformationElementIDPTIControl:
+		return "PTI Control"
+	case Dot11InformationElementIDPUBufferStatus:
+		return "PU Buffer Status"
+	case Dot11InformationElementIDInterworking:
+		return "Interworking"
+	case Dot11InformationElementIDAdvertisementProtocol:
+		return "Advertisement Protocol"
+	case Dot11InformationElementIDExpBWRequest:
+		return "Expedited Bandwidth Request"
+	case Dot11InformationElementIDQOSMapSet:
+		return "QoS Map Set"
+	case Dot11InformationElementIDRoamingConsortium:
+		return "Roaming Consortium"
+	case Dot11InformationElementIDEmergencyAlertIdentifier:
+		return "Emergency Alert Identifier"
+	case Dot11InformationElementIDMeshConfiguration:
+		return "Mesh Configuration"
+	case Dot11InformationElementIDMeshID:
+		return "Mesh ID"
+	case Dot11InformationElementIDMeshLinkMetricReport:
+		return "Mesh Link Metric Report"
+	case Dot11InformationElementIDCongestionNotification:
+		return "Congestion Notification"
+	case Dot11InformationElementIDMeshPeeringManagement:
+		return "Mesh Peering Management"
+	case Dot11InformationElementIDMeshChannelSwitchParam:
+		return "Mesh Channel Switch Parameters"
+	case Dot11InformationElementIDMeshAwakeWindows:
+		return "Mesh Awake Windows"
+	case Dot11InformationElementIDBeaconTiming:
+		return "Beacon Timing"
+	case Dot11InformationElementIDMCCAOPSetupRequest:
+		return "MCCAOP Setup Request"
+	case Dot11InformationElementIDMCCAOPSetupReply:
+		return "MCCAOP SETUP Reply"
+	case Dot11InformationElementIDMCCAOPAdvertisement:
+		return "MCCAOP Advertisement"
+	case Dot11InformationElementIDMCCAOPTeardown:
+		return "MCCAOP Teardown"
+	case Dot11InformationElementIDGateAnnouncement:
+		return "Gate Announcement"
+	case Dot11InformationElementIDRootAnnouncement:
+		return "Root Announcement"
+	case Dot11InformationElementIDExtCapability:
+		return "Extended Capabilities"
+	case Dot11InformationElementIDAgereProprietary:
+		return "Agere Proprietary"
+	case Dot11InformationElementIDPathRequest:
+		return "Path Request"
+	case Dot11InformationElementIDPathReply:
+		return "Path Reply"
+	case Dot11InformationElementIDPathError:
+		return "Path Error"
+	case Dot11InformationElementIDCiscoCCX1CKIPDeviceName:
+		return "Cisco CCX1 CKIP + Device Name"
+	case Dot11InformationElementIDCiscoCCX2:
+		return "Cisco CCX2"
+	case Dot11InformationElementIDProxyUpdate:
+		return "Proxy Update"
+	case Dot11InformationElementIDProxyUpdateConfirmation:
+		return "Proxy Update Confirmation"
+	case Dot11InformationElementIDAuthMeshPerringExch:
+		return "Auhenticated Mesh Perring Exchange"
+	case Dot11InformationElementIDMIC:
+		return "MIC (Message Integrity Code)"
+	case Dot11InformationElementIDDestinationURI:
+		return "Destination URI"
+	case Dot11InformationElementIDUAPSDCoexistence:
+		return "U-APSD Coexistence"
+	case Dot11InformationElementIDWakeupSchedule80211ad:
+		return "Wakeup Schedule 802.11ad"
+	case Dot11InformationElementIDExtendedSchedule:
+		return "Extended Schedule"
+	case Dot11InformationElementIDSTAAvailability:
+		return "STA Availability"
+	case Dot11InformationElementIDDMGTSPEC:
+		return "DMG TSPEC"
+	case Dot11InformationElementIDNextDMGATI:
+		return "Next DMG ATI"
+	case Dot11InformationElementIDDMSCapabilities:
+		return "DMG Capabilities"
+	case Dot11InformationElementIDCiscoUnknown95:
+		return "Cisco Unknown 95"
+	case Dot11InformationElementIDVendor2:
+		return "Vendor Specific"
+	case Dot11InformationElementIDDMGOperating:
+		return "DMG Operating"
+	case Dot11InformationElementIDDMGBSSParamChange:
+		return "DMG BSS Parameter Change"
+	case Dot11InformationElementIDDMGBeamRefinement:
+		return "DMG Beam Refinement"
+	case Dot11InformationElementIDChannelMeasFeedback:
+		return "Channel Measurement Feedback"
+	case Dot11InformationElementIDAwakeWindow:
+		return "Awake Window"
+	case Dot11InformationElementIDMultiBand:
+		return "Multi Band"
+	case Dot11InformationElementIDADDBAExtension:
+		return "ADDBA Extension"
+	case Dot11InformationElementIDNEXTPCPList:
+		return "NEXTPCP List"
+	case Dot11InformationElementIDPCPHandover:
+		return "PCP Handover"
+	case Dot11InformationElementIDDMGLinkMargin:
+		return "DMG Link Margin"
+	case Dot11InformationElementIDSwitchingStream:
+		return "Switching Stream"
+	case Dot11InformationElementIDSessionTransmission:
+		return "Session Transmission"
+	case Dot11InformationElementIDDynamicTonePairReport:
+		return "Dynamic Tone Pairing Report"
+	case Dot11InformationElementIDClusterReport:
+		return "Cluster Report"
+	case Dot11InformationElementIDRelayCapabilities:
+		return "Relay Capabilities"
+	case Dot11InformationElementIDRelayTransferParameter:
+		return "Relay Transfer Parameter"
+	case Dot11InformationElementIDBeamlinkMaintenance:
+		return "Beamlink Maintenance"
+	case Dot11InformationElementIDMultipleMacSublayers:
+		return "Multiple MAC Sublayers"
+	case Dot11InformationElementIDUPID:
+		return "U-PID"
+	case Dot11InformationElementIDDMGLinkAdaptionAck:
+		return "DMG Link Adaption Acknowledgment"
+	case Dot11InformationElementIDSymbolProprietary:
+		return "Symbol Proprietary"
+	case Dot11InformationElementIDMCCAOPAdvertOverview:
+		return "MCCAOP Advertisement Overview"
+	case Dot11InformationElementIDQuietPeriodRequest:
+		return "Quiet Period Request"
+	case Dot11InformationElementIDQuietPeriodResponse:
+		return "Quiet Period Response"
+	case Dot11InformationElementIDECPACPolicy:
+		return "ECPAC Policy"
+	case Dot11InformationElementIDClusterTimeOffset:
+		return "Cluster Time Offset"
+	case Dot11InformationElementIDAntennaSectorID:
+		return "Antenna Sector ID"
+	case Dot11InformationElementIDVHTCapabilities:
+		return "VHT Capabilities (IEEE Std 802.11ac/D3.1)"
+	case Dot11InformationElementIDVHTOperation:
+		return "VHT Operation (IEEE Std 802.11ac/D3.1)"
+	case Dot11InformationElementIDExtendedBSSLoad:
+		return "Extended BSS Load"
+	case Dot11InformationElementIDWideBWChannelSwitch:
+		return "Wide Bandwidth Channel Switch"
+	case Dot11InformationElementIDVHTTxPowerEnvelope:
+		return "VHT Tx Power Envelope (IEEE Std 802.11ac/D5.0)"
+	case Dot11InformationElementIDChannelSwitchWrapper:
+		return "Channel Switch Wrapper"
+	case Dot11InformationElementIDOperatingModeNotification:
+		return "Operating Mode Notification"
+	case Dot11InformationElementIDUPSIM:
+		return "UP SIM"
+	case Dot11InformationElementIDReducedNeighborReport:
+		return "Reduced Neighbor Report"
+	case Dot11InformationElementIDTVHTOperation:
+		return "TVHT Op"
+	case Dot11InformationElementIDDeviceLocation:
+		return "Device Location"
+	case Dot11InformationElementIDWhiteSpaceMap:
+		return "White Space Map"
+	case Dot11InformationElementIDFineTuningMeasureParams:
+		return "Fine Tuning Measure Parameters"
+	case Dot11InformationElementIDVendor:
+		return "Vendor"
+	default:
+		return "Unknown information element id"
+	}
+}
+
+// Dot11 provides an IEEE 802.11 base packet header.
+// See http://standards.ieee.org/findstds/standard/802.11-2012.html
+// for excruciating detail.
+type Dot11 struct {
+	BaseLayer
+	Type           Dot11Type
+	Proto          uint8
+	Flags          Dot11Flags
+	DurationID     uint16
+	Address1       net.HardwareAddr
+	Address2       net.HardwareAddr
+	Address3       net.HardwareAddr
+	Address4       net.HardwareAddr
+	SequenceNumber uint16
+	FragmentNumber uint16
+	Checksum       uint32
+	QOS            *Dot11QOS
+	HTControl      *Dot11HTControl
+	DataLayer      gopacket.Layer
+}
+
+type Dot11QOS struct {
+	TID       uint8 /* Traffic IDentifier */
+	EOSP      bool  /* End of service period */
+	AckPolicy Dot11AckPolicy
+	TXOP      uint8
+}
+
+type Dot11HTControl struct {
+	ACConstraint bool
+	RDGMorePPDU  bool
+
+	VHT *Dot11HTControlVHT
+	HT  *Dot11HTControlHT
+}
+
+type Dot11HTControlHT struct {
+	LinkAdapationControl *Dot11LinkAdapationControl
+	CalibrationPosition  uint8
+	CalibrationSequence  uint8
+	CSISteering          uint8
+	NDPAnnouncement      bool
+	DEI                  bool
+}
+
+type Dot11HTControlVHT struct {
+	MRQ            bool
+	UnsolicitedMFB bool
+	MSI            *uint8
+	MFB            Dot11HTControlMFB
+	CompressedMSI  *uint8
+	STBCIndication bool
+	MFSI           *uint8
+	GID            *uint8
+	CodingType     *Dot11CodingType
+	FbTXBeamformed bool
+}
+
+type Dot11HTControlMFB struct {
+	NumSTS uint8
+	VHTMCS uint8
+	BW     uint8
+	SNR    int8
+}
+
+type Dot11LinkAdapationControl struct {
+	TRQ  bool
+	MRQ  bool
+	MSI  uint8
+	MFSI uint8
+	ASEL *Dot11ASEL
+	MFB  *uint8
+}
+
+type Dot11ASEL struct {
+	Command uint8
+	Data    uint8
+}
+
+type Dot11CodingType uint8
+
+const (
+	Dot11CodingTypeBCC  = 0
+	Dot11CodingTypeLDPC = 1
+)
+
+func (a Dot11CodingType) String() string {
+	switch a {
+	case Dot11CodingTypeBCC:
+		return "BCC"
+	case Dot11CodingTypeLDPC:
+		return "LDPC"
+	default:
+		return "Unknown coding type"
+	}
+}
+
+func (m *Dot11HTControlMFB) NoFeedBackPresent() bool {
+	return m.VHTMCS == 15 && m.NumSTS == 7
+}
+
+func decodeDot11(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11{}
+	err := d.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(d)
+	if d.DataLayer != nil {
+		p.AddLayer(d.DataLayer)
+	}
+	return p.NextDecoder(d.NextLayerType())
+}
+
+func (m *Dot11) LayerType() gopacket.LayerType  { return LayerTypeDot11 }
+func (m *Dot11) CanDecode() gopacket.LayerClass { return LayerTypeDot11 }
+func (m *Dot11) NextLayerType() gopacket.LayerType {
+	if m.DataLayer != nil {
+		if m.Flags.WEP() {
+			return LayerTypeDot11WEP
+		}
+		return m.DataLayer.(gopacket.DecodingLayer).NextLayerType()
+	}
+	return m.Type.LayerType()
+}
+
+func createU8(x uint8) *uint8 {
+	return &x
+}
+
+var dataDecodeMap = map[Dot11Type]func() gopacket.DecodingLayer{
+	Dot11TypeData:                   func() gopacket.DecodingLayer { return &Dot11Data{} },
+	Dot11TypeDataCFAck:              func() gopacket.DecodingLayer { return &Dot11DataCFAck{} },
+	Dot11TypeDataCFPoll:             func() gopacket.DecodingLayer { return &Dot11DataCFPoll{} },
+	Dot11TypeDataCFAckPoll:          func() gopacket.DecodingLayer { return &Dot11DataCFAckPoll{} },
+	Dot11TypeDataNull:               func() gopacket.DecodingLayer { return &Dot11DataNull{} },
+	Dot11TypeDataCFAckNoData:        func() gopacket.DecodingLayer { return &Dot11DataCFAckNoData{} },
+	Dot11TypeDataCFPollNoData:       func() gopacket.DecodingLayer { return &Dot11DataCFPollNoData{} },
+	Dot11TypeDataCFAckPollNoData:    func() gopacket.DecodingLayer { return &Dot11DataCFAckPollNoData{} },
+	Dot11TypeDataQOSData:            func() gopacket.DecodingLayer { return &Dot11DataQOSData{} },
+	Dot11TypeDataQOSDataCFAck:       func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFAck{} },
+	Dot11TypeDataQOSDataCFPoll:      func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFPoll{} },
+	Dot11TypeDataQOSDataCFAckPoll:   func() gopacket.DecodingLayer { return &Dot11DataQOSDataCFAckPoll{} },
+	Dot11TypeDataQOSNull:            func() gopacket.DecodingLayer { return &Dot11DataQOSNull{} },
+	Dot11TypeDataQOSCFPollNoData:    func() gopacket.DecodingLayer { return &Dot11DataQOSCFPollNoData{} },
+	Dot11TypeDataQOSCFAckPollNoData: func() gopacket.DecodingLayer { return &Dot11DataQOSCFAckPollNoData{} },
+}
+
+func (m *Dot11) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 10 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11 length %v too short, %v required", len(data), 10)
+	}
+	m.Type = Dot11Type((data[0])&0xFC) >> 2
+
+	m.DataLayer = nil
+	m.Proto = uint8(data[0]) & 0x0003
+	m.Flags = Dot11Flags(data[1])
+	m.DurationID = binary.LittleEndian.Uint16(data[2:4])
+	m.Address1 = net.HardwareAddr(data[4:10])
+
+	offset := 10
+
+	mainType := m.Type.MainType()
+
+	switch mainType {
+	case Dot11TypeCtrl:
+		switch m.Type {
+		case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck:
+			if len(data) < offset+6 {
+				df.SetTruncated()
+				return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6)
+			}
+			m.Address2 = net.HardwareAddr(data[offset : offset+6])
+			offset += 6
+		}
+	case Dot11TypeMgmt, Dot11TypeData:
+		if len(data) < offset+14 {
+			df.SetTruncated()
+			return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+14)
+		}
+		m.Address2 = net.HardwareAddr(data[offset : offset+6])
+		offset += 6
+		m.Address3 = net.HardwareAddr(data[offset : offset+6])
+		offset += 6
+
+		m.SequenceNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0xFFF0) >> 4
+		m.FragmentNumber = (binary.LittleEndian.Uint16(data[offset:offset+2]) & 0x000F)
+		offset += 2
+	}
+
+	if mainType == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() {
+		if len(data) < offset+6 {
+			df.SetTruncated()
+			return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6)
+		}
+		m.Address4 = net.HardwareAddr(data[offset : offset+6])
+		offset += 6
+	}
+
+	if m.Type.QOS() {
+		if len(data) < offset+2 {
+			df.SetTruncated()
+			return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6)
+		}
+		m.QOS = &Dot11QOS{
+			TID:       (uint8(data[offset]) & 0x0F),
+			EOSP:      (uint8(data[offset]) & 0x10) == 0x10,
+			AckPolicy: Dot11AckPolicy((uint8(data[offset]) & 0x60) >> 5),
+			TXOP:      uint8(data[offset+1]),
+		}
+		offset += 2
+	}
+	if m.Flags.Order() && (m.Type.QOS() || mainType == Dot11TypeMgmt) {
+		if len(data) < offset+4 {
+			df.SetTruncated()
+			return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+6)
+		}
+
+		htc := &Dot11HTControl{
+			ACConstraint: data[offset+3]&0x40 != 0,
+			RDGMorePPDU:  data[offset+3]&0x80 != 0,
+		}
+		m.HTControl = htc
+
+		if data[offset]&0x1 != 0 { // VHT Variant
+			vht := &Dot11HTControlVHT{}
+			htc.VHT = vht
+			vht.MRQ = data[offset]&0x4 != 0
+			vht.UnsolicitedMFB = data[offset+3]&0x20 != 0
+			vht.MFB = Dot11HTControlMFB{
+				NumSTS: uint8(data[offset+1] >> 1 & 0x7),
+				VHTMCS: uint8(data[offset+1] >> 4 & 0xF),
+				BW:     uint8(data[offset+2] & 0x3),
+				SNR:    int8((-(data[offset+2] >> 2 & 0x20))+data[offset+2]>>2&0x1F) + 22,
+			}
+
+			if vht.UnsolicitedMFB {
+				if !vht.MFB.NoFeedBackPresent() {
+					vht.CompressedMSI = createU8(data[offset] >> 3 & 0x3)
+					vht.STBCIndication = data[offset]&0x20 != 0
+					vht.CodingType = (*Dot11CodingType)(createU8(data[offset+3] >> 3 & 0x1))
+					vht.FbTXBeamformed = data[offset+3]&0x10 != 0
+					vht.GID = createU8(
+						data[offset]>>6 +
+							(data[offset+1] & 0x1 << 2) +
+							data[offset+3]&0x7<<3)
+				}
+			} else {
+				if vht.MRQ {
+					vht.MSI = createU8((data[offset] >> 3) & 0x07)
+				}
+				vht.MFSI = createU8(data[offset]>>6 + (data[offset+1] & 0x1 << 2))
+			}
+
+		} else { // HT Variant
+			ht := &Dot11HTControlHT{}
+			htc.HT = ht
+
+			lac := &Dot11LinkAdapationControl{}
+			ht.LinkAdapationControl = lac
+			lac.TRQ = data[offset]&0x2 != 0
+			lac.MFSI = data[offset]>>6&0x3 + data[offset+1]&0x1<<3
+			if data[offset]&0x3C == 0x38 { // ASEL
+				lac.ASEL = &Dot11ASEL{
+					Command: data[offset+1] >> 1 & 0x7,
+					Data:    data[offset+1] >> 4 & 0xF,
+				}
+			} else {
+				lac.MRQ = data[offset]&0x4 != 0
+				if lac.MRQ {
+					lac.MSI = data[offset] >> 3 & 0x7
+				}
+				lac.MFB = createU8(data[offset+1] >> 1)
+			}
+			ht.CalibrationPosition = data[offset+2] & 0x3
+			ht.CalibrationSequence = data[offset+2] >> 2 & 0x3
+			ht.CSISteering = data[offset+2] >> 6 & 0x3
+			ht.NDPAnnouncement = data[offset+3]&0x1 != 0
+			if mainType != Dot11TypeMgmt {
+				ht.DEI = data[offset+3]&0x20 != 0
+			}
+		}
+
+		offset += 4
+	}
+
+	if len(data) < offset+4 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11 length %v too short, %v required", len(data), offset+4)
+	}
+
+	m.BaseLayer = BaseLayer{
+		Contents: data[0:offset],
+		Payload:  data[offset : len(data)-4],
+	}
+
+	if mainType == Dot11TypeData {
+		d := dataDecodeMap[m.Type]
+		if d == nil {
+			return fmt.Errorf("unsupported type: %v", m.Type)
+		}
+		l := d()
+		err := l.DecodeFromBytes(m.BaseLayer.Payload, df)
+		if err != nil {
+			return err
+		}
+		m.DataLayer = l.(gopacket.Layer)
+	}
+
+	m.Checksum = binary.LittleEndian.Uint32(data[len(data)-4 : len(data)])
+	return nil
+}
+
+func (m *Dot11) ChecksumValid() bool {
+	// only for CTRL and MGMT frames
+	h := crc32.NewIEEE()
+	h.Write(m.Contents)
+	h.Write(m.Payload)
+	return m.Checksum == h.Sum32()
+}
+
+func (m Dot11) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(24)
+
+	if err != nil {
+		return err
+	}
+
+	buf[0] = (uint8(m.Type) << 2) | m.Proto
+	buf[1] = uint8(m.Flags)
+
+	binary.LittleEndian.PutUint16(buf[2:4], m.DurationID)
+
+	copy(buf[4:10], m.Address1)
+
+	offset := 10
+
+	switch m.Type.MainType() {
+	case Dot11TypeCtrl:
+		switch m.Type {
+		case Dot11TypeCtrlRTS, Dot11TypeCtrlPowersavePoll, Dot11TypeCtrlCFEnd, Dot11TypeCtrlCFEndAck:
+			copy(buf[offset:offset+6], m.Address2)
+			offset += 6
+		}
+	case Dot11TypeMgmt, Dot11TypeData:
+		copy(buf[offset:offset+6], m.Address2)
+		offset += 6
+		copy(buf[offset:offset+6], m.Address3)
+		offset += 6
+
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], (m.SequenceNumber<<4)|m.FragmentNumber)
+		offset += 2
+	}
+
+	if m.Type.MainType() == Dot11TypeData && m.Flags.FromDS() && m.Flags.ToDS() {
+		copy(buf[offset:offset+6], m.Address4)
+		offset += 6
+	}
+
+	return nil
+}
+
+// Dot11Mgmt is a base for all IEEE 802.11 management layers.
+type Dot11Mgmt struct {
+	BaseLayer
+}
+
+func (m *Dot11Mgmt) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload }
+func (m *Dot11Mgmt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+// Dot11Ctrl is a base for all IEEE 802.11 control layers.
+type Dot11Ctrl struct {
+	BaseLayer
+}
+
+func (m *Dot11Ctrl) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload }
+
+func (m *Dot11Ctrl) LayerType() gopacket.LayerType  { return LayerTypeDot11Ctrl }
+func (m *Dot11Ctrl) CanDecode() gopacket.LayerClass { return LayerTypeDot11Ctrl }
+func (m *Dot11Ctrl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+func decodeDot11Ctrl(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11Ctrl{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+// Dot11WEP contains WEP encrpted IEEE 802.11 data.
+type Dot11WEP struct {
+	BaseLayer
+}
+
+func (m *Dot11WEP) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload }
+
+func (m *Dot11WEP) LayerType() gopacket.LayerType  { return LayerTypeDot11WEP }
+func (m *Dot11WEP) CanDecode() gopacket.LayerClass { return LayerTypeDot11WEP }
+func (m *Dot11WEP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+func decodeDot11WEP(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11WEP{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+// Dot11Data is a base for all IEEE 802.11 data layers.
+type Dot11Data struct {
+	BaseLayer
+}
+
+func (m *Dot11Data) NextLayerType() gopacket.LayerType {
+	return LayerTypeLLC
+}
+
+func (m *Dot11Data) LayerType() gopacket.LayerType  { return LayerTypeDot11Data }
+func (m *Dot11Data) CanDecode() gopacket.LayerClass { return LayerTypeDot11Data }
+func (m *Dot11Data) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Payload = data
+	return nil
+}
+
+func decodeDot11Data(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11Data{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type Dot11DataCFAck struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFAck) LayerType() gopacket.LayerType  { return LayerTypeDot11DataCFAck }
+func (m *Dot11DataCFAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAck }
+func (m *Dot11DataCFAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataCFPoll struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFPoll(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFPoll{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFPoll) LayerType() gopacket.LayerType  { return LayerTypeDot11DataCFPoll }
+func (m *Dot11DataCFPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFPoll }
+func (m *Dot11DataCFPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataCFAckPoll struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFAckPoll(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFAckPoll{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFAckPoll) LayerType() gopacket.LayerType  { return LayerTypeDot11DataCFAckPoll }
+func (m *Dot11DataCFAckPoll) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckPoll }
+func (m *Dot11DataCFAckPoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataNull struct {
+	Dot11Data
+}
+
+func decodeDot11DataNull(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataNull{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataNull) LayerType() gopacket.LayerType  { return LayerTypeDot11DataNull }
+func (m *Dot11DataNull) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataNull }
+func (m *Dot11DataNull) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataCFAckNoData struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFAckNoData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFAckNoData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFAckNoData) LayerType() gopacket.LayerType  { return LayerTypeDot11DataCFAckNoData }
+func (m *Dot11DataCFAckNoData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataCFAckNoData }
+func (m *Dot11DataCFAckNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataCFPollNoData struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFPollNoData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFPollNoData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFPollNoData) LayerType() gopacket.LayerType { return LayerTypeDot11DataCFPollNoData }
+func (m *Dot11DataCFPollNoData) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataCFPollNoData
+}
+func (m *Dot11DataCFPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataCFAckPollNoData struct {
+	Dot11Data
+}
+
+func decodeDot11DataCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataCFAckPollNoData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataCFAckPollNoData) LayerType() gopacket.LayerType {
+	return LayerTypeDot11DataCFAckPollNoData
+}
+func (m *Dot11DataCFAckPollNoData) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataCFAckPollNoData
+}
+func (m *Dot11DataCFAckPollNoData) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Data.DecodeFromBytes(data, df)
+}
+
+type Dot11DataQOS struct {
+	Dot11Ctrl
+}
+
+func (m *Dot11DataQOS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.BaseLayer = BaseLayer{Payload: data}
+	return nil
+}
+
+type Dot11DataQOSData struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSData) LayerType() gopacket.LayerType  { return LayerTypeDot11DataQOSData }
+func (m *Dot11DataQOSData) CanDecode() gopacket.LayerClass { return LayerTypeDot11DataQOSData }
+
+func (m *Dot11DataQOSData) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11Data
+}
+
+type Dot11DataQOSDataCFAck struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSDataCFAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSDataCFAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSDataCFAck) LayerType() gopacket.LayerType { return LayerTypeDot11DataQOSDataCFAck }
+func (m *Dot11DataQOSDataCFAck) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataQOSDataCFAck
+}
+func (m *Dot11DataQOSDataCFAck) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFAck }
+
+type Dot11DataQOSDataCFPoll struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSDataCFPoll(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSDataCFPoll{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSDataCFPoll) LayerType() gopacket.LayerType {
+	return LayerTypeDot11DataQOSDataCFPoll
+}
+func (m *Dot11DataQOSDataCFPoll) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataQOSDataCFPoll
+}
+func (m *Dot11DataQOSDataCFPoll) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataCFPoll }
+
+type Dot11DataQOSDataCFAckPoll struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSDataCFAckPoll(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSDataCFAckPoll{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSDataCFAckPoll) LayerType() gopacket.LayerType {
+	return LayerTypeDot11DataQOSDataCFAckPoll
+}
+func (m *Dot11DataQOSDataCFAckPoll) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataQOSDataCFAckPoll
+}
+func (m *Dot11DataQOSDataCFAckPoll) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11DataCFAckPoll
+}
+
+type Dot11DataQOSNull struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSNull(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSNull{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSNull) LayerType() gopacket.LayerType     { return LayerTypeDot11DataQOSNull }
+func (m *Dot11DataQOSNull) CanDecode() gopacket.LayerClass    { return LayerTypeDot11DataQOSNull }
+func (m *Dot11DataQOSNull) NextLayerType() gopacket.LayerType { return LayerTypeDot11DataNull }
+
+type Dot11DataQOSCFPollNoData struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSCFPollNoData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSCFPollNoData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSCFPollNoData) LayerType() gopacket.LayerType {
+	return LayerTypeDot11DataQOSCFPollNoData
+}
+func (m *Dot11DataQOSCFPollNoData) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataQOSCFPollNoData
+}
+func (m *Dot11DataQOSCFPollNoData) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11DataCFPollNoData
+}
+
+type Dot11DataQOSCFAckPollNoData struct {
+	Dot11DataQOS
+}
+
+func decodeDot11DataQOSCFAckPollNoData(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11DataQOSCFAckPollNoData{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11DataQOSCFAckPollNoData) LayerType() gopacket.LayerType {
+	return LayerTypeDot11DataQOSCFAckPollNoData
+}
+func (m *Dot11DataQOSCFAckPollNoData) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11DataQOSCFAckPollNoData
+}
+func (m *Dot11DataQOSCFAckPollNoData) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11DataCFAckPollNoData
+}
+
+type Dot11InformationElement struct {
+	BaseLayer
+	ID     Dot11InformationElementID
+	Length uint8
+	OUI    []byte
+	Info   []byte
+}
+
+func (m *Dot11InformationElement) LayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+func (m *Dot11InformationElement) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11InformationElement
+}
+
+func (m *Dot11InformationElement) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+
+func (m *Dot11InformationElement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 2 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), 2)
+	}
+	m.ID = Dot11InformationElementID(data[0])
+	m.Length = data[1]
+	offset := int(2)
+
+	if len(data) < offset+int(m.Length) {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11InformationElement length %v too short, %v required", len(data), offset+int(m.Length))
+	}
+	if len(data) < offset+4 {
+		df.SetTruncated()
+		return fmt.Errorf("vendor extension size < %d", offset+int(m.Length))
+	}
+	if m.ID == 221 {
+		// Vendor extension
+		m.OUI = data[offset : offset+4]
+		m.Info = data[offset+4 : offset+int(m.Length)]
+	} else {
+		m.Info = data[offset : offset+int(m.Length)]
+	}
+
+	offset += int(m.Length)
+
+	m.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
+	return nil
+}
+
+func (d *Dot11InformationElement) String() string {
+	if d.ID == 0 {
+		return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, SSID: %v)", d.ID, d.Length, string(d.Info))
+	} else if d.ID == 1 {
+		rates := ""
+		for i := 0; i < len(d.Info); i++ {
+			if d.Info[i]&0x80 == 0 {
+				rates += fmt.Sprintf("%.1f ", float32(d.Info[i])*0.5)
+			} else {
+				rates += fmt.Sprintf("%.1f* ", float32(d.Info[i]&0x7F)*0.5)
+			}
+		}
+		return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, Rates: %s Mbit)", d.ID, d.Length, rates)
+	} else if d.ID == 221 {
+		return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, OUI: %X, Info: %X)", d.ID, d.Length, d.OUI, d.Info)
+	} else {
+		return fmt.Sprintf("802.11 Information Element (ID: %v, Length: %v, Info: %X)", d.ID, d.Length, d.Info)
+	}
+}
+
+func (m Dot11InformationElement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	length := len(m.Info) + len(m.OUI)
+	if buf, err := b.PrependBytes(2 + length); err != nil {
+		return err
+	} else {
+		buf[0] = uint8(m.ID)
+		buf[1] = uint8(length)
+		copy(buf[2:], m.OUI)
+		copy(buf[2+len(m.OUI):], m.Info)
+	}
+	return nil
+}
+
+func decodeDot11InformationElement(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11InformationElement{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type Dot11CtrlCTS struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlCTS(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlCTS{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlCTS) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlCTS
+}
+func (m *Dot11CtrlCTS) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlCTS
+}
+func (m *Dot11CtrlCTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlRTS struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlRTS(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlRTS{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlRTS) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlRTS
+}
+func (m *Dot11CtrlRTS) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlRTS
+}
+func (m *Dot11CtrlRTS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlBlockAckReq struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlBlockAckReq(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlBlockAckReq{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlBlockAckReq) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlBlockAckReq
+}
+func (m *Dot11CtrlBlockAckReq) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlBlockAckReq
+}
+func (m *Dot11CtrlBlockAckReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlBlockAck struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlBlockAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlBlockAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlBlockAck) LayerType() gopacket.LayerType  { return LayerTypeDot11CtrlBlockAck }
+func (m *Dot11CtrlBlockAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlBlockAck }
+func (m *Dot11CtrlBlockAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlPowersavePoll struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlPowersavePoll(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlPowersavePoll{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlPowersavePoll) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlPowersavePoll
+}
+func (m *Dot11CtrlPowersavePoll) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlPowersavePoll
+}
+func (m *Dot11CtrlPowersavePoll) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlAck struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlAck) LayerType() gopacket.LayerType  { return LayerTypeDot11CtrlAck }
+func (m *Dot11CtrlAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11CtrlAck }
+func (m *Dot11CtrlAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlCFEnd struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlCFEnd(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlCFEnd{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlCFEnd) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlCFEnd
+}
+func (m *Dot11CtrlCFEnd) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlCFEnd
+}
+func (m *Dot11CtrlCFEnd) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11CtrlCFEndAck struct {
+	Dot11Ctrl
+}
+
+func decodeDot11CtrlCFEndAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11CtrlCFEndAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11CtrlCFEndAck) LayerType() gopacket.LayerType {
+	return LayerTypeDot11CtrlCFEndAck
+}
+func (m *Dot11CtrlCFEndAck) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11CtrlCFEndAck
+}
+func (m *Dot11CtrlCFEndAck) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	return m.Dot11Ctrl.DecodeFromBytes(data, df)
+}
+
+type Dot11MgmtAssociationReq struct {
+	Dot11Mgmt
+	CapabilityInfo uint16
+	ListenInterval uint16
+}
+
+func decodeDot11MgmtAssociationReq(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtAssociationReq{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtAssociationReq) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtAssociationReq
+}
+func (m *Dot11MgmtAssociationReq) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtAssociationReq
+}
+func (m *Dot11MgmtAssociationReq) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+func (m *Dot11MgmtAssociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtAssociationReq length %v too short, %v required", len(data), 4)
+	}
+	m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2])
+	m.ListenInterval = binary.LittleEndian.Uint16(data[2:4])
+	m.Payload = data[4:]
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtAssociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(4)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo)
+	binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval)
+
+	return nil
+}
+
+type Dot11MgmtAssociationResp struct {
+	Dot11Mgmt
+	CapabilityInfo uint16
+	Status         Dot11Status
+	AID            uint16
+}
+
+func decodeDot11MgmtAssociationResp(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtAssociationResp{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtAssociationResp) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtAssociationResp
+}
+func (m *Dot11MgmtAssociationResp) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtAssociationResp
+}
+func (m *Dot11MgmtAssociationResp) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+func (m *Dot11MgmtAssociationResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 6 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtAssociationResp length %v too short, %v required", len(data), 6)
+	}
+	m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2])
+	m.Status = Dot11Status(binary.LittleEndian.Uint16(data[2:4]))
+	m.AID = binary.LittleEndian.Uint16(data[4:6])
+	m.Payload = data[6:]
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtAssociationResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(6)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo)
+	binary.LittleEndian.PutUint16(buf[2:4], uint16(m.Status))
+	binary.LittleEndian.PutUint16(buf[4:6], m.AID)
+
+	return nil
+}
+
+type Dot11MgmtReassociationReq struct {
+	Dot11Mgmt
+	CapabilityInfo   uint16
+	ListenInterval   uint16
+	CurrentApAddress net.HardwareAddr
+}
+
+func decodeDot11MgmtReassociationReq(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtReassociationReq{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtReassociationReq) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtReassociationReq
+}
+func (m *Dot11MgmtReassociationReq) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtReassociationReq
+}
+func (m *Dot11MgmtReassociationReq) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+func (m *Dot11MgmtReassociationReq) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 10 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtReassociationReq length %v too short, %v required", len(data), 10)
+	}
+	m.CapabilityInfo = binary.LittleEndian.Uint16(data[0:2])
+	m.ListenInterval = binary.LittleEndian.Uint16(data[2:4])
+	m.CurrentApAddress = net.HardwareAddr(data[4:10])
+	m.Payload = data[10:]
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtReassociationReq) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(10)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], m.CapabilityInfo)
+	binary.LittleEndian.PutUint16(buf[2:4], m.ListenInterval)
+
+	copy(buf[4:10], m.CurrentApAddress)
+
+	return nil
+}
+
+type Dot11MgmtReassociationResp struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtReassociationResp(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtReassociationResp{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtReassociationResp) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtReassociationResp
+}
+func (m *Dot11MgmtReassociationResp) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtReassociationResp
+}
+func (m *Dot11MgmtReassociationResp) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+
+type Dot11MgmtProbeReq struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtProbeReq(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtProbeReq{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtProbeReq) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtProbeReq }
+func (m *Dot11MgmtProbeReq) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeReq }
+func (m *Dot11MgmtProbeReq) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+
+type Dot11MgmtProbeResp struct {
+	Dot11Mgmt
+	Timestamp uint64
+	Interval  uint16
+	Flags     uint16
+}
+
+func decodeDot11MgmtProbeResp(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtProbeResp{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtProbeResp) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtProbeResp }
+func (m *Dot11MgmtProbeResp) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtProbeResp }
+func (m *Dot11MgmtProbeResp) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 12 {
+		df.SetTruncated()
+
+		return fmt.Errorf("Dot11MgmtProbeResp length %v too short, %v required", len(data), 12)
+	}
+
+	m.Timestamp = binary.LittleEndian.Uint64(data[0:8])
+	m.Interval = binary.LittleEndian.Uint16(data[8:10])
+	m.Flags = binary.LittleEndian.Uint16(data[10:12])
+	m.Payload = data[12:]
+
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m *Dot11MgmtProbeResp) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+
+func (m Dot11MgmtProbeResp) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(12)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp)
+	binary.LittleEndian.PutUint16(buf[8:10], m.Interval)
+	binary.LittleEndian.PutUint16(buf[10:12], m.Flags)
+
+	return nil
+}
+
+type Dot11MgmtMeasurementPilot struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtMeasurementPilot(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtMeasurementPilot{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtMeasurementPilot) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtMeasurementPilot
+}
+func (m *Dot11MgmtMeasurementPilot) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtMeasurementPilot
+}
+
+type Dot11MgmtBeacon struct {
+	Dot11Mgmt
+	Timestamp uint64
+	Interval  uint16
+	Flags     uint16
+}
+
+func decodeDot11MgmtBeacon(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtBeacon{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtBeacon) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtBeacon }
+func (m *Dot11MgmtBeacon) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtBeacon }
+func (m *Dot11MgmtBeacon) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 12 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtBeacon length %v too short, %v required", len(data), 12)
+	}
+	m.Timestamp = binary.LittleEndian.Uint64(data[0:8])
+	m.Interval = binary.LittleEndian.Uint16(data[8:10])
+	m.Flags = binary.LittleEndian.Uint16(data[10:12])
+	m.Payload = data[12:]
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m *Dot11MgmtBeacon) NextLayerType() gopacket.LayerType { return LayerTypeDot11InformationElement }
+
+func (m Dot11MgmtBeacon) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(12)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint64(buf[0:8], m.Timestamp)
+	binary.LittleEndian.PutUint16(buf[8:10], m.Interval)
+	binary.LittleEndian.PutUint16(buf[10:12], m.Flags)
+
+	return nil
+}
+
+type Dot11MgmtATIM struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtATIM(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtATIM{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtATIM) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtATIM }
+func (m *Dot11MgmtATIM) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtATIM }
+
+type Dot11MgmtDisassociation struct {
+	Dot11Mgmt
+	Reason Dot11Reason
+}
+
+func decodeDot11MgmtDisassociation(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtDisassociation{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtDisassociation) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtDisassociation
+}
+func (m *Dot11MgmtDisassociation) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtDisassociation
+}
+func (m *Dot11MgmtDisassociation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 2 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtDisassociation length %v too short, %v required", len(data), 2)
+	}
+	m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2]))
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtDisassociation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(2)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason))
+
+	return nil
+}
+
+type Dot11MgmtAuthentication struct {
+	Dot11Mgmt
+	Algorithm Dot11Algorithm
+	Sequence  uint16
+	Status    Dot11Status
+}
+
+func decodeDot11MgmtAuthentication(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtAuthentication{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtAuthentication) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtAuthentication
+}
+func (m *Dot11MgmtAuthentication) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtAuthentication
+}
+func (m *Dot11MgmtAuthentication) NextLayerType() gopacket.LayerType {
+	return LayerTypeDot11InformationElement
+}
+func (m *Dot11MgmtAuthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 6 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtAuthentication length %v too short, %v required", len(data), 6)
+	}
+	m.Algorithm = Dot11Algorithm(binary.LittleEndian.Uint16(data[0:2]))
+	m.Sequence = binary.LittleEndian.Uint16(data[2:4])
+	m.Status = Dot11Status(binary.LittleEndian.Uint16(data[4:6]))
+	m.Payload = data[6:]
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtAuthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(6)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Algorithm))
+	binary.LittleEndian.PutUint16(buf[2:4], m.Sequence)
+	binary.LittleEndian.PutUint16(buf[4:6], uint16(m.Status))
+
+	return nil
+}
+
+type Dot11MgmtDeauthentication struct {
+	Dot11Mgmt
+	Reason Dot11Reason
+}
+
+func decodeDot11MgmtDeauthentication(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtDeauthentication{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtDeauthentication) LayerType() gopacket.LayerType {
+	return LayerTypeDot11MgmtDeauthentication
+}
+func (m *Dot11MgmtDeauthentication) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot11MgmtDeauthentication
+}
+func (m *Dot11MgmtDeauthentication) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 2 {
+		df.SetTruncated()
+		return fmt.Errorf("Dot11MgmtDeauthentication length %v too short, %v required", len(data), 2)
+	}
+	m.Reason = Dot11Reason(binary.LittleEndian.Uint16(data[0:2]))
+	return m.Dot11Mgmt.DecodeFromBytes(data, df)
+}
+
+func (m Dot11MgmtDeauthentication) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(2)
+
+	if err != nil {
+		return err
+	}
+
+	binary.LittleEndian.PutUint16(buf[0:2], uint16(m.Reason))
+
+	return nil
+}
+
+type Dot11MgmtAction struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtAction(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtAction{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtAction) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtAction }
+func (m *Dot11MgmtAction) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtAction }
+
+type Dot11MgmtActionNoAck struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtActionNoAck(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtActionNoAck{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtActionNoAck) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtActionNoAck }
+func (m *Dot11MgmtActionNoAck) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtActionNoAck }
+
+type Dot11MgmtArubaWLAN struct {
+	Dot11Mgmt
+}
+
+func decodeDot11MgmtArubaWLAN(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot11MgmtArubaWLAN{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *Dot11MgmtArubaWLAN) LayerType() gopacket.LayerType  { return LayerTypeDot11MgmtArubaWLAN }
+func (m *Dot11MgmtArubaWLAN) CanDecode() gopacket.LayerClass { return LayerTypeDot11MgmtArubaWLAN }

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