From c78d825be73ed80480a17892c8fc9e0226571daf Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Thu, 22 Aug 2024 17:39:06 +0200
Subject: [PATCH 01/48] Add a source pod create retry function for egress
 firewall e2e. In our e2e tests, a strange behaviour for ipv6 was seen: newly
 created pod can't reach ipv6 destination. But if the same pod is re-created,
 everything works. We don't know what causes that behaviour, so given function
 is a workaround for this issue. It also only historically fails for the first
 ef test "Should validate the egress firewall policy functionality for allowed
 IP", so only used there for now.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 test/e2e/egress_firewall.go | 39 ++++++++++++++++++++++++++++++++++---
 1 file changed, 36 insertions(+), 3 deletions(-)

diff --git a/test/e2e/egress_firewall.go b/test/e2e/egress_firewall.go
index 8105cf1c55e..1e941b650c6 100644
--- a/test/e2e/egress_firewall.go
+++ b/test/e2e/egress_firewall.go
@@ -23,6 +23,7 @@ import (
 	"k8s.io/kubernetes/test/e2e/framework"
 	e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
 	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
+	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 	utilnet "k8s.io/utils/net"
 )
 
@@ -151,6 +152,36 @@ var _ = ginkgo.Describe("e2e egress firewall policy validation", func() {
 			}
 		}
 
+		// createSrcPodWithRetry creates a pod that can reach the specified destination with a given number of retries.
+		// In our e2e tests, a strange behaviour for ipv6 was seen: newly created pod can't reach ipv6 destination.
+		// But if the same pod is re-created, everything works.
+		// We don't know what causes that behaviour, so given function is a workaround for this issue.
+		// It also only historically fails for the first ef test "Should validate the egress firewall policy functionality for allowed IP",
+		// so only used there for now.
+		createSrcPodWithRetry := func(retries int, reachableDst string, reachablePort int,
+			podName, nodeName string, ipCheckInterval, ipCheckTimeout time.Duration, f *framework.Framework) {
+			for i := 0; i < retries; i++ {
+				createSrcPod(podName, nodeName, ipCheckInterval, ipCheckTimeout, f)
+				testContainer := fmt.Sprintf("%s-container", podName)
+				testContainerFlag := fmt.Sprintf("--container=%s", testContainer)
+				for connectRetry := 0; connectRetry < 5; connectRetry++ {
+					_, err := e2ekubectl.RunKubectl(f.Namespace.Name, "exec", podName, testContainerFlag, "--",
+						"curl", "-s", "--connect-timeout", fmt.Sprint(testTimeout), net.JoinHostPort(reachableDst, fmt.Sprint(reachablePort)))
+					if err == nil {
+						return
+					}
+				}
+				err := e2epod.DeletePodWithWait(context.TODO(), f.ClientSet, &v1.Pod{
+					ObjectMeta: metav1.ObjectMeta{
+						Namespace: f.Namespace.Name,
+						Name:      podName,
+					},
+				})
+				gomega.Expect(err).NotTo(gomega.HaveOccurred())
+			}
+			framework.Failf("Failed to create pod %s that can reach %s:%d after %d retries", podName, reachableDst, reachablePort, retries)
+		}
+
 		ginkgo.BeforeEach(func() {
 			externalContainer1IPV4, externalContainer1IPV6 := createClusterExternalContainer(externalContainerName1, agnhostImage,
 				[]string{"--network", ciNetworkName, "-p", fmt.Sprintf("%d:%d", externalContainerPort1, externalContainerPort1)},
@@ -203,6 +234,11 @@ var _ = ginkgo.Describe("e2e egress firewall policy validation", func() {
 
 		ginkgo.It("Should validate the egress firewall policy functionality for allowed IP", func() {
 			srcPodName := "e2e-egress-fw-src-pod"
+
+			// create the pod that will be used as the source for the connectivity test
+			createSrcPodWithRetry(3, externalContainer1IP, externalContainerPort1,
+				srcPodName, serverNodeInfo.name, retryInterval, retryTimeout, f)
+
 			// egress firewall crd yaml configuration
 			var egressFirewallConfig = fmt.Sprintf(`kind: EgressFirewall
 apiVersion: k8s.ovn.org/v1
@@ -220,9 +256,6 @@ spec:
 `, f.Namespace.Name, externalContainer1IP, singleIPMask, denyAllCIDR)
 			applyEF(egressFirewallConfig, f.Namespace.Name)
 
-			// create the pod that will be used as the source for the connectivity test
-			createSrcPod(srcPodName, serverNodeInfo.name, retryInterval, retryTimeout, f)
-
 			// Verify the remote host/port as explicitly allowed by the firewall policy is reachable
 			ginkgo.By(fmt.Sprintf("Verifying connectivity to an explicitly allowed host %s is permitted as defined "+
 				"by the external firewall policy", externalContainer1IP))

From 00c1534514336229d962f3ff60d14406c83d02ae Mon Sep 17 00:00:00 2001
From: Martin Kennelly <mkennell@redhat.com>
Date: Fri, 30 Aug 2024 16:41:13 +0100
Subject: [PATCH 02/48] Emit Event if NAD cannot be parsed

Signed-off-by: Martin Kennelly <mkennell@redhat.com>
---
 .../secondary_network_cluster_manager.go               |  2 +-
 .../network_attach_def_controller.go                   | 10 +++++++++-
 .../network_attach_def_controller_test.go              |  1 +
 .../network_controller_manager.go                      |  2 +-
 .../node_network_controller_manager.go                 |  2 +-
 5 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/go-controller/pkg/clustermanager/secondary_network_cluster_manager.go b/go-controller/pkg/clustermanager/secondary_network_cluster_manager.go
index b24c2b6f465..fc31fe5bbdc 100644
--- a/go-controller/pkg/clustermanager/secondary_network_cluster_manager.go
+++ b/go-controller/pkg/clustermanager/secondary_network_cluster_manager.go
@@ -54,7 +54,7 @@ func newSecondaryNetworkClusterManager(ovnClient *util.OVNClusterManagerClientse
 		recorder:           recorder,
 	}
 
-	sncm.nadController, err = nad.NewNetAttachDefinitionController("cluster-manager", sncm, wf)
+	sncm.nadController, err = nad.NewNetAttachDefinitionController("cluster-manager", sncm, wf, recorder)
 	if err != nil {
 		return nil, err
 	}
diff --git a/go-controller/pkg/network-attach-def-controller/network_attach_def_controller.go b/go-controller/pkg/network-attach-def-controller/network_attach_def_controller.go
index 0250c4e6959..4d8605664e8 100644
--- a/go-controller/pkg/network-attach-def-controller/network_attach_def_controller.go
+++ b/go-controller/pkg/network-attach-def-controller/network_attach_def_controller.go
@@ -7,10 +7,12 @@ import (
 	"reflect"
 	"time"
 
+	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/client-go/tools/cache"
+	"k8s.io/client-go/tools/record"
 	"k8s.io/client-go/util/workqueue"
 	"k8s.io/klog/v2"
 
@@ -56,7 +58,7 @@ type NetAttachDefinitionController struct {
 	name               string
 	netAttachDefLister nadlisters.NetworkAttachmentDefinitionLister
 	controller         controller.Controller
-
+	recorder           record.EventRecorder
 	// networkManager is used to manage the network controllers
 	networkManager networkManager
 
@@ -70,9 +72,11 @@ func NewNetAttachDefinitionController(
 	name string,
 	ncm NetworkControllerManager,
 	wf watchFactory,
+	recorder record.EventRecorder,
 ) (*NetAttachDefinitionController, error) {
 	nadController := &NetAttachDefinitionController{
 		name:           fmt.Sprintf("[%s NAD controller]", name),
+		recorder:       recorder,
 		networkManager: newNetworkManager(name, ncm),
 		networks:       map[string]util.NetInfo{},
 		nads:           map[string]string{},
@@ -182,6 +186,10 @@ func (nadController *NetAttachDefinitionController) syncNAD(key string, nad *net
 	if nad != nil {
 		nadNetwork, err = util.ParseNADInfo(nad)
 		if err != nil {
+			if nadController.recorder != nil {
+				nadController.recorder.Eventf(&corev1.ObjectReference{Kind: nad.Kind, Namespace: nad.Namespace, Name: nad.Name}, corev1.EventTypeWarning,
+					"InvalidConfig", "Failed to parse network config: %v", err.Error())
+			}
 			klog.Errorf("%s: failed parsing NAD %s: %v", nadController.name, key, err)
 			return nil
 		}
diff --git a/go-controller/pkg/network-attach-def-controller/network_attach_def_controller_test.go b/go-controller/pkg/network-attach-def-controller/network_attach_def_controller_test.go
index ac4e1f5c63a..85f842b2292 100644
--- a/go-controller/pkg/network-attach-def-controller/network_attach_def_controller_test.go
+++ b/go-controller/pkg/network-attach-def-controller/network_attach_def_controller_test.go
@@ -491,6 +491,7 @@ func TestSyncAll(t *testing.T) {
 				"SUT",
 				tncm,
 				wf,
+				nil,
 			)
 			g.Expect(err).ToNot(gomega.HaveOccurred())
 
diff --git a/go-controller/pkg/network-controller-manager/network_controller_manager.go b/go-controller/pkg/network-controller-manager/network_controller_manager.go
index fc735baf3e1..4b26fc58a7f 100644
--- a/go-controller/pkg/network-controller-manager/network_controller_manager.go
+++ b/go-controller/pkg/network-controller-manager/network_controller_manager.go
@@ -205,7 +205,7 @@ func NewNetworkControllerManager(ovnClient *util.OVNClientset, wf *factory.Watch
 
 	var err error
 	if config.OVNKubernetesFeature.EnableMultiNetwork {
-		cm.nadController, err = nad.NewNetAttachDefinitionController("network-controller-manager", cm, wf)
+		cm.nadController, err = nad.NewNetAttachDefinitionController("network-controller-manager", cm, wf, nil)
 		if err != nil {
 			return nil, err
 		}
diff --git a/go-controller/pkg/network-controller-manager/node_network_controller_manager.go b/go-controller/pkg/network-controller-manager/node_network_controller_manager.go
index 6547886686a..30161bb6214 100644
--- a/go-controller/pkg/network-controller-manager/node_network_controller_manager.go
+++ b/go-controller/pkg/network-controller-manager/node_network_controller_manager.go
@@ -127,7 +127,7 @@ func NewNodeNetworkControllerManager(ovnClient *util.OVNClientset, wf factory.No
 	// need to start NAD controller on node side for programming gateway pieces for UDNs
 	var err error
 	if isNodeNADControllerRequired() {
-		ncm.nadController, err = nad.NewNetAttachDefinitionController("node-network-controller-manager", ncm, wf)
+		ncm.nadController, err = nad.NewNetAttachDefinitionController("node-network-controller-manager", ncm, wf, nil)
 	}
 	if util.IsNetworkSegmentationSupportEnabled() {
 		ncm.vrfManager = vrfmanager.NewController(ncm.routeManager)

From f0c876f08bcde15662f911b096d78201214be2e5 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Tue, 6 Aug 2024 23:04:34 +0200
Subject: [PATCH 03/48] Print errors on node annotation failure.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 go-controller/pkg/clustermanager/node/node_allocator.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/go-controller/pkg/clustermanager/node/node_allocator.go b/go-controller/pkg/clustermanager/node/node_allocator.go
index c1277c59fae..bf860f38407 100644
--- a/go-controller/pkg/clustermanager/node/node_allocator.go
+++ b/go-controller/pkg/clustermanager/node/node_allocator.go
@@ -389,7 +389,7 @@ func (na *NodeAllocator) updateNodeNetworkAnnotationsWithRetry(nodeName string,
 		return na.kube.UpdateNodeStatus(cnode)
 	})
 	if resultErr != nil {
-		return fmt.Errorf("failed to update node %s annotation", nodeName)
+		return fmt.Errorf("failed to update node %s annotation: %w", nodeName, resultErr)
 	}
 	return nil
 }

From 46668987f7544bcaeb49c3c28d5d06c9986a1c40 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Fri, 2 Aug 2024 12:36:03 +0200
Subject: [PATCH 04/48] Bump ovn dbs to ovn-org/ovn/tree/branch-24.09.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 go-controller/pkg/libovsdb/ops/model.go       |  33 +++
 go-controller/pkg/nbdb/acl.go                 |  50 ++++
 go-controller/pkg/nbdb/dhcp_relay.go          | 145 ++++++++++++
 go-controller/pkg/nbdb/logical_router_port.go |  25 ++
 go-controller/pkg/nbdb/model.go               | 221 +++++++++++++++++-
 go-controller/pkg/nbdb/nat.go                 |  12 +
 go-controller/pkg/nbdb/sample.go              |  85 +++++++
 go-controller/pkg/nbdb/sample_collector.go    | 105 +++++++++
 go-controller/pkg/nbdb/sampling_app.go        | 103 ++++++++
 go-controller/pkg/sbdb/igmp_group.go          |   8 +-
 go-controller/pkg/sbdb/logical_flow.go        |  25 ++
 go-controller/pkg/sbdb/model.go               |  14 +-
 12 files changed, 823 insertions(+), 3 deletions(-)
 create mode 100644 go-controller/pkg/nbdb/dhcp_relay.go
 create mode 100644 go-controller/pkg/nbdb/sample.go
 create mode 100644 go-controller/pkg/nbdb/sample_collector.go
 create mode 100644 go-controller/pkg/nbdb/sampling_app.go

diff --git a/go-controller/pkg/libovsdb/ops/model.go b/go-controller/pkg/libovsdb/ops/model.go
index b52f958133e..b40dd4104de 100644
--- a/go-controller/pkg/libovsdb/ops/model.go
+++ b/go-controller/pkg/libovsdb/ops/model.go
@@ -50,6 +50,12 @@ func getUUID(model model.Model) string {
 		return t.UUID
 	case *nbdb.Meter:
 		return t.UUID
+	case *nbdb.Sample:
+		return t.UUID
+	case *nbdb.SampleCollector:
+		return t.UUID
+	case *nbdb.SamplingApp:
+		return t.UUID
 	case *nbdb.StaticMACBinding:
 		return t.UUID
 	case *sbdb.Chassis:
@@ -113,6 +119,12 @@ func setUUID(model model.Model, uuid string) {
 		t.UUID = uuid
 	case *nbdb.Meter:
 		t.UUID = uuid
+	case *nbdb.Sample:
+		t.UUID = uuid
+	case *nbdb.SampleCollector:
+		t.UUID = uuid
+	case *nbdb.SamplingApp:
+		t.UUID = uuid
 	case *nbdb.StaticMACBinding:
 		t.UUID = uuid
 	case *sbdb.Chassis:
@@ -229,6 +241,21 @@ func copyIndexes(model model.Model) model.Model {
 			UUID: t.UUID,
 			Name: t.Name,
 		}
+	case *nbdb.Sample:
+		return &nbdb.Sample{
+			UUID:     t.UUID,
+			Metadata: t.Metadata,
+		}
+	case *nbdb.SampleCollector:
+		return &nbdb.SampleCollector{
+			UUID: t.UUID,
+			ID:   t.ID,
+		}
+	case *nbdb.SamplingApp:
+		return &nbdb.SamplingApp{
+			UUID: t.UUID,
+			Type: t.Type,
+		}
 	case *nbdb.StaticMACBinding:
 		return &nbdb.StaticMACBinding{
 			UUID:        t.UUID,
@@ -327,6 +354,12 @@ func getListFromModel(model model.Model) interface{} {
 		return &[]*nbdb.MeterBand{}
 	case *nbdb.Meter:
 		return &[]*nbdb.Meter{}
+	case *nbdb.Sample:
+		return &[]*nbdb.Sample{}
+	case *nbdb.SampleCollector:
+		return &[]*nbdb.SampleCollector{}
+	case *nbdb.SamplingApp:
+		return &[]*nbdb.SamplingApp{}
 	case *nbdb.StaticMACBinding:
 		return &[]*nbdb.StaticMACBinding{}
 	case *sbdb.Chassis:
diff --git a/go-controller/pkg/nbdb/acl.go b/go-controller/pkg/nbdb/acl.go
index 2cfde033cbc..0c2840c1788 100644
--- a/go-controller/pkg/nbdb/acl.go
+++ b/go-controller/pkg/nbdb/acl.go
@@ -42,6 +42,8 @@ type ACL struct {
 	Name        *string           `ovsdb:"name"`
 	Options     map[string]string `ovsdb:"options"`
 	Priority    int               `ovsdb:"priority"`
+	SampleEst   *string           `ovsdb:"sample_est"`
+	SampleNew   *string           `ovsdb:"sample_new"`
 	Severity    *ACLSeverity      `ovsdb:"severity"`
 	Tier        int               `ovsdb:"tier"`
 }
@@ -178,6 +180,50 @@ func (a *ACL) GetPriority() int {
 	return a.Priority
 }
 
+func (a *ACL) GetSampleEst() *string {
+	return a.SampleEst
+}
+
+func copyACLSampleEst(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalACLSampleEst(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *ACL) GetSampleNew() *string {
+	return a.SampleNew
+}
+
+func copyACLSampleNew(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalACLSampleNew(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
 func (a *ACL) GetSeverity() *ACLSeverity {
 	return a.Severity
 }
@@ -210,6 +256,8 @@ func (a *ACL) DeepCopyInto(b *ACL) {
 	b.Meter = copyACLMeter(a.Meter)
 	b.Name = copyACLName(a.Name)
 	b.Options = copyACLOptions(a.Options)
+	b.SampleEst = copyACLSampleEst(a.SampleEst)
+	b.SampleNew = copyACLSampleNew(a.SampleNew)
 	b.Severity = copyACLSeverity(a.Severity)
 }
 
@@ -240,6 +288,8 @@ func (a *ACL) Equals(b *ACL) bool {
 		equalACLName(a.Name, b.Name) &&
 		equalACLOptions(a.Options, b.Options) &&
 		a.Priority == b.Priority &&
+		equalACLSampleEst(a.SampleEst, b.SampleEst) &&
+		equalACLSampleNew(a.SampleNew, b.SampleNew) &&
 		equalACLSeverity(a.Severity, b.Severity) &&
 		a.Tier == b.Tier
 }
diff --git a/go-controller/pkg/nbdb/dhcp_relay.go b/go-controller/pkg/nbdb/dhcp_relay.go
new file mode 100644
index 00000000000..f0e973ab785
--- /dev/null
+++ b/go-controller/pkg/nbdb/dhcp_relay.go
@@ -0,0 +1,145 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package nbdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const DHCPRelayTable = "DHCP_Relay"
+
+// DHCPRelay defines an object in DHCP_Relay table
+type DHCPRelay struct {
+	UUID        string            `ovsdb:"_uuid"`
+	ExternalIDs map[string]string `ovsdb:"external_ids"`
+	Name        string            `ovsdb:"name"`
+	Options     map[string]string `ovsdb:"options"`
+	Servers     *string           `ovsdb:"servers"`
+}
+
+func (a *DHCPRelay) GetUUID() string {
+	return a.UUID
+}
+
+func (a *DHCPRelay) GetExternalIDs() map[string]string {
+	return a.ExternalIDs
+}
+
+func copyDHCPRelayExternalIDs(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalDHCPRelayExternalIDs(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *DHCPRelay) GetName() string {
+	return a.Name
+}
+
+func (a *DHCPRelay) GetOptions() map[string]string {
+	return a.Options
+}
+
+func copyDHCPRelayOptions(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalDHCPRelayOptions(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *DHCPRelay) GetServers() *string {
+	return a.Servers
+}
+
+func copyDHCPRelayServers(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalDHCPRelayServers(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *DHCPRelay) DeepCopyInto(b *DHCPRelay) {
+	*b = *a
+	b.ExternalIDs = copyDHCPRelayExternalIDs(a.ExternalIDs)
+	b.Options = copyDHCPRelayOptions(a.Options)
+	b.Servers = copyDHCPRelayServers(a.Servers)
+}
+
+func (a *DHCPRelay) DeepCopy() *DHCPRelay {
+	b := new(DHCPRelay)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *DHCPRelay) CloneModelInto(b model.Model) {
+	c := b.(*DHCPRelay)
+	a.DeepCopyInto(c)
+}
+
+func (a *DHCPRelay) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *DHCPRelay) Equals(b *DHCPRelay) bool {
+	return a.UUID == b.UUID &&
+		equalDHCPRelayExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		a.Name == b.Name &&
+		equalDHCPRelayOptions(a.Options, b.Options) &&
+		equalDHCPRelayServers(a.Servers, b.Servers)
+}
+
+func (a *DHCPRelay) EqualsModel(b model.Model) bool {
+	c := b.(*DHCPRelay)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &DHCPRelay{}
+var _ model.ComparableModel = &DHCPRelay{}
diff --git a/go-controller/pkg/nbdb/logical_router_port.go b/go-controller/pkg/nbdb/logical_router_port.go
index dbe4ea8708f..d39fe0db423 100644
--- a/go-controller/pkg/nbdb/logical_router_port.go
+++ b/go-controller/pkg/nbdb/logical_router_port.go
@@ -10,6 +10,7 @@ const LogicalRouterPortTable = "Logical_Router_Port"
 // LogicalRouterPort defines an object in Logical_Router_Port table
 type LogicalRouterPort struct {
 	UUID           string            `ovsdb:"_uuid"`
+	DhcpRelay      *string           `ovsdb:"dhcp_relay"`
 	Enabled        *bool             `ovsdb:"enabled"`
 	ExternalIDs    map[string]string `ovsdb:"external_ids"`
 	GatewayChassis []string          `ovsdb:"gateway_chassis"`
@@ -28,6 +29,28 @@ func (a *LogicalRouterPort) GetUUID() string {
 	return a.UUID
 }
 
+func (a *LogicalRouterPort) GetDhcpRelay() *string {
+	return a.DhcpRelay
+}
+
+func copyLogicalRouterPortDhcpRelay(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalLogicalRouterPortDhcpRelay(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
 func (a *LogicalRouterPort) GetEnabled() *bool {
 	return a.Enabled
 }
@@ -308,6 +331,7 @@ func equalLogicalRouterPortStatus(a, b map[string]string) bool {
 
 func (a *LogicalRouterPort) DeepCopyInto(b *LogicalRouterPort) {
 	*b = *a
+	b.DhcpRelay = copyLogicalRouterPortDhcpRelay(a.DhcpRelay)
 	b.Enabled = copyLogicalRouterPortEnabled(a.Enabled)
 	b.ExternalIDs = copyLogicalRouterPortExternalIDs(a.ExternalIDs)
 	b.GatewayChassis = copyLogicalRouterPortGatewayChassis(a.GatewayChassis)
@@ -337,6 +361,7 @@ func (a *LogicalRouterPort) CloneModel() model.Model {
 
 func (a *LogicalRouterPort) Equals(b *LogicalRouterPort) bool {
 	return a.UUID == b.UUID &&
+		equalLogicalRouterPortDhcpRelay(a.DhcpRelay, b.DhcpRelay) &&
 		equalLogicalRouterPortEnabled(a.Enabled, b.Enabled) &&
 		equalLogicalRouterPortExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
 		equalLogicalRouterPortGatewayChassis(a.GatewayChassis, b.GatewayChassis) &&
diff --git a/go-controller/pkg/nbdb/model.go b/go-controller/pkg/nbdb/model.go
index d6809ef08a5..daabac4530b 100644
--- a/go-controller/pkg/nbdb/model.go
+++ b/go-controller/pkg/nbdb/model.go
@@ -20,6 +20,7 @@ func FullDatabaseModel() (model.ClientDBModel, error) {
 		"Connection":                  &Connection{},
 		"Copp":                        &Copp{},
 		"DHCP_Options":                &DHCPOptions{},
+		"DHCP_Relay":                  &DHCPRelay{},
 		"DNS":                         &DNS{},
 		"Forwarding_Group":            &ForwardingGroup{},
 		"Gateway_Chassis":             &GatewayChassis{},
@@ -42,13 +43,16 @@ func FullDatabaseModel() (model.ClientDBModel, error) {
 		"Port_Group":                  &PortGroup{},
 		"QoS":                         &QoS{},
 		"SSL":                         &SSL{},
+		"Sample":                      &Sample{},
+		"Sample_Collector":            &SampleCollector{},
+		"Sampling_App":                &SamplingApp{},
 		"Static_MAC_Binding":          &StaticMACBinding{},
 	})
 }
 
 var schema = `{
   "name": "OVN_Northbound",
-  "version": "7.3.0",
+  "version": "7.6.0",
   "tables": {
     "ACL": {
       "columns": {
@@ -152,6 +156,28 @@ var schema = `{
             }
           }
         },
+        "sample_est": {
+          "type": {
+            "key": {
+              "type": "uuid",
+              "refTable": "Sample",
+              "refType": "strong"
+            },
+            "min": 0,
+            "max": 1
+          }
+        },
+        "sample_new": {
+          "type": {
+            "key": {
+              "type": "uuid",
+              "refTable": "Sample",
+              "refType": "strong"
+            },
+            "min": 0,
+            "max": 1
+          }
+        },
         "severity": {
           "type": {
             "key": {
@@ -482,6 +508,47 @@ var schema = `{
       },
       "isRoot": true
     },
+    "DHCP_Relay": {
+      "columns": {
+        "external_ids": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "value": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": "unlimited"
+          }
+        },
+        "name": {
+          "type": "string"
+        },
+        "options": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "value": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": "unlimited"
+          }
+        },
+        "servers": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": 1
+          }
+        }
+      },
+      "isRoot": true
+    },
     "DNS": {
       "columns": {
         "external_ids": {
@@ -1034,6 +1101,17 @@ var schema = `{
     },
     "Logical_Router_Port": {
       "columns": {
+        "dhcp_relay": {
+          "type": {
+            "key": {
+              "type": "uuid",
+              "refTable": "DHCP_Relay",
+              "refType": "strong"
+            },
+            "min": 0,
+            "max": 1
+          }
+        },
         "enabled": {
           "type": {
             "key": {
@@ -1740,6 +1818,9 @@ var schema = `{
             "max": 1
           }
         },
+        "match": {
+          "type": "string"
+        },
         "options": {
           "type": {
             "key": {
@@ -1752,6 +1833,15 @@ var schema = `{
             "max": "unlimited"
           }
         },
+        "priority": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 0,
+              "maxInteger": 32767
+            }
+          }
+        },
         "type": {
           "type": {
             "key": {
@@ -2007,6 +2097,135 @@ var schema = `{
         }
       }
     },
+    "Sample": {
+      "columns": {
+        "collectors": {
+          "type": {
+            "key": {
+              "type": "uuid",
+              "refTable": "Sample_Collector",
+              "refType": "strong"
+            },
+            "min": 0,
+            "max": "unlimited"
+          }
+        },
+        "metadata": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 1,
+              "maxInteger": 4294967295
+            },
+            "min": 1,
+            "max": 1
+          }
+        }
+      },
+      "indexes": [
+        [
+          "metadata"
+        ]
+      ]
+    },
+    "Sample_Collector": {
+      "columns": {
+        "external_ids": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "value": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": "unlimited"
+          }
+        },
+        "id": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 1,
+              "maxInteger": 255
+            }
+          }
+        },
+        "name": {
+          "type": "string"
+        },
+        "probability": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 0,
+              "maxInteger": 65535
+            }
+          }
+        },
+        "set_id": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 1,
+              "maxInteger": 4294967295
+            }
+          }
+        }
+      },
+      "indexes": [
+        [
+          "id"
+        ]
+      ],
+      "isRoot": true
+    },
+    "Sampling_App": {
+      "columns": {
+        "external_ids": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "value": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": "unlimited"
+          }
+        },
+        "id": {
+          "type": {
+            "key": {
+              "type": "integer",
+              "minInteger": 1,
+              "maxInteger": 255
+            }
+          }
+        },
+        "type": {
+          "type": {
+            "key": {
+              "type": "string",
+              "enum": [
+                "set",
+                [
+                  "drop",
+                  "acl-new",
+                  "acl-est"
+                ]
+              ]
+            }
+          }
+        }
+      },
+      "indexes": [
+        [
+          "type"
+        ]
+      ],
+      "isRoot": true
+    },
     "Static_MAC_Binding": {
       "columns": {
         "ip": {
diff --git a/go-controller/pkg/nbdb/nat.go b/go-controller/pkg/nbdb/nat.go
index 3286bbde2c4..4bd1b7ed492 100644
--- a/go-controller/pkg/nbdb/nat.go
+++ b/go-controller/pkg/nbdb/nat.go
@@ -29,7 +29,9 @@ type NAT struct {
 	GatewayPort       *string           `ovsdb:"gateway_port"`
 	LogicalIP         string            `ovsdb:"logical_ip"`
 	LogicalPort       *string           `ovsdb:"logical_port"`
+	Match             string            `ovsdb:"match"`
 	Options           map[string]string `ovsdb:"options"`
+	Priority          int               `ovsdb:"priority"`
 	Type              NATType           `ovsdb:"type"`
 }
 
@@ -189,6 +191,10 @@ func equalNATLogicalPort(a, b *string) bool {
 	return *a == *b
 }
 
+func (a *NAT) GetMatch() string {
+	return a.Match
+}
+
 func (a *NAT) GetOptions() map[string]string {
 	return a.Options
 }
@@ -219,6 +225,10 @@ func equalNATOptions(a, b map[string]string) bool {
 	return true
 }
 
+func (a *NAT) GetPriority() int {
+	return a.Priority
+}
+
 func (a *NAT) GetType() NATType {
 	return a.Type
 }
@@ -260,7 +270,9 @@ func (a *NAT) Equals(b *NAT) bool {
 		equalNATGatewayPort(a.GatewayPort, b.GatewayPort) &&
 		a.LogicalIP == b.LogicalIP &&
 		equalNATLogicalPort(a.LogicalPort, b.LogicalPort) &&
+		a.Match == b.Match &&
 		equalNATOptions(a.Options, b.Options) &&
+		a.Priority == b.Priority &&
 		a.Type == b.Type
 }
 
diff --git a/go-controller/pkg/nbdb/sample.go b/go-controller/pkg/nbdb/sample.go
new file mode 100644
index 00000000000..639393a1e66
--- /dev/null
+++ b/go-controller/pkg/nbdb/sample.go
@@ -0,0 +1,85 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package nbdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const SampleTable = "Sample"
+
+// Sample defines an object in Sample table
+type Sample struct {
+	UUID       string   `ovsdb:"_uuid"`
+	Collectors []string `ovsdb:"collectors"`
+	Metadata   int      `ovsdb:"metadata"`
+}
+
+func (a *Sample) GetUUID() string {
+	return a.UUID
+}
+
+func (a *Sample) GetCollectors() []string {
+	return a.Collectors
+}
+
+func copySampleCollectors(a []string) []string {
+	if a == nil {
+		return nil
+	}
+	b := make([]string, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalSampleCollectors(a, b []string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Sample) GetMetadata() int {
+	return a.Metadata
+}
+
+func (a *Sample) DeepCopyInto(b *Sample) {
+	*b = *a
+	b.Collectors = copySampleCollectors(a.Collectors)
+}
+
+func (a *Sample) DeepCopy() *Sample {
+	b := new(Sample)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *Sample) CloneModelInto(b model.Model) {
+	c := b.(*Sample)
+	a.DeepCopyInto(c)
+}
+
+func (a *Sample) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *Sample) Equals(b *Sample) bool {
+	return a.UUID == b.UUID &&
+		equalSampleCollectors(a.Collectors, b.Collectors) &&
+		a.Metadata == b.Metadata
+}
+
+func (a *Sample) EqualsModel(b model.Model) bool {
+	c := b.(*Sample)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &Sample{}
+var _ model.ComparableModel = &Sample{}
diff --git a/go-controller/pkg/nbdb/sample_collector.go b/go-controller/pkg/nbdb/sample_collector.go
new file mode 100644
index 00000000000..50f0659040e
--- /dev/null
+++ b/go-controller/pkg/nbdb/sample_collector.go
@@ -0,0 +1,105 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package nbdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const SampleCollectorTable = "Sample_Collector"
+
+// SampleCollector defines an object in Sample_Collector table
+type SampleCollector struct {
+	UUID        string            `ovsdb:"_uuid"`
+	ExternalIDs map[string]string `ovsdb:"external_ids"`
+	ID          int               `ovsdb:"id"`
+	Name        string            `ovsdb:"name"`
+	Probability int               `ovsdb:"probability"`
+	SetID       int               `ovsdb:"set_id"`
+}
+
+func (a *SampleCollector) GetUUID() string {
+	return a.UUID
+}
+
+func (a *SampleCollector) GetExternalIDs() map[string]string {
+	return a.ExternalIDs
+}
+
+func copySampleCollectorExternalIDs(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalSampleCollectorExternalIDs(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *SampleCollector) GetID() int {
+	return a.ID
+}
+
+func (a *SampleCollector) GetName() string {
+	return a.Name
+}
+
+func (a *SampleCollector) GetProbability() int {
+	return a.Probability
+}
+
+func (a *SampleCollector) GetSetID() int {
+	return a.SetID
+}
+
+func (a *SampleCollector) DeepCopyInto(b *SampleCollector) {
+	*b = *a
+	b.ExternalIDs = copySampleCollectorExternalIDs(a.ExternalIDs)
+}
+
+func (a *SampleCollector) DeepCopy() *SampleCollector {
+	b := new(SampleCollector)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *SampleCollector) CloneModelInto(b model.Model) {
+	c := b.(*SampleCollector)
+	a.DeepCopyInto(c)
+}
+
+func (a *SampleCollector) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *SampleCollector) Equals(b *SampleCollector) bool {
+	return a.UUID == b.UUID &&
+		equalSampleCollectorExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		a.ID == b.ID &&
+		a.Name == b.Name &&
+		a.Probability == b.Probability &&
+		a.SetID == b.SetID
+}
+
+func (a *SampleCollector) EqualsModel(b model.Model) bool {
+	c := b.(*SampleCollector)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &SampleCollector{}
+var _ model.ComparableModel = &SampleCollector{}
diff --git a/go-controller/pkg/nbdb/sampling_app.go b/go-controller/pkg/nbdb/sampling_app.go
new file mode 100644
index 00000000000..a152b4237d0
--- /dev/null
+++ b/go-controller/pkg/nbdb/sampling_app.go
@@ -0,0 +1,103 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package nbdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const SamplingAppTable = "Sampling_App"
+
+type (
+	SamplingAppType = string
+)
+
+var (
+	SamplingAppTypeDrop   SamplingAppType = "drop"
+	SamplingAppTypeACLNew SamplingAppType = "acl-new"
+	SamplingAppTypeACLEst SamplingAppType = "acl-est"
+)
+
+// SamplingApp defines an object in Sampling_App table
+type SamplingApp struct {
+	UUID        string            `ovsdb:"_uuid"`
+	ExternalIDs map[string]string `ovsdb:"external_ids"`
+	ID          int               `ovsdb:"id"`
+	Type        SamplingAppType   `ovsdb:"type"`
+}
+
+func (a *SamplingApp) GetUUID() string {
+	return a.UUID
+}
+
+func (a *SamplingApp) GetExternalIDs() map[string]string {
+	return a.ExternalIDs
+}
+
+func copySamplingAppExternalIDs(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalSamplingAppExternalIDs(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *SamplingApp) GetID() int {
+	return a.ID
+}
+
+func (a *SamplingApp) GetType() SamplingAppType {
+	return a.Type
+}
+
+func (a *SamplingApp) DeepCopyInto(b *SamplingApp) {
+	*b = *a
+	b.ExternalIDs = copySamplingAppExternalIDs(a.ExternalIDs)
+}
+
+func (a *SamplingApp) DeepCopy() *SamplingApp {
+	b := new(SamplingApp)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *SamplingApp) CloneModelInto(b model.Model) {
+	c := b.(*SamplingApp)
+	a.DeepCopyInto(c)
+}
+
+func (a *SamplingApp) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *SamplingApp) Equals(b *SamplingApp) bool {
+	return a.UUID == b.UUID &&
+		equalSamplingAppExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		a.ID == b.ID &&
+		a.Type == b.Type
+}
+
+func (a *SamplingApp) EqualsModel(b model.Model) bool {
+	c := b.(*SamplingApp)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &SamplingApp{}
+var _ model.ComparableModel = &SamplingApp{}
diff --git a/go-controller/pkg/sbdb/igmp_group.go b/go-controller/pkg/sbdb/igmp_group.go
index a8576a4834c..73a0bb9437b 100644
--- a/go-controller/pkg/sbdb/igmp_group.go
+++ b/go-controller/pkg/sbdb/igmp_group.go
@@ -15,6 +15,7 @@ type IGMPGroup struct {
 	ChassisName string   `ovsdb:"chassis_name"`
 	Datapath    *string  `ovsdb:"datapath"`
 	Ports       []string `ovsdb:"ports"`
+	Protocol    string   `ovsdb:"protocol"`
 }
 
 func (a *IGMPGroup) GetUUID() string {
@@ -101,6 +102,10 @@ func equalIGMPGroupPorts(a, b []string) bool {
 	return true
 }
 
+func (a *IGMPGroup) GetProtocol() string {
+	return a.Protocol
+}
+
 func (a *IGMPGroup) DeepCopyInto(b *IGMPGroup) {
 	*b = *a
 	b.Chassis = copyIGMPGroupChassis(a.Chassis)
@@ -129,7 +134,8 @@ func (a *IGMPGroup) Equals(b *IGMPGroup) bool {
 		equalIGMPGroupChassis(a.Chassis, b.Chassis) &&
 		a.ChassisName == b.ChassisName &&
 		equalIGMPGroupDatapath(a.Datapath, b.Datapath) &&
-		equalIGMPGroupPorts(a.Ports, b.Ports)
+		equalIGMPGroupPorts(a.Ports, b.Ports) &&
+		a.Protocol == b.Protocol
 }
 
 func (a *IGMPGroup) EqualsModel(b model.Model) bool {
diff --git a/go-controller/pkg/sbdb/logical_flow.go b/go-controller/pkg/sbdb/logical_flow.go
index 26fbda1b513..42af1cdf544 100644
--- a/go-controller/pkg/sbdb/logical_flow.go
+++ b/go-controller/pkg/sbdb/logical_flow.go
@@ -22,6 +22,7 @@ type LogicalFlow struct {
 	Actions         string              `ovsdb:"actions"`
 	ControllerMeter *string             `ovsdb:"controller_meter"`
 	ExternalIDs     map[string]string   `ovsdb:"external_ids"`
+	FlowDesc        *string             `ovsdb:"flow_desc"`
 	LogicalDatapath *string             `ovsdb:"logical_datapath"`
 	LogicalDpGroup  *string             `ovsdb:"logical_dp_group"`
 	Match           string              `ovsdb:"match"`
@@ -91,6 +92,28 @@ func equalLogicalFlowExternalIDs(a, b map[string]string) bool {
 	return true
 }
 
+func (a *LogicalFlow) GetFlowDesc() *string {
+	return a.FlowDesc
+}
+
+func copyLogicalFlowFlowDesc(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalLogicalFlowFlowDesc(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
 func (a *LogicalFlow) GetLogicalDatapath() *string {
 	return a.LogicalDatapath
 }
@@ -185,6 +208,7 @@ func (a *LogicalFlow) DeepCopyInto(b *LogicalFlow) {
 	*b = *a
 	b.ControllerMeter = copyLogicalFlowControllerMeter(a.ControllerMeter)
 	b.ExternalIDs = copyLogicalFlowExternalIDs(a.ExternalIDs)
+	b.FlowDesc = copyLogicalFlowFlowDesc(a.FlowDesc)
 	b.LogicalDatapath = copyLogicalFlowLogicalDatapath(a.LogicalDatapath)
 	b.LogicalDpGroup = copyLogicalFlowLogicalDpGroup(a.LogicalDpGroup)
 	b.Tags = copyLogicalFlowTags(a.Tags)
@@ -210,6 +234,7 @@ func (a *LogicalFlow) Equals(b *LogicalFlow) bool {
 		a.Actions == b.Actions &&
 		equalLogicalFlowControllerMeter(a.ControllerMeter, b.ControllerMeter) &&
 		equalLogicalFlowExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		equalLogicalFlowFlowDesc(a.FlowDesc, b.FlowDesc) &&
 		equalLogicalFlowLogicalDatapath(a.LogicalDatapath, b.LogicalDatapath) &&
 		equalLogicalFlowLogicalDpGroup(a.LogicalDpGroup, b.LogicalDpGroup) &&
 		a.Match == b.Match &&
diff --git a/go-controller/pkg/sbdb/model.go b/go-controller/pkg/sbdb/model.go
index 109819f75ca..bc838fe4975 100644
--- a/go-controller/pkg/sbdb/model.go
+++ b/go-controller/pkg/sbdb/model.go
@@ -52,7 +52,7 @@ func FullDatabaseModel() (model.ClientDBModel, error) {
 
 var schema = `{
   "name": "OVN_Southbound",
-  "version": "20.33.0",
+  "version": "20.37.0",
   "tables": {
     "Address_Set": {
       "columns": {
@@ -833,6 +833,9 @@ var schema = `{
             "min": 0,
             "max": "unlimited"
           }
+        },
+        "protocol": {
+          "type": "string"
         }
       },
       "indexes": [
@@ -1071,6 +1074,15 @@ var schema = `{
             "max": "unlimited"
           }
         },
+        "flow_desc": {
+          "type": {
+            "key": {
+              "type": "string"
+            },
+            "min": 0,
+            "max": 1
+          }
+        },
         "logical_datapath": {
           "type": {
             "key": {

From d431a61499428f1ce66852a22de93e60cfbd7246 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Sun, 4 Aug 2024 12:22:58 +0200
Subject: [PATCH 05/48] Add config options to enable observability.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 contrib/kind-helm.sh                          |  8 ++++++-
 contrib/kind.sh                               | 13 ++++++++--
 dist/images/daemonset.sh                      | 13 ++++++++++
 dist/images/ovnkube.sh                        | 24 +++++++++++++++++++
 .../ovnkube-single-node-zone.yaml.j2          |  2 ++
 .../templates/ovnkube-zone-controller.yaml.j2 |  2 ++
 go-controller/pkg/config/config.go            |  7 ++++++
 .../templates/ovnkube-single-node-zone.yaml   |  2 ++
 .../templates/ovnkube-zone-controller.yaml    |  2 ++
 9 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/contrib/kind-helm.sh b/contrib/kind-helm.sh
index 196ac37c31d..a3690941751 100755
--- a/contrib/kind-helm.sh
+++ b/contrib/kind-helm.sh
@@ -21,6 +21,7 @@ set_default_params() {
   export OVN_HA=${OVN_HA:-false}
   export OVN_MULTICAST_ENABLE=${OVN_MULTICAST_ENABLE:-false}
   export OVN_HYBRID_OVERLAY_ENABLE=${OVN_HYBRID_OVERLAY_ENABLE:-false}
+  export OVN_OBSERV_ENABLE=${OVN_OBSERV_ENABLE:-false}
   export OVN_EMPTY_LB_EVENTS=${OVN_EMPTY_LB_EVENTS:-false}
   export KIND_REMOVE_TAINT=${KIND_REMOVE_TAINT:-true}
   export ENABLE_MULTI_NET=${ENABLE_MULTI_NET:-false}
@@ -106,6 +107,7 @@ usage() {
     echo "                                    DEFAULT: Remove taint components"
     echo "-me  | --multicast-enabled          Enable multicast. DEFAULT: Disabled"
     echo "-ho  | --hybrid-enabled             Enable hybrid overlay. DEFAULT: Disabled"
+    echo "-obs | --observability              Enable observability. DEFAULT: Disabled"
     echo "-el  | --ovn-empty-lb-events        Enable empty-lb-events generation for LB without backends. DEFAULT: Disabled"
     echo "-ii  | --install-ingress            Flag to install Ingress Components."
     echo "                                    DEFAULT: Don't install ingress components."
@@ -143,6 +145,8 @@ parse_args() {
                                                 ;;
             -ho | --hybrid-enabled )            OVN_HYBRID_OVERLAY_ENABLE=true
                                                 ;;
+            -obs | --observability )            OVN_OBSERV_ENABLE=true
+                                                ;;
             -el | --ovn-empty-lb-events )       OVN_EMPTY_LB_EVENTS=true
                                                 ;;
             -ii | --install-ingress )           KIND_INSTALL_INGRESS=true
@@ -202,6 +206,7 @@ print_params() {
      echo "OVN_HA = $OVN_HA"
      echo "OVN_MULTICAST_ENABLE = $OVN_MULTICAST_ENABLE"
      echo "OVN_HYBRID_OVERLAY_ENABLE = $OVN_HYBRID_OVERLAY_ENABLE"
+     echo "OVN_OBSERV_ENABLE = $OVN_OBSERV_ENABLE"
      echo "OVN_EMPTY_LB_EVENTS = $OVN_EMPTY_LB_EVENTS"
      echo "KIND_CLUSTER_NAME = $KIND_CLUSTER_NAME"
      echo "KIND_REMOVE_TAINT = $KIND_REMOVE_TAINT"
@@ -398,7 +403,8 @@ create_ovn_kubernetes() {
           --set global.enableMulticast=$(if [ "${OVN_MULTICAST_ENABLE}" == "true" ]; then echo "true"; else echo "false"; fi) \
           --set global.enableMultiNetwork=$(if [ "${ENABLE_MULTI_NET}" == "true" ]; then echo "true"; else echo "false"; fi) \
           --set global.enableHybridOverlay=$(if [ "${OVN_HYBRID_OVERLAY_ENABLE}" == "true" ]; then echo "true"; else echo "false"; fi) \
-          --set global.emptyLbEvents=$(if [ "${OVN_EMPTY_LB_EVENTS}" == "true" ]; then echo "true"; else echo "false"; fi) \
+          --set global.enableObservability=$(if [ "${OVN_OBSERV_ENABLE}" == "true" ]; then echo "true"; else echo "false"; fi) \
+        --set global.emptyLbEvents=$(if [ "${OVN_EMPTY_LB_EVENTS}" == "true" ]; then echo "true"; else echo "false"; fi) \
           --set global.enableDNSNameResolver=$(if [ "${OVN_ENABLE_DNSNAMERESOLVER}" == "true" ]; then echo "true"; else echo "false"; fi) \
           ${ovnkube_db_options}
 }
diff --git a/contrib/kind.sh b/contrib/kind.sh
index 469e1191c53..5d2ca4cd267 100755
--- a/contrib/kind.sh
+++ b/contrib/kind.sh
@@ -79,6 +79,7 @@ usage() {
     echo "                 [-ic | --enable-interconnect]"
     echo "                 [--isolated]"
     echo "                 [-dns | --enable-dnsnameresolver]"
+    echo "                 [-obs | --observability]"
     echo "                 [-h]]"
     echo ""
     echo "-cf  | --config-file                Name of the KIND J2 configuration file."
@@ -141,6 +142,7 @@ usage() {
     echo "--deploy                            Deploy ovn kubernetes without restarting kind"
     echo "--add-nodes                         Adds nodes to an existing cluster. The number of nodes to be added is specified by --num-workers. Also use -ic if the cluster is using interconnect."
     echo "-dns | --enable-dnsnameresolver     Enable DNSNameResolver for resolving the DNS names used in the DNS rules of EgressFirewall."
+    echo "-obs | --observability              Enable OVN Observability feature."
     echo ""
 }
 
@@ -199,6 +201,8 @@ parse_args() {
             -ifa | --ipfix-cache-active-timeout ) shift
                                                 OVN_IPFIX_CACHE_ACTIVE_TIMEOUT=$1
                                                 ;;
+            -obs | --observability )            OVN_OBSERV_ENABLE=true
+                                                ;;
             -el | --ovn-empty-lb-events )       OVN_EMPTY_LB_EVENTS=true
                                                 ;;
             -kt | --keep-taint )                KIND_REMOVE_TAINT=false
@@ -334,7 +338,8 @@ parse_args() {
             -h | --help )                       usage
                                                 exit
                                                 ;;
-            * )                                 usage
+            * )                                 echo "Invalid option: $1"
+                                                usage
                                                 exit 1
         esac
         shift
@@ -377,6 +382,7 @@ print_params() {
      echo "OVN_IPFIX_SAMPLING = $OVN_IPFIX_SAMPLING"
      echo "OVN_IPFIX_CACHE_MAX_FLOWS = $OVN_IPFIX_CACHE_MAX_FLOWS"
      echo "OVN_IPFIX_CACHE_ACTIVE_TIMEOUT = $OVN_IPFIX_CACHE_ACTIVE_TIMEOUT"
+     echo "OVN_OBSERV_ENABLE = $OVN_OBSERV_ENABLE"
      echo "OVN_EMPTY_LB_EVENTS = $OVN_EMPTY_LB_EVENTS"
      echo "OVN_MULTICAST_ENABLE = $OVN_MULTICAST_ENABLE"
      echo "OVN_IMAGE = $OVN_IMAGE"
@@ -604,6 +610,7 @@ set_default_params() {
   fi
   OVN_MTU=${OVN_MTU:-1400}
   OVN_ENABLE_DNSNAMERESOLVER=${OVN_ENABLE_DNSNAMERESOLVER:-false}
+  OVN_OBSERV_ENABLE=${OVN_OBSERV_ENABLE:-false}
 }
 
 check_ipv6() {
@@ -851,7 +858,9 @@ create_ovn_kube_manifests() {
     --enable-ovnkube-identity="${OVN_ENABLE_OVNKUBE_IDENTITY}" \
     --enable-persistent-ips=true \
     --mtu="${OVN_MTU}" \
-    --enable-dnsnameresolver="${OVN_ENABLE_DNSNAMERESOLVER}"
+    --enable-dnsnameresolver="${OVN_ENABLE_DNSNAMERESOLVER}" \
+    --mtu="${OVN_MTU}" \
+    --enable-observ="${OVN_OBSERV_ENABLE}"
   popd
 }
 
diff --git a/dist/images/daemonset.sh b/dist/images/daemonset.sh
index c5ac11acc88..eb30a32c7c1 100755
--- a/dist/images/daemonset.sh
+++ b/dist/images/daemonset.sh
@@ -99,6 +99,7 @@ OVN_ENABLE_DNSNAMERESOLVER="false"
 IN_UPGRADE=
 # northd-backoff-interval, in ms
 OVN_NORTHD_BACKOFF_INTERVAL=
+OVN_OBSERV_ENABLE="false"
 
 # Parse parameters given as arguments to this script.
 while [ "$1" != "" ]; do
@@ -354,6 +355,9 @@ while [ "$1" != "" ]; do
   --enable-dnsnameresolver)
     OVN_ENABLE_DNSNAMERESOLVER=$VALUE
     ;;
+  --enable-observ)
+    OVN_OBSERV_ENABLE=$VALUE
+    ;;
   *)
     echo "WARNING: unknown parameter \"$PARAM\""
     exit 1
@@ -544,6 +548,9 @@ echo "ovn_enable_svc_template_support: ${ovn_enable_svc_template_support}"
 ovn_enable_dnsnameresolver=${OVN_ENABLE_DNSNAMERESOLVER}
 echo "ovn_enable_dnsnameresolver: ${ovn_enable_dnsnameresolver}"
 
+ovn_observ_enable=${OVN_OBSERV_ENABLE}
+echo "ovn_observ_enable: ${ovn_observ_enable}"
+
 ovn_image=${ovnkube_image} \
   ovnkube_compact_mode_enable=${ovnkube_compact_mode_enable} \
   ovn_image_pull_policy=${image_pull_policy} \
@@ -592,6 +599,7 @@ ovn_image=${ovnkube_image} \
   ovn_enable_interconnect=${ovn_enable_interconnect} \
   ovn_enable_multi_external_gateway=${ovn_enable_multi_external_gateway} \
   ovn_enable_ovnkube_identity=${ovn_enable_ovnkube_identity} \
+  ovn_observ_enable=${ovn_observ_enable} \
   ovnkube_app_name=ovnkube-node \
   jinjanate ../templates/ovnkube-node.yaml.j2 -o ${output_dir}/ovnkube-node.yaml
 
@@ -643,6 +651,7 @@ ovn_image=${ovnkube_image} \
   ovn_enable_interconnect=${ovn_enable_interconnect} \
   ovn_enable_multi_external_gateway=${ovn_enable_multi_external_gateway} \
   ovn_enable_ovnkube_identity=${ovn_enable_ovnkube_identity} \
+  ovn_observ_enable=${ovn_observ_enable} \
   ovnkube_app_name=ovnkube-node-dpu \
   jinjanate ../templates/ovnkube-node.yaml.j2 -o ${output_dir}/ovnkube-node-dpu.yaml
 
@@ -733,6 +742,7 @@ ovn_image=${ovnkube_image} \
   ovn_enable_persistent_ips=${ovn_enable_persistent_ips} \
   ovn_enable_svc_template_support=${ovn_enable_svc_template_support} \
   ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \
+  ovn_observ_enable=${ovn_observ_enable} \
   jinjanate ../templates/ovnkube-master.yaml.j2 -o ${output_dir}/ovnkube-master.yaml
 
 ovn_image=${ovnkube_image} \
@@ -774,6 +784,7 @@ ovn_image=${ovnkube_image} \
   ovn_v6_transit_switch_subnet=${ovn_v6_transit_switch_subnet} \
   ovn_enable_persistent_ips=${ovn_enable_persistent_ips} \
   ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \
+  ovn_observ_enable=${ovn_observ_enable} \
   jinjanate ../templates/ovnkube-control-plane.yaml.j2 -o ${output_dir}/ovnkube-control-plane.yaml
 
 ovn_image=${image} \
@@ -869,6 +880,7 @@ ovn_image=${ovnkube_image} \
   ovn_enable_persistent_ips=${ovn_enable_persistent_ips} \
   ovn_enable_svc_template_support=${ovn_enable_svc_template_support} \
   ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \
+  ovn_observ_enable=${ovn_observ_enable} \
   jinjanate ../templates/ovnkube-single-node-zone.yaml.j2 -o ${output_dir}/ovnkube-single-node-zone.yaml
 
 ovn_image=${ovnkube_image} \
@@ -932,6 +944,7 @@ ovn_image=${ovnkube_image} \
   ovn_enable_persistent_ips=${ovn_enable_persistent_ips} \
   ovn_enable_svc_template_support=${ovn_enable_svc_template_support} \
   ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \
+  ovn_observ_enable=${ovn_observ_enable} \
   jinjanate ../templates/ovnkube-zone-controller.yaml.j2 -o ${output_dir}/ovnkube-zone-controller.yaml
 
 ovn_image=${image} \
diff --git a/dist/images/ovnkube.sh b/dist/images/ovnkube.sh
index 265face90f5..33ed682a5ca 100755
--- a/dist/images/ovnkube.sh
+++ b/dist/images/ovnkube.sh
@@ -96,6 +96,7 @@ fi
 # OVN_NORTHD_BACKOFF_INTERVAL - ovn northd backoff interval in ms (default 300)
 # OVN_ENABLE_SVC_TEMPLATE_SUPPORT - enable svc template support
 # OVN_ENABLE_DNSNAMERESOLVER - enable dns name resolver support
+# OVN_OBSERV_ENABLE - enable observability for ovnkube
 
 # The argument to the command is the operation to be performed
 # ovn-master ovn-controller ovn-node display display_env ovn_debug
@@ -310,6 +311,8 @@ ovn_northd_backoff_interval=${OVN_NORTHD_BACKOFF_INTERVAL:-"300"}
 ovn_enable_svc_template_support=${OVN_ENABLE_SVC_TEMPLATE_SUPPORT:-true}
 # OVN_ENABLE_DNSNAMERESOLVER - enable dns name resolver support
 ovn_enable_dnsnameresolver=${OVN_ENABLE_DNSNAMERESOLVER:-false}
+# OVN_OBSERV_ENABLE - enable observability for ovnkube
+ovn_observ_enable=${OVN_OBSERV_ENABLE:-false}
 
 # Determine the ovn rundir.
 if [[ -f /usr/bin/ovn-appctl ]]; then
@@ -1259,6 +1262,12 @@ ovn-master() {
   fi
   echo "ovn_enable_svc_template_support_flag=${ovn_enable_svc_template_support_flag}"
 
+  ovn_observ_enable_flag=
+  if [[ ${ovn_observ_enable} == "true" ]]; then
+    ovn_observ_enable_flag="--enable-observability"
+  fi
+  echo "ovn_observ_enable_flag=${ovn_observ_enable_flag}"
+
   init_node_flags=
   if [[ ${ovnkube_compact_mode_enable} == "true" ]]; then
     init_node_flags="--init-node ${K8S_NODE} --nodeport"
@@ -1298,6 +1307,7 @@ ovn-master() {
     ${network_segmentation_enabled_flag} \
     ${ovn_acl_logging_rate_limit_flag} \
     ${ovn_enable_svc_template_support_flag} \
+    ${ovn_observ_enable_flag} \
     ${ovnkube_config_duration_enable_flag} \
     ${ovnkube_enable_multi_external_gateway_flag} \
     ${ovnkube_metrics_scale_enable_flag} \
@@ -1561,6 +1571,12 @@ ovnkube-controller() {
   fi
   echo "ovn_enable_dnsnameresolver_flag=${ovn_enable_dnsnameresolver_flag}"
 
+  ovn_observ_enable_flag=
+  if [[ ${ovn_observ_enable} == "true" ]]; then
+    ovn_observ_enable_flag="--enable-observability"
+  fi
+  echo "ovn_observ_enable_flag=${ovn_observ_enable_flag}"
+
   echo "=============== ovnkube-controller ========== MASTER ONLY"
   /usr/bin/ovnkube --init-ovnkube-controller ${K8S_NODE} \
     ${anp_enabled_flag} \
@@ -1579,6 +1595,7 @@ ovnkube-controller() {
     ${ovn_acl_logging_rate_limit_flag} \
     ${ovn_dbs} \
     ${ovn_enable_svc_template_support_flag} \
+    ${ovn_observ_enable_flag} \
     ${ovnkube_config_duration_enable_flag} \
     ${ovnkube_enable_interconnect_flag} \
     ${ovnkube_local_cert_flags} \
@@ -1960,6 +1977,12 @@ ovnkube-controller-with-node() {
   fi
   echo "ovn_enable_dnsnameresolver_flag=${ovn_enable_dnsnameresolver_flag}"
 
+  ovn_observ_enable_flag=
+  if [[ ${ovn_observ_enable} == "true" ]]; then
+    ovn_observ_enable_flag="--enable-observability"
+  fi
+  echo "ovn_observ_enable_flag=${ovn_observ_enable_flag}"
+
   echo "=============== ovnkube-controller-with-node --init-ovnkube-controller-with-node=========="
   /usr/bin/ovnkube --init-ovnkube-controller ${K8S_NODE} --init-node ${K8S_NODE} \
     ${anp_enabled_flag} \
@@ -1990,6 +2013,7 @@ ovnkube-controller-with-node() {
     ${ovn_acl_logging_rate_limit_flag} \
     ${ovn_dbs} \
     ${ovn_enable_svc_template_support_flag} \
+    ${ovn_observ_enable_flag} \
     ${ovn_encap_ip_flag} \
     ${ovn_encap_port_flag} \
     ${ovnkube_config_duration_enable_flag} \
diff --git a/dist/templates/ovnkube-single-node-zone.yaml.j2 b/dist/templates/ovnkube-single-node-zone.yaml.j2
index dd82cb48619..a7d03c6f9dc 100644
--- a/dist/templates/ovnkube-single-node-zone.yaml.j2
+++ b/dist/templates/ovnkube-single-node-zone.yaml.j2
@@ -446,6 +446,8 @@ spec:
           value: "local"
         - name: OVN_ENABLE_INTERCONNECT
           value: "{{ ovn_enable_interconnect }}"
+        - name: OVN_OBSERV_ENABLE
+          value: "{{ ovn_observ_enable }}"
         - name: OVN_ENABLE_MULTI_EXTERNAL_GATEWAY
           value: "{{ ovn_enable_multi_external_gateway }}"
         - name: OVN_ENABLE_OVNKUBE_IDENTITY
diff --git a/dist/templates/ovnkube-zone-controller.yaml.j2 b/dist/templates/ovnkube-zone-controller.yaml.j2
index 5fa5b104c0b..2150b9c8ff0 100644
--- a/dist/templates/ovnkube-zone-controller.yaml.j2
+++ b/dist/templates/ovnkube-zone-controller.yaml.j2
@@ -386,6 +386,8 @@ spec:
           value: "local"
         - name: OVN_ENABLE_DNSNAMERESOLVER
           value: "{{ ovn_enable_dnsnameresolver }}"
+        - name: OVN_OBSERV_ENABLE
+          value: "{{ ovn_observ_enable }}"
       # end of container
 
       volumes:
diff --git a/go-controller/pkg/config/config.go b/go-controller/pkg/config/config.go
index f174f1c00e2..6a54509afed 100644
--- a/go-controller/pkg/config/config.go
+++ b/go-controller/pkg/config/config.go
@@ -413,6 +413,7 @@ type OVNKubernetesFeatureConfig struct {
 	EnablePersistentIPs             bool `gcfg:"enable-persistent-ips"`
 	EnableDNSNameResolver           bool `gcfg:"enable-dns-name-resolver"`
 	EnableServiceTemplateSupport    bool `gcfg:"enable-svc-template-support"`
+	EnableObservability             bool `gcfg:"enable-observability"`
 }
 
 // GatewayMode holds the node gateway mode
@@ -1083,6 +1084,12 @@ var OVNK8sFeatureFlags = []cli.Flag{
 		Destination: &cliConfig.OVNKubernetesFeature.EnableServiceTemplateSupport,
 		Value:       OVNKubernetesFeature.EnableServiceTemplateSupport,
 	},
+	&cli.BoolFlag{
+		Name:        "enable-observability",
+		Usage:       "Configure to use OVN sampling with ovn-kubernetes.",
+		Destination: &cliConfig.OVNKubernetesFeature.EnableObservability,
+		Value:       OVNKubernetesFeature.EnableObservability,
+	},
 }
 
 // K8sFlags capture Kubernetes-related options
diff --git a/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml b/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml
index 0a2d84080c6..bbe17ebd0b4 100644
--- a/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml
+++ b/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml
@@ -433,6 +433,8 @@ spec:
           value: {{ hasKey .Values.global "enableSvcTemplate" | ternary .Values.global.enableSvcTemplate true | quote }}
         - name: OVN_ENABLE_DNSNAMERESOLVER
           value: {{ hasKey .Values.global "enableDNSNameResolver" | ternary .Values.global.enableDNSNameResolver false | quote }}
+        - name: OVN_OBSERV_ENABLE
+          value: {{ hasKey .Values.global "enableObservability" | ternary .Values.global.enableObservability false | quote }}
         readinessProbe:
           exec:
             command: ["/usr/bin/ovn-kube-util", "readiness-probe", "-t", "ovnkube-node"]
diff --git a/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml b/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml
index bcb6e18db46..906b7a7e04e 100644
--- a/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml
+++ b/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml
@@ -352,6 +352,8 @@ spec:
           value: "local"
         - name: OVN_ENABLE_DNSNAMERESOLVER
           value: {{ hasKey .Values.global "enableDNSNameResolver" | ternary .Values.global.enableDNSNameResolver false | quote }}
+        - name: OVN_OBSERV_ENABLE
+          value: {{ hasKey .Values.global "enableObservability" | ternary .Values.global.enableObservability false | quote }}
       # end of container
       volumes:
       # Common volumes

From ca417023a20a677f9bd55710d25b1d3f7b1e7dc5 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Fri, 2 Aug 2024 13:12:52 +0200
Subject: [PATCH 06/48] Add observability manager, add samplingConfig for acl
 db ops.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 go-controller/pkg/libovsdb/ops/acl.go         |   9 +-
 go-controller/pkg/libovsdb/ops/acl_test.go    |   2 +-
 .../pkg/libovsdb/ops/db_object_ids.go         |  12 +-
 go-controller/pkg/libovsdb/ops/sample.go      | 221 +++++++++++
 .../network_controller_manager.go             |  19 +-
 .../pkg/observability/observability.go        | 303 ++++++++++++++
 .../observability/observability_suite_test.go |  13 +
 .../pkg/observability/observability_test.go   | 374 ++++++++++++++++++
 .../pkg/ovn/base_network_controller.go        |  10 +
 .../ovn/base_network_controller_multicast.go  |   6 +-
 .../pkg/ovn/base_network_controller_policy.go |   8 +-
 .../admin_network_policy.go                   |   4 +-
 .../admin_network_policy_controller.go        |  15 +-
 .../admin_network_policy/status_test.go       |   1 +
 .../pkg/ovn/default_network_controller.go     |   8 +-
 .../ovn/default_network_controller_policy.go  |   2 +-
 go-controller/pkg/ovn/egressfirewall.go       |   2 +-
 .../ovn/external_ids_syncer/acl/acl_sync.go   |   4 +-
 go-controller/pkg/ovn/ovn.go                  |   1 +
 go-controller/pkg/ovn/ovn_test.go             |   2 +-
 go-controller/pkg/ovn/udn_isolation.go        |   2 +-
 21 files changed, 985 insertions(+), 33 deletions(-)
 create mode 100644 go-controller/pkg/libovsdb/ops/sample.go
 create mode 100644 go-controller/pkg/observability/observability.go
 create mode 100644 go-controller/pkg/observability/observability_suite_test.go
 create mode 100644 go-controller/pkg/observability/observability_test.go

diff --git a/go-controller/pkg/libovsdb/ops/acl.go b/go-controller/pkg/libovsdb/ops/acl.go
index aacc00706d8..3d6e8151459 100644
--- a/go-controller/pkg/libovsdb/ops/acl.go
+++ b/go-controller/pkg/libovsdb/ops/acl.go
@@ -22,7 +22,7 @@ func GetACLName(acl *nbdb.ACL) string {
 
 func getACLMutableFields(acl *nbdb.ACL) []interface{} {
 	return []interface{}{&acl.Action, &acl.Direction, &acl.ExternalIDs, &acl.Log, &acl.Match, &acl.Meter,
-		&acl.Name, &acl.Options, &acl.Priority, &acl.Severity, &acl.Tier}
+		&acl.Name, &acl.Options, &acl.Priority, &acl.Severity, &acl.Tier, &acl.SampleNew, &acl.SampleEst}
 }
 
 type aclPredicate func(*nbdb.ACL) bool
@@ -107,7 +107,7 @@ func SetACLLogging(acl *nbdb.ACL, severity nbdb.ACLSeverity, log bool) {
 
 // CreateOrUpdateACLsOps creates or updates the provided ACLs returning the
 // corresponding ops
-func CreateOrUpdateACLsOps(nbClient libovsdbclient.Client, ops []libovsdb.Operation, acls ...*nbdb.ACL) ([]libovsdb.Operation, error) {
+func CreateOrUpdateACLsOps(nbClient libovsdbclient.Client, ops []libovsdb.Operation, samplingConfig *SamplingConfig, acls ...*nbdb.ACL) ([]libovsdb.Operation, error) {
 	opModels := make([]operationModel, 0, len(acls))
 	for i := range acls {
 		// can't use i in the predicate, for loop replaces it in-memory
@@ -117,6 +117,7 @@ func CreateOrUpdateACLsOps(nbClient libovsdbclient.Client, ops []libovsdb.Operat
 			// node ACLs won't have names set
 			*acl.Name = fmt.Sprintf("%.63s", *acl.Name)
 		}
+		opModels = addSample(samplingConfig, opModels, acl)
 		opModel := operationModel{
 			Model:          acl,
 			OnModelUpdates: getACLMutableFields(acl),
@@ -149,8 +150,8 @@ func UpdateACLsOps(nbClient libovsdbclient.Client, ops []libovsdb.Operation, acl
 }
 
 // CreateOrUpdateACLs creates or updates the provided ACLs
-func CreateOrUpdateACLs(nbClient libovsdbclient.Client, acls ...*nbdb.ACL) error {
-	ops, err := CreateOrUpdateACLsOps(nbClient, nil, acls...)
+func CreateOrUpdateACLs(nbClient libovsdbclient.Client, samplingConfig *SamplingConfig, acls ...*nbdb.ACL) error {
+	ops, err := CreateOrUpdateACLsOps(nbClient, nil, samplingConfig, acls...)
 	if err != nil {
 		return err
 	}
diff --git a/go-controller/pkg/libovsdb/ops/acl_test.go b/go-controller/pkg/libovsdb/ops/acl_test.go
index 2b2ad7a6d10..7f52bb01e76 100644
--- a/go-controller/pkg/libovsdb/ops/acl_test.go
+++ b/go-controller/pkg/libovsdb/ops/acl_test.go
@@ -146,7 +146,7 @@ func TestCreateOrUpdateACL(t *testing.T) {
 
 			updatedACL := tt.finalACL.DeepCopy()
 			updatedACL.UUID = initialACLs[0].UUID
-			err = CreateOrUpdateACLs(nbClient, updatedACL)
+			err = CreateOrUpdateACLs(nbClient, nil, updatedACL)
 			if err != nil {
 				t.Fatalf("test: \"%s\" failed to set up test harness: %v", tt.desc, err)
 			}
diff --git a/go-controller/pkg/libovsdb/ops/db_object_ids.go b/go-controller/pkg/libovsdb/ops/db_object_ids.go
index 2275091f8c5..b242d7d3ea0 100644
--- a/go-controller/pkg/libovsdb/ops/db_object_ids.go
+++ b/go-controller/pkg/libovsdb/ops/db_object_ids.go
@@ -8,7 +8,7 @@ import (
 )
 
 type dbObjType int
-type ownerType string
+type ownerType = string
 type ExternalIDKey string
 
 func (key ExternalIDKey) String() string {
@@ -225,7 +225,7 @@ func (objectIDs *DbObjectIDs) GetExternalIDs() map[string]string {
 func (objectIDs *DbObjectIDs) getExternalIDs(allowEmptyKeys bool) map[string]string {
 	externalIDs := map[string]string{
 		OwnerControllerKey.String(): objectIDs.ownerControllerName,
-		OwnerTypeKey.String():       string(objectIDs.idsType.ownerObjectType),
+		OwnerTypeKey.String():       objectIDs.idsType.ownerObjectType,
 	}
 	for key, value := range objectIDs.objectIDs {
 		externalIDs[key.String()] = value
@@ -244,7 +244,7 @@ func (objectIDs *DbObjectIDs) getExternalIDs(allowEmptyKeys bool) map[string]str
 // in the DbObjectIDs.objectIDs, they will be replaced with empty strings.
 // String returns the representation of all the information set in DbObjectIDs.
 func (objectIDs *DbObjectIDs) String() string {
-	id := objectIDs.ownerControllerName + ":" + string(objectIDs.idsType.ownerObjectType)
+	id := objectIDs.ownerControllerName + ":" + objectIDs.idsType.ownerObjectType
 	for _, key := range objectIDs.idsType.GetExternalIDKeys() {
 		id += ":" + objectIDs.objectIDs[key]
 	}
@@ -258,7 +258,7 @@ func (objectIDs *DbObjectIDs) GetIDsType() *ObjectIDsType {
 // getUniqueID returns primary id that is build based on objectIDs values.
 // If at least one required key is missing, an error will be returned.
 func (objectIDs *DbObjectIDs) getUniqueID() (string, error) {
-	id := objectIDs.ownerControllerName + ":" + string(objectIDs.idsType.ownerObjectType)
+	id := objectIDs.ownerControllerName + ":" + objectIDs.idsType.ownerObjectType
 	for _, key := range objectIDs.idsType.GetExternalIDKeys() {
 		value, ok := objectIDs.objectIDs[key]
 		if !ok {
@@ -273,9 +273,9 @@ func (objectIDs *DbObjectIDs) getUniqueID() (string, error) {
 // on OwnerControllerKey key, and verifies OwnerControllerKey value matches given objectIDsType.
 // All the other ids from objectIDsType will be set to DbObjectIDs.objectIDs.
 func NewDbObjectIDsFromExternalIDs(objectIDsType *ObjectIDsType, externalIDs map[string]string) (*DbObjectIDs, error) {
-	if externalIDs[OwnerTypeKey.String()] != string(objectIDsType.ownerObjectType) {
+	if externalIDs[OwnerTypeKey.String()] != objectIDsType.ownerObjectType {
 		return nil, fmt.Errorf("expected ExternalID %s to equal %s, got %s",
-			OwnerTypeKey, string(objectIDsType.ownerObjectType), externalIDs[OwnerTypeKey.String()])
+			OwnerTypeKey, objectIDsType.ownerObjectType, externalIDs[OwnerTypeKey.String()])
 	}
 	if externalIDs[OwnerControllerKey.String()] == "" {
 		return nil, fmt.Errorf("required ExternalID %s is empty", OwnerControllerKey)
diff --git a/go-controller/pkg/libovsdb/ops/sample.go b/go-controller/pkg/libovsdb/ops/sample.go
new file mode 100644
index 00000000000..7f4f527d1bd
--- /dev/null
+++ b/go-controller/pkg/libovsdb/ops/sample.go
@@ -0,0 +1,221 @@
+package ops
+
+import (
+	"golang.org/x/net/context"
+	"hash/fnv"
+
+	libovsdbclient "github.com/ovn-org/libovsdb/client"
+	"github.com/ovn-org/libovsdb/model"
+	libovsdb "github.com/ovn-org/libovsdb/ovsdb"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+)
+
+func CreateOrUpdateSampleCollector(nbClient libovsdbclient.Client, collector *nbdb.SampleCollector) error {
+	opModel := operationModel{
+		Model:          collector,
+		OnModelUpdates: onModelUpdatesAllNonDefault(),
+		ErrNotFound:    false,
+		BulkOp:         false,
+	}
+
+	m := newModelClient(nbClient)
+	_, err := m.CreateOrUpdate(opModel)
+	return err
+}
+
+func UpdateSampleCollectorExternalIDs(nbClient libovsdbclient.Client, collector *nbdb.SampleCollector) error {
+	opModel := operationModel{
+		Model:          collector,
+		OnModelUpdates: []interface{}{&collector.ExternalIDs},
+		ErrNotFound:    true,
+		BulkOp:         false,
+	}
+
+	m := newModelClient(nbClient)
+	_, err := m.CreateOrUpdate(opModel)
+	return err
+}
+
+func DeleteSampleCollector(nbClient libovsdbclient.Client, collector *nbdb.SampleCollector) error {
+	opModel := operationModel{
+		Model:       collector,
+		ErrNotFound: false,
+		BulkOp:      false,
+	}
+	m := newModelClient(nbClient)
+	return m.Delete(opModel)
+}
+
+func DeleteSampleCollectorWithPredicate(nbClient libovsdbclient.Client, p func(collector *nbdb.SampleCollector) bool) error {
+	opModel := operationModel{
+		Model:          &nbdb.SampleCollector{},
+		ModelPredicate: p,
+		ErrNotFound:    false,
+		BulkOp:         true,
+	}
+	m := newModelClient(nbClient)
+	return m.Delete(opModel)
+}
+
+func FindSampleCollectorWithPredicate(nbClient libovsdbclient.Client, p func(*nbdb.SampleCollector) bool) ([]*nbdb.SampleCollector, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), config.Default.OVSDBTxnTimeout)
+	defer cancel()
+	collectors := []*nbdb.SampleCollector{}
+	err := nbClient.WhereCache(p).List(ctx, &collectors)
+	return collectors, err
+}
+
+func ListSampleCollectors(nbClient libovsdbclient.Client) ([]*nbdb.SampleCollector, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), config.Default.OVSDBTxnTimeout)
+	defer cancel()
+	collectors := []*nbdb.SampleCollector{}
+	err := nbClient.List(ctx, &collectors)
+	return collectors, err
+}
+
+func CreateOrUpdateSamplingAppsOps(nbClient libovsdbclient.Client, ops []libovsdb.Operation, samplingApps ...*nbdb.SamplingApp) ([]libovsdb.Operation, error) {
+	opModels := make([]operationModel, 0, len(samplingApps))
+	for i := range samplingApps {
+		// can't use i in the predicate, for loop replaces it in-memory
+		samplingApp := samplingApps[i]
+		opModel := operationModel{
+			Model:          samplingApp,
+			OnModelUpdates: onModelUpdatesAllNonDefault(),
+			ErrNotFound:    false,
+			BulkOp:         false,
+		}
+		opModels = append(opModels, opModel)
+	}
+
+	modelClient := newModelClient(nbClient)
+	return modelClient.CreateOrUpdateOps(ops, opModels...)
+}
+
+func DeleteSamplingAppsWithPredicate(nbClient libovsdbclient.Client, p func(collector *nbdb.SamplingApp) bool) error {
+	opModel := operationModel{
+		Model:          &nbdb.SamplingApp{},
+		ModelPredicate: p,
+		ErrNotFound:    false,
+		BulkOp:         true,
+	}
+	m := newModelClient(nbClient)
+	return m.Delete(opModel)
+}
+
+func FindSample(nbClient libovsdbclient.Client, sampleMetadata int) (*nbdb.Sample, error) {
+	sample := &nbdb.Sample{
+		Metadata: sampleMetadata,
+	}
+	return GetSample(nbClient, sample)
+}
+
+func GetSample(nbClient libovsdbclient.Client, sample *nbdb.Sample) (*nbdb.Sample, error) {
+	found := []*nbdb.Sample{}
+	opModel := operationModel{
+		Model:          sample,
+		ExistingResult: &found,
+		ErrNotFound:    true,
+		BulkOp:         false,
+	}
+	modelClient := newModelClient(nbClient)
+	err := modelClient.Lookup(opModel)
+	if err != nil {
+		return nil, err
+	}
+	return found[0], err
+}
+
+type SampleFeature = string
+
+const (
+	EgressFirewallSample     SampleFeature = "EgressFirewall"
+	NetworkPolicySample      SampleFeature = "NetworkPolicy"
+	AdminNetworkPolicySample SampleFeature = "AdminNetworkPolicy"
+	MulticastSample          SampleFeature = "Multicast"
+	UDNIsolationSample       SampleFeature = "UDNIsolation"
+)
+
+// SamplingConfig is used to configure sampling for different db objects.
+type SamplingConfig struct {
+	featureCollectors map[SampleFeature][]string
+}
+
+func NewSamplingConfig(featureCollectors map[SampleFeature][]string) *SamplingConfig {
+	return &SamplingConfig{
+		featureCollectors: featureCollectors,
+	}
+}
+
+func addSample(c *SamplingConfig, opModels []operationModel, model model.Model) []operationModel {
+	switch t := model.(type) {
+	case *nbdb.ACL:
+		return createOrUpdateSampleForACL(opModels, c, t)
+	}
+	return opModels
+}
+
+// createOrUpdateSampleForACL should be called before acl operationModel is appended to opModels.
+func createOrUpdateSampleForACL(opModels []operationModel, c *SamplingConfig, acl *nbdb.ACL) []operationModel {
+	if c == nil {
+		acl.SampleEst = nil
+		acl.SampleNew = nil
+		return opModels
+	}
+	collectors := c.featureCollectors[getACLSampleFeature(acl)]
+	if len(collectors) == 0 {
+		acl.SampleEst = nil
+		acl.SampleNew = nil
+		return opModels
+	}
+	aclID := GetACLSampleID(acl)
+	sample := &nbdb.Sample{
+		Collectors: collectors,
+		// 32 bits
+		Metadata: int(aclID),
+	}
+	opModel := operationModel{
+		Model: sample,
+		DoAfter: func() {
+			acl.SampleEst = &sample.UUID
+			acl.SampleNew = &sample.UUID
+		},
+		OnModelUpdates: []interface{}{&sample.Collectors},
+		ErrNotFound:    false,
+		BulkOp:         false,
+	}
+	opModels = append(opModels, opModel)
+	return opModels
+}
+
+func GetACLSampleID(acl *nbdb.ACL) uint32 {
+	// primaryID is unique for each ACL, but established connections will keep sampleID that is set on
+	// connection creation. Here is the situation we want to avoid:
+	// 1. ACL1 is created with sampleID=1 (e.g. based on ANP namespace+name+...+rule index with action Allow)
+	// 2. connection A is established with sampleID=1, sample is decoded to say "Allowed by ANP namespace+name"
+	// 3. ACL1 is updated with sampleID=1 (e.g. now same rule in ANP says Deny, but PrimaryIDKey is the same)
+	// 4. connection A still generates samples with sampleID=1, but now it is "Denied by ANP namespace+name"
+	// In reality, connection A is still allowed, as existing connections are not affected by ANP updates.
+	// To avoid this, we encode Match and Action to the sampleID, to ensure a new sampleID is assigned on Match or action change.
+	// In that case stale sampleIDs will just report messages like "sampling for this connection was updated or deleted".
+	primaryID := acl.ExternalIDs[PrimaryIDKey.String()] + acl.Match + acl.Action
+	h := fnv.New32a()
+	h.Write([]byte(primaryID))
+	return h.Sum32()
+}
+
+func getACLSampleFeature(acl *nbdb.ACL) SampleFeature {
+	switch acl.ExternalIDs[OwnerTypeKey.String()] {
+	case AdminNetworkPolicyOwnerType, BaselineAdminNetworkPolicyOwnerType:
+		return AdminNetworkPolicySample
+	case MulticastNamespaceOwnerType, MulticastClusterOwnerType:
+		return MulticastSample
+	case NetpolNodeOwnerType, NetworkPolicyOwnerType, NetpolNamespaceOwnerType:
+		return NetworkPolicySample
+	case EgressFirewallOwnerType:
+		return EgressFirewallSample
+	case UDNIsolationOwnerType:
+		return UDNIsolationSample
+	}
+	return ""
+}
diff --git a/go-controller/pkg/network-controller-manager/network_controller_manager.go b/go-controller/pkg/network-controller-manager/network_controller_manager.go
index fc735baf3e1..0a47a396ef1 100644
--- a/go-controller/pkg/network-controller-manager/network_controller_manager.go
+++ b/go-controller/pkg/network-controller-manager/network_controller_manager.go
@@ -17,6 +17,7 @@ import (
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/metrics"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
 	nad "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/network-attach-def-controller"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/observability"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn"
 	ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
@@ -284,12 +285,12 @@ func (cm *NetworkControllerManager) newCommonNetworkControllerInfo() (*ovn.Commo
 }
 
 // initDefaultNetworkController creates the controller for default network
-func (cm *NetworkControllerManager) initDefaultNetworkController() error {
+func (cm *NetworkControllerManager) initDefaultNetworkController(observManager *observability.Manager) error {
 	cnci, err := cm.newCommonNetworkControllerInfo()
 	if err != nil {
 		return fmt.Errorf("failed to create common network controller info: %w", err)
 	}
-	defaultController, err := ovn.NewDefaultNetworkController(cnci)
+	defaultController, err := ovn.NewDefaultNetworkController(cnci, observManager)
 	if err != nil {
 		return err
 	}
@@ -385,7 +386,19 @@ func (cm *NetworkControllerManager) Start(ctx context.Context) error {
 	}
 	cm.podRecorder.Run(cm.sbClient, cm.stopChan)
 
-	err = cm.initDefaultNetworkController()
+	var observabilityManager *observability.Manager
+	if config.OVNKubernetesFeature.EnableObservability {
+		observabilityManager = observability.NewManager(cm.nbClient)
+		if err = observabilityManager.Init(); err != nil {
+			return fmt.Errorf("failed to init observability manager: %w", err)
+		}
+	} else {
+		err = observability.Cleanup(cm.nbClient)
+		if err != nil {
+			klog.Warningf("Observability cleanup failed, expected if not all Samples ware deleted yet: %v", err)
+		}
+	}
+	err = cm.initDefaultNetworkController(observabilityManager)
 	if err != nil {
 		return fmt.Errorf("failed to init default network controller: %v", err)
 	}
diff --git a/go-controller/pkg/observability/observability.go b/go-controller/pkg/observability/observability.go
new file mode 100644
index 00000000000..6349c15199f
--- /dev/null
+++ b/go-controller/pkg/observability/observability.go
@@ -0,0 +1,303 @@
+package observability
+
+import (
+	"fmt"
+	"slices"
+	"strings"
+	"sync"
+	"time"
+
+	libovsdbclient "github.com/ovn-org/libovsdb/client"
+	libovsdb "github.com/ovn-org/libovsdb/ovsdb"
+	"k8s.io/apimachinery/pkg/util/sets"
+	"k8s.io/klog/v2"
+
+	libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+)
+
+// OVN observ app IDs. Make sure to always add new apps in the end.
+const (
+	DropSamplingID = iota + 1
+	ACLNewTrafficSamplingID
+	ACLEstTrafficSamplingID
+)
+
+// temporary const, until we have dynamic config
+const DefaultObservabilityCollectorSetID = 42
+
+// this is inferred from nbdb schema, check Sample_Collector.id
+const maxCollectorID = 255
+const collectorFeaturesExternalID = "sample-features"
+
+// collectorConfig holds the configuration for a collector.
+// It is allowed to set different probabilities for every feature.
+// collectorSetID is used to set up sampling via OVSDB.
+type collectorConfig struct {
+	collectorSetID int
+	// probability in percent, 0 to 100
+	featuresProbability map[libovsdbops.SampleFeature]int
+}
+
+type Manager struct {
+	nbClient       libovsdbclient.Client
+	sampConfig     *libovsdbops.SamplingConfig
+	collectorsLock sync.Mutex
+	// nbdb Collectors have probability. To allow different probabilities for different features,
+	// multiple nbdb Collectors will be created, one per probability.
+	// getCollectorKey() => collector.UUID
+	dbCollectors map[string]string
+	// cleaning up unused collectors may take time and multiple retries, as all referencing samples must be removed first.
+	// Therefore, we need to save state between those retries.
+	// getCollectorKey() => collector.SetID
+	unusedCollectors              map[string]int
+	unusedCollectorsRetryInterval time.Duration
+	collectorsCleanupRetries      int
+	// Only maxCollectorID collectors are allowed, each should have unique ID.
+	// this set is tracking already assigned IDs.
+	takenCollectorIDs sets.Set[int]
+}
+
+func NewManager(nbClient libovsdbclient.Client) *Manager {
+	return &Manager{
+		nbClient:                      nbClient,
+		collectorsLock:                sync.Mutex{},
+		dbCollectors:                  make(map[string]string),
+		unusedCollectors:              make(map[string]int),
+		unusedCollectorsRetryInterval: time.Minute,
+		takenCollectorIDs:             sets.New[int](),
+	}
+}
+
+func (m *Manager) SamplingConfig() *libovsdbops.SamplingConfig {
+	return m.sampConfig
+}
+
+func (m *Manager) Init() error {
+	// this will be read from the kube-api in the future
+	currentConfig := &collectorConfig{
+		collectorSetID: DefaultObservabilityCollectorSetID,
+		featuresProbability: map[libovsdbops.SampleFeature]int{
+			libovsdbops.EgressFirewallSample:     100,
+			libovsdbops.NetworkPolicySample:      100,
+			libovsdbops.AdminNetworkPolicySample: 100,
+			libovsdbops.MulticastSample:          100,
+			libovsdbops.UDNIsolationSample:       100,
+		},
+	}
+
+	return m.initWithConfig(currentConfig)
+}
+
+func (m *Manager) initWithConfig(config *collectorConfig) error {
+	if err := m.setSamplingAppIDs(); err != nil {
+		return err
+	}
+	if err := m.setDbCollectors(); err != nil {
+		return err
+	}
+
+	featuresConfig, err := m.addCollector(config)
+	if err != nil {
+		return err
+	}
+	m.sampConfig = libovsdbops.NewSamplingConfig(featuresConfig)
+
+	// now cleanup stale collectors
+	m.deleteStaleCollectorsWithRetry()
+	return nil
+}
+
+func (m *Manager) setDbCollectors() error {
+	m.collectorsLock.Lock()
+	defer m.collectorsLock.Unlock()
+	clear(m.dbCollectors)
+	collectors, err := libovsdbops.ListSampleCollectors(m.nbClient)
+	if err != nil {
+		return fmt.Errorf("error getting sample collectors: %w", err)
+	}
+	for _, collector := range collectors {
+		collectorKey := getCollectorKey(collector.SetID, collector.Probability)
+		m.dbCollectors[collectorKey] = collector.UUID
+		m.takenCollectorIDs.Insert(collector.ID)
+		// all collectors are unused, until we update existing configs
+		m.unusedCollectors[collectorKey] = collector.ID
+	}
+	return nil
+}
+
+// Stale collectors can't be deleted until all referencing Samples are deleted.
+// Samples will be deleted asynchronously by different controllers on their init with the new Manager.
+// deleteStaleCollectorsWithRetry will retry, considering deletion should eventually succeed when all controllers
+// update their db entries to use the latest observability config.
+func (m *Manager) deleteStaleCollectorsWithRetry() {
+	if err := m.deleteStaleCollectors(); err != nil {
+		m.collectorsCleanupRetries += 1
+		// allow retries for 1 hour, hopefully it will be enough for all handler to complete initial sync
+		if m.collectorsCleanupRetries > 60 {
+			m.collectorsCleanupRetries = 0
+			klog.Errorf("Cleanup stale collectors failed after 30 retries: %v", err)
+			return
+		}
+		time.AfterFunc(m.unusedCollectorsRetryInterval, m.deleteStaleCollectorsWithRetry)
+		return
+	}
+	m.collectorsCleanupRetries = 0
+	klog.Infof("Cleanup stale collectors succeeded.")
+}
+
+func (m *Manager) deleteStaleCollectors() error {
+	m.collectorsLock.Lock()
+	defer m.collectorsLock.Unlock()
+	var lastErr error
+	for collectorKey, collectorSetID := range m.unusedCollectors {
+		collectorUUID := m.dbCollectors[collectorKey]
+		err := libovsdbops.DeleteSampleCollector(m.nbClient, &nbdb.SampleCollector{
+			UUID: collectorUUID,
+		})
+		if err != nil {
+			lastErr = err
+			klog.Infof("Error deleting collector with ID=%d: %v", collectorSetID, lastErr)
+			continue
+		}
+		delete(m.unusedCollectors, collectorKey)
+		delete(m.dbCollectors, collectorKey)
+		delete(m.takenCollectorIDs, collectorSetID)
+	}
+	return lastErr
+}
+
+// Cleanup must be called when observability is no longer needed.
+// It will return an error if some samples still exist in the db.
+// This is expected, and Cleanup may be retried on the next restart.
+func Cleanup(nbClient libovsdbclient.Client) error {
+	// Do the opposite of init
+	err := libovsdbops.DeleteSamplingAppsWithPredicate(nbClient, func(app *nbdb.SamplingApp) bool {
+		return true
+	})
+	if err != nil {
+		return fmt.Errorf("error deleting sampling apps: %w", err)
+	}
+
+	err = libovsdbops.DeleteSampleCollectorWithPredicate(nbClient, func(collector *nbdb.SampleCollector) bool {
+		return true
+	})
+	if err != nil {
+		return fmt.Errorf("error deleting sample collectors: %w", err)
+	}
+	return nil
+}
+
+func (m *Manager) setSamplingAppIDs() error {
+	var ops []libovsdb.Operation
+	var err error
+	for _, appConfig := range []struct {
+		id      int
+		appType nbdb.SamplingAppType
+	}{
+		{
+			id:      DropSamplingID,
+			appType: nbdb.SamplingAppTypeDrop,
+		},
+		{
+			id:      ACLNewTrafficSamplingID,
+			appType: nbdb.SamplingAppTypeACLNew,
+		},
+		{
+			id:      ACLEstTrafficSamplingID,
+			appType: nbdb.SamplingAppTypeACLEst,
+		},
+	} {
+		samplingApp := &nbdb.SamplingApp{
+			ID:   appConfig.id,
+			Type: appConfig.appType,
+		}
+		ops, err = libovsdbops.CreateOrUpdateSamplingAppsOps(m.nbClient, ops, samplingApp)
+		if err != nil {
+			return fmt.Errorf("error creating or updating sampling app %s: %w", appConfig.appType, err)
+		}
+	}
+	_, err = libovsdbops.TransactAndCheck(m.nbClient, ops)
+	return err
+}
+
+func groupByProbability(c *collectorConfig) map[int][]libovsdbops.SampleFeature {
+	probabilities := make(map[int][]libovsdbops.SampleFeature)
+	for feature, percentProbability := range c.featuresProbability {
+		probability := percentToProbability(percentProbability)
+		probabilities[probability] = append(probabilities[probability], feature)
+	}
+	return probabilities
+}
+
+func getCollectorKey(collectorID int, probability int) string {
+	return fmt.Sprintf("%d-%d", collectorID, probability)
+}
+
+func (m *Manager) getFreeCollectorID() (int, error) {
+	for i := 1; i <= maxCollectorID; i++ {
+		if !m.takenCollectorIDs.Has(i) {
+			return i, nil
+		}
+	}
+	return 0, fmt.Errorf("no free collector IDs")
+}
+
+func (m *Manager) addCollector(conf *collectorConfig) (map[libovsdbops.SampleFeature][]string, error) {
+	m.collectorsLock.Lock()
+	defer m.collectorsLock.Unlock()
+	sampleFeaturesConfig := make(map[libovsdbops.SampleFeature][]string)
+	probabilityConfig := groupByProbability(conf)
+
+	for probability, features := range probabilityConfig {
+		collectorKey := getCollectorKey(conf.collectorSetID, probability)
+		var collectorUUID string
+		var ok bool
+		// ensure predictable externalID
+		slices.Sort(features)
+		collectorFeatures := strings.Join(features, ",")
+		if collectorUUID, ok = m.dbCollectors[collectorKey]; !ok {
+			collectorID, err := m.getFreeCollectorID()
+			if err != nil {
+				return sampleFeaturesConfig, err
+			}
+			collector := &nbdb.SampleCollector{
+				ID:          collectorID,
+				SetID:       conf.collectorSetID,
+				Probability: probability,
+				ExternalIDs: map[string]string{
+					collectorFeaturesExternalID: collectorFeatures,
+				},
+			}
+			err = libovsdbops.CreateOrUpdateSampleCollector(m.nbClient, collector)
+			if err != nil {
+				return sampleFeaturesConfig, err
+			}
+			collectorUUID = collector.UUID
+			m.dbCollectors[collectorKey] = collectorUUID
+			m.takenCollectorIDs.Insert(collectorID)
+		} else {
+			// update collector's features
+			collector := &nbdb.SampleCollector{
+				UUID: collectorUUID,
+				ExternalIDs: map[string]string{
+					collectorFeaturesExternalID: collectorFeatures,
+				},
+			}
+			err := libovsdbops.UpdateSampleCollectorExternalIDs(m.nbClient, collector)
+			if err != nil {
+				return sampleFeaturesConfig, err
+			}
+			// collector is used, remove from unused Collectors
+			delete(m.unusedCollectors, collectorKey)
+		}
+		for _, feature := range features {
+			sampleFeaturesConfig[feature] = append(sampleFeaturesConfig[feature], collectorUUID)
+		}
+	}
+	return sampleFeaturesConfig, nil
+}
+
+func percentToProbability(percent int) int {
+	return 65535 * percent / 100
+}
diff --git a/go-controller/pkg/observability/observability_suite_test.go b/go-controller/pkg/observability/observability_suite_test.go
new file mode 100644
index 00000000000..03ca9737a91
--- /dev/null
+++ b/go-controller/pkg/observability/observability_suite_test.go
@@ -0,0 +1,13 @@
+package observability
+
+import (
+	"testing"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+)
+
+func TestObservabilityManager(t *testing.T) {
+	RegisterFailHandler(Fail)
+	RunSpecs(t, "Observability Manager Suite")
+}
diff --git a/go-controller/pkg/observability/observability_test.go b/go-controller/pkg/observability/observability_test.go
new file mode 100644
index 00000000000..bd5399bc0fa
--- /dev/null
+++ b/go-controller/pkg/observability/observability_test.go
@@ -0,0 +1,374 @@
+package observability
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"strings"
+	"time"
+
+	libovsdbclient "github.com/ovn-org/libovsdb/client"
+	libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+	libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb"
+)
+
+var _ = Describe("Observability Manager", func() {
+	var (
+		nbClient        libovsdbclient.Client
+		libovsdbCleanup *libovsdbtest.Context
+		manager         *Manager
+		initialDB       []libovsdbtest.TestData
+		samplingApps    []libovsdbtest.TestData
+	)
+
+	const collectorUUID = "collector-uuid"
+
+	startManager := func(data []libovsdbtest.TestData) {
+		var err error
+		nbClient, _, libovsdbCleanup, err = libovsdbtest.NewNBSBTestHarness(libovsdbtest.TestSetup{
+			NBData: data})
+		Expect(err).NotTo(HaveOccurred())
+		manager = NewManager(nbClient)
+		err = manager.Init()
+		Expect(err).NotTo(HaveOccurred())
+	}
+
+	createACLWithPortGroup := func(acl *nbdb.ACL) *nbdb.PortGroup {
+		ops, err := libovsdbops.CreateOrUpdateACLsOps(nbClient, nil, manager.SamplingConfig(), acl)
+		Expect(err).NotTo(HaveOccurred())
+		pg := &nbdb.PortGroup{
+			UUID: "pg-uuid",
+			ACLs: []string{acl.UUID},
+		}
+		ops, err = libovsdbops.CreateOrUpdatePortGroupsOps(nbClient, ops, pg)
+		Expect(err).NotTo(HaveOccurred())
+		_, err = libovsdbops.TransactAndCheck(nbClient, ops)
+		Expect(err).NotTo(HaveOccurred())
+		return pg
+	}
+
+	// createOrUpdateACLPreserveUUID calls CreateOrUpdateACLs and sets the acl.UUID back.
+	// that is required as setting real UUID breaks libovsdb matching
+	createOrUpdateACLPreserveUUID := func(nbClient libovsdbclient.Client, samplingConfig *libovsdbops.SamplingConfig, acl *nbdb.ACL) error {
+		namedUUID := acl.UUID
+		err := libovsdbops.CreateOrUpdateACLs(nbClient, samplingConfig, acl)
+		acl.UUID = namedUUID
+		return err
+	}
+
+	BeforeEach(func() {
+		initialDB = []libovsdbtest.TestData{
+			&nbdb.SamplingApp{
+				UUID: "drop-sampling-uuid",
+				ID:   DropSamplingID,
+				Type: nbdb.SamplingAppTypeDrop,
+			},
+			&nbdb.SamplingApp{
+				UUID: "acl-new-traffic-sampling-uuid",
+				ID:   ACLNewTrafficSamplingID,
+				Type: nbdb.SamplingAppTypeACLNew,
+			},
+			&nbdb.SamplingApp{
+				UUID: "acl-est-traffic-sampling-uuid",
+				ID:   ACLEstTrafficSamplingID,
+				Type: nbdb.SamplingAppTypeACLEst,
+			},
+			&nbdb.SampleCollector{
+				UUID:        collectorUUID,
+				ID:          1,
+				SetID:       DefaultObservabilityCollectorSetID,
+				Probability: 65535,
+				ExternalIDs: map[string]string{
+					collectorFeaturesExternalID: strings.Join([]string{libovsdbops.AdminNetworkPolicySample, libovsdbops.EgressFirewallSample,
+						libovsdbops.MulticastSample, libovsdbops.NetworkPolicySample, libovsdbops.UDNIsolationSample}, ","),
+				},
+			},
+		}
+
+		samplingApps = initialDB[:3]
+	})
+
+	AfterEach(func() {
+		if libovsdbCleanup != nil {
+			libovsdbCleanup.Cleanup()
+		}
+	})
+
+	for _, dbSetup := range [][]libovsdbtest.TestData{
+		nil, initialDB,
+	} {
+		msg := "db is empty"
+		if dbSetup != nil {
+			msg = "db is not empty"
+		}
+		When(msg, func() {
+
+			It("should initialize database", func() {
+				startManager(dbSetup)
+				Eventually(nbClient).Should(libovsdbtest.HaveData(initialDB))
+			})
+
+			It("should cleanup database", func() {
+				startManager(dbSetup)
+				Eventually(nbClient).Should(libovsdbtest.HaveData(initialDB))
+				err := Cleanup(nbClient)
+				Expect(err).NotTo(HaveOccurred())
+				Eventually(nbClient).Should(libovsdbtest.HaveEmptyData())
+			})
+
+			It("should return correct collectors for an ACL, when feature is enabled", func() {
+				startManager(dbSetup)
+
+				acl := &nbdb.ACL{
+					UUID: "acl-uuid",
+					ExternalIDs: map[string]string{
+						// NetworkPolicy is enabled by default
+						libovsdbops.OwnerTypeKey.String(): libovsdbops.NetworkPolicyOwnerType,
+					},
+				}
+				pg := createACLWithPortGroup(acl)
+
+				sample := &nbdb.Sample{
+					UUID:       "sample-uuid",
+					Metadata:   int(libovsdbops.GetACLSampleID(acl)),
+					Collectors: []string{collectorUUID},
+				}
+				acl.SampleNew = &sample.UUID
+				acl.SampleEst = &sample.UUID
+
+				Eventually(nbClient).Should(libovsdbtest.HaveData(append(initialDB, sample, pg, acl)))
+			})
+			It("should return correct collectors for an ACL, when feature is disabled", func() {
+				startManager(dbSetup)
+				acl := &nbdb.ACL{
+					UUID: "acl-uuid",
+					ExternalIDs: map[string]string{
+						// disabled-feature doesn't exist => not enabled
+						libovsdbops.OwnerTypeKey.String(): "disabled-feature",
+					},
+				}
+				pg := createACLWithPortGroup(acl)
+
+				Eventually(nbClient).Should(libovsdbtest.HaveData(append(initialDB, pg, acl)))
+			})
+		})
+	}
+
+	It("should update existing ACL, when feature is enabled", func() {
+		// start with ACL that doesn't have samples
+		acl := &nbdb.ACL{
+			UUID: "acl-uuid",
+			ExternalIDs: map[string]string{
+				// NetworkPolicy is enabled by default
+				libovsdbops.OwnerTypeKey.String(): libovsdbops.NetworkPolicyOwnerType,
+			},
+		}
+		pg := &nbdb.PortGroup{
+			UUID: "pg-uuid",
+			ACLs: []string{acl.UUID},
+		}
+		startManager(append(initialDB, acl, pg))
+
+		err := createOrUpdateACLPreserveUUID(nbClient, manager.SamplingConfig(), acl)
+		Expect(err).NotTo(HaveOccurred())
+		// expect sample to be added to the existing acl
+		sample := &nbdb.Sample{
+			UUID:       "sample-uuid",
+			Metadata:   int(libovsdbops.GetACLSampleID(acl)),
+			Collectors: []string{collectorUUID},
+		}
+		acl.SampleNew = &sample.UUID
+		acl.SampleEst = &sample.UUID
+		Eventually(nbClient).Should(libovsdbtest.HaveData(append(initialDB, sample, pg, acl)))
+	})
+
+	It("should update existing ACL, when feature is disabled", func() {
+		// start with ACL that has samples
+		acl := &nbdb.ACL{
+			UUID: "acl-uuid",
+			ExternalIDs: map[string]string{
+				// disabled-feature doesn't exist => not enabled
+				libovsdbops.OwnerTypeKey.String(): "disabled-feature",
+			},
+		}
+		pg := &nbdb.PortGroup{
+			UUID: "pg-uuid",
+			ACLs: []string{acl.UUID},
+		}
+		sample := &nbdb.Sample{
+			UUID:       "sample-uuid",
+			Metadata:   int(libovsdbops.GetACLSampleID(acl)),
+			Collectors: []string{collectorUUID},
+		}
+		acl.SampleNew = &sample.UUID
+		acl.SampleEst = &sample.UUID
+		startManager(append(initialDB, sample, acl, pg))
+
+		err := createOrUpdateACLPreserveUUID(nbClient, manager.SamplingConfig(), acl)
+		Expect(err).NotTo(HaveOccurred())
+		// expect sample to be removed from the existing acl
+		acl.SampleNew = nil
+		acl.SampleEst = nil
+
+		Eventually(nbClient).Should(libovsdbtest.HaveData(append(initialDB, pg, acl)))
+	})
+
+	It("should generate new sampleID on ACL action change", func() {
+		startManager(initialDB)
+		acl := &nbdb.ACL{
+			UUID:   "acl-uuid",
+			Action: nbdb.ACLActionAllowRelated,
+			ExternalIDs: map[string]string{
+				// NetworkPolicy is enabled by default
+				libovsdbops.OwnerTypeKey.String(): libovsdbops.NetworkPolicyOwnerType,
+			},
+		}
+		createACLWithPortGroup(acl)
+
+		// find sample by ACL and save sampleID
+		acls, err := libovsdbops.FindACLs(nbClient, []*nbdb.ACL{acl})
+		Expect(err).NotTo(HaveOccurred())
+		Expect(acls).To(HaveLen(1))
+		sample, err := libovsdbops.GetSample(nbClient, &nbdb.Sample{
+			UUID: *acls[0].SampleNew,
+		})
+		Expect(err).NotTo(HaveOccurred())
+		sampleID := sample.Metadata
+
+		// update acl Action
+		acl.Action = nbdb.ACLActionDrop
+		err = createOrUpdateACLPreserveUUID(nbClient, manager.SamplingConfig(), acl)
+		Expect(err).NotTo(HaveOccurred())
+
+		// find new sampleID
+		acls, err = libovsdbops.FindACLs(nbClient, []*nbdb.ACL{acl})
+		Expect(err).NotTo(HaveOccurred())
+		Expect(acls).To(HaveLen(1))
+		sample, err = libovsdbops.GetSample(nbClient, &nbdb.Sample{
+			UUID: *acls[0].SampleNew,
+		})
+		Expect(err).NotTo(HaveOccurred())
+		Expect(sample.Metadata).NotTo(Equal(sampleID))
+	})
+
+	When("non-default config is used", func() {
+		startManagerWithConfig := func(data []libovsdbtest.TestData, config *collectorConfig) {
+			var err error
+			nbClient, _, libovsdbCleanup, err = libovsdbtest.NewNBSBTestHarness(libovsdbtest.TestSetup{
+				NBData: data})
+			Expect(err).NotTo(HaveOccurred())
+			manager = NewManager(nbClient)
+			// tweak retry interval for testing
+			manager.unusedCollectorsRetryInterval = time.Second
+			err = manager.initWithConfig(config)
+			Expect(err).NotTo(HaveOccurred())
+		}
+
+		It("should update stale collectors", func() {
+			// tweakedConfig doesn't have EgressFirewall enabled, and sets different probability for NetworkPolicy
+			tweakedConfig := &collectorConfig{
+				collectorSetID: DefaultObservabilityCollectorSetID,
+				featuresProbability: map[libovsdbops.SampleFeature]int{
+					libovsdbops.NetworkPolicySample:      50,
+					libovsdbops.AdminNetworkPolicySample: 100,
+					libovsdbops.MulticastSample:          100,
+					libovsdbops.UDNIsolationSample:       100,
+				},
+			}
+			startManagerWithConfig(initialDB, tweakedConfig)
+			expectedDB := append(samplingApps,
+				&nbdb.SampleCollector{
+					UUID:        collectorUUID,
+					ID:          1,
+					SetID:       DefaultObservabilityCollectorSetID,
+					Probability: 65535,
+					ExternalIDs: map[string]string{
+						collectorFeaturesExternalID: strings.Join([]string{libovsdbops.AdminNetworkPolicySample,
+							libovsdbops.MulticastSample, libovsdbops.UDNIsolationSample}, ","),
+					},
+				},
+				&nbdb.SampleCollector{
+					UUID:        collectorUUID + "-2",
+					ID:          2,
+					SetID:       DefaultObservabilityCollectorSetID,
+					Probability: 32767,
+					ExternalIDs: map[string]string{
+						collectorFeaturesExternalID: libovsdbops.NetworkPolicySample,
+					},
+				},
+			)
+			Eventually(nbClient).Should(libovsdbtest.HaveData(expectedDB))
+		})
+		It("should cleanup stale collectors", func() {
+			// tweakedConfig doesn't have probability used by existing collector
+			tweakedConfig := &collectorConfig{
+				collectorSetID: DefaultObservabilityCollectorSetID,
+				featuresProbability: map[libovsdbops.SampleFeature]int{
+					libovsdbops.NetworkPolicySample: 50,
+				},
+			}
+
+			startManagerWithConfig(initialDB, tweakedConfig)
+			expectedDB := append(samplingApps,
+				&nbdb.SampleCollector{
+					UUID:        collectorUUID + "-2",
+					ID:          2,
+					SetID:       DefaultObservabilityCollectorSetID,
+					Probability: 32767,
+					ExternalIDs: map[string]string{
+						collectorFeaturesExternalID: libovsdbops.NetworkPolicySample,
+					},
+				},
+			)
+			Eventually(nbClient).Should(libovsdbtest.HaveData(expectedDB))
+		})
+		It("should cleanup stale collectors after samples are removed", func() {
+			// tweakedConfig doesn't have probability used by existing collector
+			tweakedConfig := &collectorConfig{
+				collectorSetID: DefaultObservabilityCollectorSetID,
+				featuresProbability: map[libovsdbops.SampleFeature]int{
+					libovsdbops.EgressFirewallSample: 50,
+				},
+			}
+			acl := &nbdb.ACL{
+				UUID: "acl-uuid",
+				ExternalIDs: map[string]string{
+					// NetworkPolicy is enabled by default
+					libovsdbops.OwnerTypeKey.String(): libovsdbops.NetworkPolicyOwnerType,
+				},
+			}
+			pg := &nbdb.PortGroup{
+				UUID: "pg-uuid",
+				ACLs: []string{acl.UUID},
+			}
+			sample := &nbdb.Sample{
+				UUID:       "sample-uuid",
+				Metadata:   int(libovsdbops.GetACLSampleID(acl)),
+				Collectors: []string{collectorUUID},
+			}
+			acl.SampleNew = &sample.UUID
+			acl.SampleEst = &sample.UUID
+			testInitialDB := append(initialDB, sample, pg, acl)
+
+			startManagerWithConfig(testInitialDB, tweakedConfig)
+			newCollector := &nbdb.SampleCollector{
+				UUID:        collectorUUID + "-2",
+				ID:          2,
+				SetID:       DefaultObservabilityCollectorSetID,
+				Probability: 32767,
+				ExternalIDs: map[string]string{
+					collectorFeaturesExternalID: libovsdbops.EgressFirewallSample,
+				},
+			}
+			// initial collector will fail to be cleaned up, since acl sample still references that collector
+			expectedDB := append(testInitialDB, newCollector)
+			Consistently(nbClient).Should(libovsdbtest.HaveData(expectedDB))
+			// now imitate netpol handler initialization by updating acl sample.
+			err := createOrUpdateACLPreserveUUID(nbClient, manager.SamplingConfig(), acl)
+			Expect(err).NotTo(HaveOccurred())
+			// sample is removed, collector should be cleaned up now
+			expectedDB = append(samplingApps, pg, acl, newCollector)
+			Eventually(nbClient, 2*manager.unusedCollectorsRetryInterval).Should(libovsdbtest.HaveData(expectedDB))
+		})
+	})
+})
diff --git a/go-controller/pkg/ovn/base_network_controller.go b/go-controller/pkg/ovn/base_network_controller.go
index 98d70cdbe6f..f0a9a2651f4 100644
--- a/go-controller/pkg/ovn/base_network_controller.go
+++ b/go-controller/pkg/ovn/base_network_controller.go
@@ -17,6 +17,7 @@ import (
 	libovsdbutil "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/util"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/metrics"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/observability"
 	addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set"
 	lsm "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/logical_switch_manager"
 	zoneic "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/zone_interconnect"
@@ -167,6 +168,8 @@ type BaseNetworkController struct {
 	// IP addresses of OVN Cluster logical router port ("GwRouterToJoinSwitchPrefix + OVNClusterRouter")
 	// connecting to the join switch
 	ovnClusterLRPToJoinIfAddrs []*net.IPNet
+
+	observManager *observability.Manager
 }
 
 // BaseSecondaryNetworkController structure holds per-network fields and network specific
@@ -935,3 +938,10 @@ func initLoadBalancerGroups(nbClient libovsdbclient.Client, netInfo util.NetInfo
 
 	return
 }
+
+func (bnc *BaseNetworkController) GetSamplingConfig() *libovsdbops.SamplingConfig {
+	if bnc.observManager != nil {
+		return bnc.observManager.SamplingConfig()
+	}
+	return nil
+}
diff --git a/go-controller/pkg/ovn/base_network_controller_multicast.go b/go-controller/pkg/ovn/base_network_controller_multicast.go
index 74d23267322..8fa0993de79 100644
--- a/go-controller/pkg/ovn/base_network_controller_multicast.go
+++ b/go-controller/pkg/ovn/base_network_controller_multicast.go
@@ -128,7 +128,7 @@ func (bnc *BaseNetworkController) createMulticastAllowPolicy(ns string, nsInfo *
 	ingressACL := libovsdbutil.BuildACL(dbIDs, types.DefaultMcastAllowPriority, ingressMatch, nbdb.ACLActionAllow, nil, aclPipeline)
 
 	acls := []*nbdb.ACL{egressACL, ingressACL}
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, acls...)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), acls...)
 	if err != nil {
 		return err
 	}
@@ -189,7 +189,7 @@ func (bnc *BaseNetworkController) createDefaultDenyMulticastPolicy() error {
 		acl := libovsdbutil.BuildACL(dbIDs, types.DefaultMcastDenyPriority, match, nbdb.ACLActionDrop, nil, aclPipeline)
 		acls = append(acls, acl)
 	}
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, acls...)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), acls...)
 	if err != nil {
 		return err
 	}
@@ -232,7 +232,7 @@ func (bnc *BaseNetworkController) createDefaultAllowMulticastPolicy() error {
 		acls = append(acls, acl)
 	}
 
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, acls...)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), acls...)
 	if err != nil {
 		return err
 	}
diff --git a/go-controller/pkg/ovn/base_network_controller_policy.go b/go-controller/pkg/ovn/base_network_controller_policy.go
index f0f59577dd9..3a1a9604da7 100644
--- a/go-controller/pkg/ovn/base_network_controller_policy.go
+++ b/go-controller/pkg/ovn/base_network_controller_policy.go
@@ -260,7 +260,7 @@ func (bnc *BaseNetworkController) addAllowACLFromNode(switchName string, mgmtPor
 	nodeACL := libovsdbutil.BuildACL(dbIDs, types.DefaultAllowPriority, match,
 		nbdb.ACLActionAllowRelated, nil, libovsdbutil.LportIngress)
 
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, nodeACL)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), nodeACL)
 	if err != nil {
 		return fmt.Errorf("failed to create or update ACL %v: %v", nodeACL, err)
 	}
@@ -365,7 +365,7 @@ func (bnc *BaseNetworkController) createDefaultDenyPGAndACLs(namespace, policy s
 	egressPGIDs := bnc.getDefaultDenyPolicyPortGroupIDs(namespace, libovsdbutil.ACLEgress)
 	egressPGName := libovsdbutil.GetPortGroupName(egressPGIDs)
 	egressDenyACL, egressAllowACL := bnc.buildDenyACLs(namespace, egressPGName, aclLogging, libovsdbutil.ACLEgress)
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, ingressDenyACL, ingressAllowACL, egressDenyACL, egressAllowACL)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), ingressDenyACL, ingressAllowACL, egressDenyACL, egressAllowACL)
 	if err != nil {
 		return err
 	}
@@ -987,7 +987,7 @@ func (bnc *BaseNetworkController) createNetworkPolicy(policy *knet.NetworkPolicy
 		ops := []ovsdb.Operation{}
 
 		acls := bnc.buildNetworkPolicyACLs(np, aclLogging)
-		ops, err = libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, ops, acls...)
+		ops, err = libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, ops, bnc.GetSamplingConfig(), acls...)
 		if err != nil {
 			return fmt.Errorf("failed to create ACL ops: %v", err)
 		}
@@ -1392,7 +1392,7 @@ func (bnc *BaseNetworkController) peerNamespaceUpdate(np *networkPolicy, gp *gre
 	}
 	// buildLocalPodACLs is safe for concurrent use, see function comment for details
 	acls, deletedACLs := gp.buildLocalPodACLs(np.portGroupName, aclLogging)
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, acls...)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(bnc.nbClient, nil, bnc.GetSamplingConfig(), acls...)
 	if err != nil {
 		return err
 	}
diff --git a/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy.go b/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy.go
index 2e5d8213994..1f6571768be 100644
--- a/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy.go
+++ b/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy.go
@@ -571,7 +571,7 @@ func (c *Controller) createNewANP(desiredANPState *adminNetworkPolicyState, desi
 		return fmt.Errorf("failed to create address-sets, %v", err)
 	}
 	ops = append(ops, addrSetOps...)
-	ops, err = libovsdbops.CreateOrUpdateACLsOps(c.nbClient, ops, desiredACLs...)
+	ops, err = libovsdbops.CreateOrUpdateACLsOps(c.nbClient, ops, c.GetSamplingConfig(), desiredACLs...)
 	if err != nil {
 		return fmt.Errorf("failed to create ACL ops: %v", err)
 	}
@@ -672,7 +672,7 @@ func (c *Controller) updateExistingANP(currentANPState, desiredANPState *adminNe
 	if fullPeerRecompute || atLeastOneRuleUpdated || hasPriorityChanged || hasACLLoggingParamsChanged {
 		klog.V(3).Infof("ANP %s with priority %d was updated", desiredANPState.name, desiredANPState.anpPriority)
 		// now update the acls to the desired ones
-		ops, err = libovsdbops.CreateOrUpdateACLsOps(c.nbClient, ops, desiredACLs...)
+		ops, err = libovsdbops.CreateOrUpdateACLsOps(c.nbClient, ops, c.GetSamplingConfig(), desiredACLs...)
 		if err != nil {
 			return fmt.Errorf("failed to create new ACL ops for anp %s: %v", desiredANPState.name, err)
 		}
diff --git a/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy_controller.go b/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy_controller.go
index 1ee4e476202..ced8b30e884 100644
--- a/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy_controller.go
+++ b/go-controller/pkg/ovn/controller/admin_network_policy/admin_network_policy_controller.go
@@ -8,6 +8,8 @@ import (
 
 	libovsdbclient "github.com/ovn-org/libovsdb/client"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory"
+	libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/observability"
 	addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
 	v1 "k8s.io/api/core/v1"
@@ -95,6 +97,8 @@ type Controller struct {
 	anpNodeLister corev1listers.NodeLister
 	anpNodeSynced cache.InformerSynced
 	anpNodeQueue  workqueue.RateLimitingInterface
+
+	observManager *observability.Manager
 }
 
 // NewController returns a new *Controller.
@@ -110,7 +114,8 @@ func NewController(
 	addressSetFactory addressset.AddressSetFactory,
 	isPodScheduledinLocalZone func(*v1.Pod) bool,
 	zone string,
-	recorder record.EventRecorder) (*Controller, error) {
+	recorder record.EventRecorder,
+	observManager *observability.Manager) (*Controller, error) {
 
 	c := &Controller{
 		controllerName:            controllerName,
@@ -122,6 +127,7 @@ func NewController(
 		anpCache:                  make(map[string]*adminNetworkPolicyState),
 		anpPriorityMap:            make(map[int32]string),
 		banpCache:                 &adminNetworkPolicyState{}, // safe to initialise pointer to empty struct than nil
+		observManager:             observManager,
 	}
 
 	klog.V(5).Info("Setting up event handlers for Admin Network Policy")
@@ -588,3 +594,10 @@ func (c *Controller) onANPNodeDelete(obj interface{}) {
 	klog.V(5).Infof("Deleting Node Admin Network Policy %s", key)
 	c.anpNodeQueue.Add(key)
 }
+
+func (c *Controller) GetSamplingConfig() *libovsdbops.SamplingConfig {
+	if c.observManager != nil {
+		return c.observManager.SamplingConfig()
+	}
+	return nil
+}
diff --git a/go-controller/pkg/ovn/controller/admin_network_policy/status_test.go b/go-controller/pkg/ovn/controller/admin_network_policy/status_test.go
index 213a758451e..37b68ce6b97 100644
--- a/go-controller/pkg/ovn/controller/admin_network_policy/status_test.go
+++ b/go-controller/pkg/ovn/controller/admin_network_policy/status_test.go
@@ -127,6 +127,7 @@ func newANPControllerWithDBSetup(dbSetup libovsdbtest.TestSetup, initANPs anpapi
 		nil, // we don't care about pods in this test
 		"targaryen",
 		recorder,
+		nil,
 	)
 	gomega.Expect(err).ToNot(gomega.HaveOccurred())
 
diff --git a/go-controller/pkg/ovn/default_network_controller.go b/go-controller/pkg/ovn/default_network_controller.go
index 216e02d0760..4f2ab8b18b6 100644
--- a/go-controller/pkg/ovn/default_network_controller.go
+++ b/go-controller/pkg/ovn/default_network_controller.go
@@ -15,6 +15,7 @@ import (
 	egressqoslisters "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/egressqos/v1/apis/listers/egressqos/v1"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/metrics"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/observability"
 	addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set"
 	anpcontroller "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/controller/admin_network_policy"
 	apbroutecontroller "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/controller/apbroute"
@@ -142,15 +143,15 @@ type DefaultNetworkController struct {
 
 // NewDefaultNetworkController creates a new OVN controller for creating logical network
 // infrastructure and policy for default l3 network
-func NewDefaultNetworkController(cnci *CommonNetworkControllerInfo) (*DefaultNetworkController, error) {
+func NewDefaultNetworkController(cnci *CommonNetworkControllerInfo, observManager *observability.Manager) (*DefaultNetworkController, error) {
 	stopChan := make(chan struct{})
 	wg := &sync.WaitGroup{}
-	return newDefaultNetworkControllerCommon(cnci, stopChan, wg, nil)
+	return newDefaultNetworkControllerCommon(cnci, stopChan, wg, nil, observManager)
 }
 
 func newDefaultNetworkControllerCommon(cnci *CommonNetworkControllerInfo,
 	defaultStopChan chan struct{}, defaultWg *sync.WaitGroup,
-	addressSetFactory addressset.AddressSetFactory) (*DefaultNetworkController, error) {
+	addressSetFactory addressset.AddressSetFactory, observManager *observability.Manager) (*DefaultNetworkController, error) {
 
 	if addressSetFactory == nil {
 		addressSetFactory = addressset.NewOvnAddressSetFactory(cnci.nbClient, config.IPv4Mode, config.IPv6Mode)
@@ -214,6 +215,7 @@ func newDefaultNetworkControllerCommon(cnci *CommonNetworkControllerInfo,
 			localZoneNodes:              &sync.Map{},
 			zoneICHandler:               zoneICHandler,
 			cancelableCtx:               util.NewCancelableContext(),
+			observManager:               observManager,
 		},
 		externalGatewayRouteInfo: apbExternalRouteController.ExternalGWRouteInfoCache,
 		eIPC: egressIPZoneController{
diff --git a/go-controller/pkg/ovn/default_network_controller_policy.go b/go-controller/pkg/ovn/default_network_controller_policy.go
index d1f40491e57..97ab842d2f5 100644
--- a/go-controller/pkg/ovn/default_network_controller_policy.go
+++ b/go-controller/pkg/ovn/default_network_controller_policy.go
@@ -42,7 +42,7 @@ func (oc *DefaultNetworkController) addHairpinAllowACL() error {
 	egressACL := libovsdbutil.BuildACL(egressACLIDs, types.DefaultAllowPriority, match,
 		nbdb.ACLActionAllowRelated, nil, libovsdbutil.LportEgressAfterLB)
 
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, nil, ingressACL, egressACL)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, nil, nil, ingressACL, egressACL)
 	if err != nil {
 		return fmt.Errorf("failed to create or update hairpin allow ACL %v", err)
 	}
diff --git a/go-controller/pkg/ovn/egressfirewall.go b/go-controller/pkg/ovn/egressfirewall.go
index ca271ac025a..a78506c8501 100644
--- a/go-controller/pkg/ovn/egressfirewall.go
+++ b/go-controller/pkg/ovn/egressfirewall.go
@@ -476,7 +476,7 @@ func (oc *DefaultNetworkController) createEgressFirewallACLOps(ops []libovsdb.Op
 		libovsdbutil.LportIngress,
 	)
 	var err error
-	ops, err = libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, ops, egressFirewallACL)
+	ops, err = libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, ops, oc.GetSamplingConfig(), egressFirewallACL)
 	if err != nil {
 		return ops, fmt.Errorf("failed to create egressFirewall ACL %v: %v", egressFirewallACL, err)
 	}
diff --git a/go-controller/pkg/ovn/external_ids_syncer/acl/acl_sync.go b/go-controller/pkg/ovn/external_ids_syncer/acl/acl_sync.go
index 861b0e5d46c..2b2413bac19 100644
--- a/go-controller/pkg/ovn/external_ids_syncer/acl/acl_sync.go
+++ b/go-controller/pkg/ovn/external_ids_syncer/acl/acl_sync.go
@@ -131,7 +131,7 @@ func (syncer *ACLSyncer) SyncACLs(existingNodes []*v1.Node) error {
 
 		// update acls with new ExternalIDs
 		err = batching.Batch[*nbdb.ACL](syncer.txnBatchSize, uniquePrimaryIDACLs, func(batchACLs []*nbdb.ACL) error {
-			return libovsdbops.CreateOrUpdateACLs(syncer.nbClient, batchACLs...)
+			return libovsdbops.CreateOrUpdateACLs(syncer.nbClient, nil, batchACLs...)
 		})
 		if err != nil {
 			return fmt.Errorf("cannot update stale ACLs: %v", err)
@@ -183,7 +183,7 @@ func (syncer *ACLSyncer) SyncACLs(existingNodes []*v1.Node) error {
 		}
 		// batch ACLs together in order of their priority: lowest first and then highest
 		err = batching.Batch[*nbdb.ACL](syncer.txnBatchSize, aclsInTier0, func(batchACLs []*nbdb.ACL) error {
-			return libovsdbops.CreateOrUpdateACLs(syncer.nbClient, batchACLs...)
+			return libovsdbops.CreateOrUpdateACLs(syncer.nbClient, nil, batchACLs...)
 		})
 		if err != nil {
 			return fmt.Errorf("cannot update ACLs to tier2: %v", err)
diff --git a/go-controller/pkg/ovn/ovn.go b/go-controller/pkg/ovn/ovn.go
index 76494cadf0c..d7dd870ee4d 100644
--- a/go-controller/pkg/ovn/ovn.go
+++ b/go-controller/pkg/ovn/ovn.go
@@ -500,6 +500,7 @@ func (oc *DefaultNetworkController) newANPController() error {
 		oc.isPodScheduledinLocalZone,
 		oc.zone,
 		oc.recorder,
+		oc.observManager,
 	)
 	return err
 }
diff --git a/go-controller/pkg/ovn/ovn_test.go b/go-controller/pkg/ovn/ovn_test.go
index 77df121bdfd..93cd2ab07e1 100644
--- a/go-controller/pkg/ovn/ovn_test.go
+++ b/go-controller/pkg/ovn/ovn_test.go
@@ -321,7 +321,7 @@ func NewOvnController(ovnClient *util.OVNMasterClientset, wf *factory.WatchFacto
 		return nil, err
 	}
 
-	dnc, err := newDefaultNetworkControllerCommon(cnci, stopChan, wg, addressSetFactory)
+	dnc, err := newDefaultNetworkControllerCommon(cnci, stopChan, wg, addressSetFactory, nil)
 	gomega.Expect(err).NotTo(gomega.HaveOccurred())
 
 	if nbZoneFailed {
diff --git a/go-controller/pkg/ovn/udn_isolation.go b/go-controller/pkg/ovn/udn_isolation.go
index 6fcd2b9f91d..0cf36e34287 100644
--- a/go-controller/pkg/ovn/udn_isolation.go
+++ b/go-controller/pkg/ovn/udn_isolation.go
@@ -108,7 +108,7 @@ func (oc *DefaultNetworkController) setupUDNACLs(mgmtPortIPs []net.IP) error {
 	match = libovsdbutil.GetACLMatch(pgName, match, libovsdbutil.ACLIngress)
 	ingressAllowACL := libovsdbutil.BuildACL(ingressAllowIDs, types.PrimaryUDNAllowPriority, match, nbdb.ACLActionAllowRelated, nil, libovsdbutil.LportIngress)
 
-	ops, err := libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, nil, egressDenyACL, egressARPACL, ingressARPACL, ingressDenyACL, ingressAllowACL)
+	ops, err := libovsdbops.CreateOrUpdateACLsOps(oc.nbClient, nil, oc.GetSamplingConfig(), egressDenyACL, egressARPACL, ingressARPACL, ingressDenyACL, ingressAllowACL)
 	if err != nil {
 		return fmt.Errorf("failed to create or update UDN ACLs: %v", err)
 	}

From a5d5072d0597dd0d716811469a82adcbedd8c46e Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Tue, 20 Aug 2024 22:30:13 +0200
Subject: [PATCH 07/48] Update libovsdb to the latest version. It allows using
 `*string` as a client index.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 go-controller/go.mod                                         | 2 +-
 go-controller/go.sum                                         | 2 ++
 .../vendor/github.com/ovn-org/libovsdb/cache/cache.go        | 5 +++++
 go-controller/vendor/modules.txt                             | 2 +-
 4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/go-controller/go.mod b/go-controller/go.mod
index 80fe78c8931..c7521ac77f1 100644
--- a/go-controller/go.mod
+++ b/go-controller/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/onsi/gomega v1.32.0
 	github.com/openshift/api v0.0.0-20231120222239-b86761094ee3
 	github.com/openshift/client-go v0.0.0-20231121143148-910ca30a1a9a
-	github.com/ovn-org/libovsdb v0.6.1-0.20240125124854-03f787b1a892
+	github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20
 	github.com/prometheus/client_golang v1.18.0
 	github.com/prometheus/client_model v0.5.0
 	github.com/safchain/ethtool v0.3.1-0.20231027162144-83e5e0097c91
diff --git a/go-controller/go.sum b/go-controller/go.sum
index ecd57107844..29eb1042665 100644
--- a/go-controller/go.sum
+++ b/go-controller/go.sum
@@ -645,6 +645,8 @@ github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPf
 github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA=
 github.com/ovn-org/libovsdb v0.6.1-0.20240125124854-03f787b1a892 h1:/yg3/z+RH+iDLMxp6FTnmlk5bStK542/Rge5EBjnA9A=
 github.com/ovn-org/libovsdb v0.6.1-0.20240125124854-03f787b1a892/go.mod h1:LC5DOvcY58jOG3HTvDyCVidoMJDurPeu+xlxv5Krd9Q=
+github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20 h1:OoDvzyaK7F/ZANIIFOgb4Haj7mye3Hle0fYZZNdidSs=
+github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20/go.mod h1:dJbxEaalQl83nn904K32FaMjlH/qOObZ0bj4ejQ78AI=
 github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
diff --git a/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go b/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go
index 60182071af3..0b1e09e721c 100644
--- a/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go
+++ b/go-controller/vendor/github.com/ovn-org/libovsdb/cache/cache.go
@@ -1261,6 +1261,11 @@ func valueFromColumnKey(info *mapper.Info, columnKey model.ColumnKey) (interface
 			return "", fmt.Errorf("can't get key value from map: %v", err)
 		}
 	}
+	// if the value is a non-nil pointer of an optional, dereference
+	v := reflect.ValueOf(val)
+	if v.Kind() == reflect.Ptr && !v.IsNil() {
+		val = v.Elem().Interface()
+	}
 	return val, err
 }
 
diff --git a/go-controller/vendor/modules.txt b/go-controller/vendor/modules.txt
index 5729c308ad8..6af1617d6b8 100644
--- a/go-controller/vendor/modules.txt
+++ b/go-controller/vendor/modules.txt
@@ -358,7 +358,7 @@ github.com/openshift/client-go/network/listers/network/v1alpha1
 # github.com/openshift/custom-resource-status v1.1.2
 ## explicit; go 1.12
 github.com/openshift/custom-resource-status/conditions/v1
-# github.com/ovn-org/libovsdb v0.6.1-0.20240125124854-03f787b1a892
+# github.com/ovn-org/libovsdb v0.7.1-0.20240820095311-ce1951614a20
 ## explicit; go 1.18
 github.com/ovn-org/libovsdb/cache
 github.com/ovn-org/libovsdb/client

From 29ec673f5b3e287b400fdbe40bc791a6696c7831 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Fri, 2 Aug 2024 13:47:30 +0200
Subject: [PATCH 08/48] Add observability library and ovnkube-observ binary to
 run debug mode.

Use freshly-baked libovsdb functionality to index ACLs by sample_new
and sample_est. This allows to avoid expensive predicate search.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 dist/images/Dockerfile.fedora                 |     4 +-
 dist/images/Dockerfile.fedora.dev             |     1 +
 dist/images/Dockerfile.ubuntu                 |     2 +-
 go-controller/Makefile                        |     2 +-
 .../cmd/ovnkube-observ/ovnkubeobserv.go       |    41 +
 go-controller/go.mod                          |     1 +
 go-controller/go.sum                          |     2 +
 go-controller/hack/update-modelgen.sh         |     2 +-
 .../observability-lib/ovsdb/.gitignore        |     1 +
 .../observability-lib/ovsdb/bridge.go         |   570 +
 .../ovsdb/flow_sample_collector_set.go        |   143 +
 go-controller/observability-lib/ovsdb/gen.go  |     3 +
 .../observability-lib/ovsdb/observ_model.go   |    11 +
 .../observability-lib/parse_sample.go         |   243 +
 .../sampledecoder/db_client.go                |   118 +
 .../sampledecoder/sample_decoder.go           |   290 +
 .../github.com/google/gopacket/.gitignore     |    38 +
 .../google/gopacket/.travis.gofmt.sh          |     7 +
 .../google/gopacket/.travis.golint.sh         |    28 +
 .../google/gopacket/.travis.govet.sh          |    10 +
 .../google/gopacket/.travis.install.sh        |     9 +
 .../google/gopacket/.travis.script.sh         |    10 +
 .../github.com/google/gopacket/.travis.yml    |    57 +
 .../vendor/github.com/google/gopacket/AUTHORS |    54 +
 .../google/gopacket/CONTRIBUTING.md           |   215 +
 .../vendor/github.com/google/gopacket/LICENSE |    28 +
 .../github.com/google/gopacket/README.md      |    12 +
 .../vendor/github.com/google/gopacket/base.go |   178 +
 .../github.com/google/gopacket/decode.go      |   157 +
 .../vendor/github.com/google/gopacket/doc.go  |   432 +
 .../github.com/google/gopacket/flows.go       |   236 +
 .../vendor/github.com/google/gopacket/gc      |   288 +
 .../github.com/google/gopacket/layerclass.go  |   107 +
 .../google/gopacket/layers/.lint_blacklist    |    39 +
 .../github.com/google/gopacket/layers/arp.go  |   118 +
 .../github.com/google/gopacket/layers/asf.go  |   166 +
 .../gopacket/layers/asf_presencepong.go       |   194 +
 .../github.com/google/gopacket/layers/base.go |    52 +
 .../github.com/google/gopacket/layers/bfd.go  |   481 +
 .../github.com/google/gopacket/layers/cdp.go  |   659 +
 .../github.com/google/gopacket/layers/ctp.go  |   109 +
 .../google/gopacket/layers/dhcpv4.go          |   592 +
 .../google/gopacket/layers/dhcpv6.go          |   360 +
 .../google/gopacket/layers/dhcpv6_options.go  |   621 +
 .../github.com/google/gopacket/layers/dns.go  |  1098 ++
 .../github.com/google/gopacket/layers/doc.go  |    61 +
 .../google/gopacket/layers/dot11.go           |  2118 +++
 .../google/gopacket/layers/dot1q.go           |    75 +
 .../github.com/google/gopacket/layers/eap.go  |   114 +
 .../google/gopacket/layers/eapol.go           |   302 +
 .../google/gopacket/layers/endpoints.go       |    97 +
 .../google/gopacket/layers/enums.go           |   443 +
 .../google/gopacket/layers/enums_generated.go |   434 +
 .../google/gopacket/layers/erspan2.go         |    86 +
 .../google/gopacket/layers/etherip.go         |    45 +
 .../google/gopacket/layers/ethernet.go        |   123 +
 .../github.com/google/gopacket/layers/fddi.go |    41 +
 .../google/gopacket/layers/fuzz_layer.go      |    39 +
 .../google/gopacket/layers/gen_linted.sh      |     3 +
 .../google/gopacket/layers/geneve.go          |   121 +
 .../github.com/google/gopacket/layers/gre.go  |   200 +
 .../github.com/google/gopacket/layers/gtp.go  |   184 +
 .../google/gopacket/layers/iana_ports.go      | 11351 ++++++++++++++++
 .../google/gopacket/layers/icmp4.go           |   267 +
 .../google/gopacket/layers/icmp6.go           |   266 +
 .../google/gopacket/layers/icmp6msg.go        |   578 +
 .../github.com/google/gopacket/layers/igmp.go |   355 +
 .../github.com/google/gopacket/layers/ip4.go  |   325 +
 .../github.com/google/gopacket/layers/ip6.go  |   722 +
 .../google/gopacket/layers/ipsec.go           |    77 +
 .../google/gopacket/layers/layertypes.go      |   223 +
 .../github.com/google/gopacket/layers/lcm.go  |   218 +
 .../google/gopacket/layers/linux_sll.go       |    98 +
 .../github.com/google/gopacket/layers/llc.go  |   193 +
 .../github.com/google/gopacket/layers/lldp.go |  1603 +++
 .../google/gopacket/layers/loopback.go        |    80 +
 .../google/gopacket/layers/mldv1.go           |   182 +
 .../google/gopacket/layers/mldv2.go           |   619 +
 .../google/gopacket/layers/modbustcp.go       |   150 +
 .../github.com/google/gopacket/layers/mpls.go |    87 +
 .../github.com/google/gopacket/layers/ndp.go  |   611 +
 .../github.com/google/gopacket/layers/ntp.go  |   416 +
 .../github.com/google/gopacket/layers/ospf.go |   715 +
 .../google/gopacket/layers/pflog.go           |    84 +
 .../google/gopacket/layers/ports.go           |   156 +
 .../github.com/google/gopacket/layers/ppp.go  |    88 +
 .../google/gopacket/layers/pppoe.go           |    60 +
 .../google/gopacket/layers/prism.go           |   146 +
 .../google/gopacket/layers/radiotap.go        |  1076 ++
 .../google/gopacket/layers/radius.go          |   560 +
 .../github.com/google/gopacket/layers/rmcp.go |   170 +
 .../github.com/google/gopacket/layers/rudp.go |    93 +
 .../github.com/google/gopacket/layers/sctp.go |   746 +
 .../google/gopacket/layers/sflow.go           |  2567 ++++
 .../github.com/google/gopacket/layers/sip.go  |   542 +
 .../github.com/google/gopacket/layers/stp.go  |    27 +
 .../github.com/google/gopacket/layers/tcp.go  |   341 +
 .../google/gopacket/layers/tcpip.go           |   104 +
 .../google/gopacket/layers/test_creator.py    |   103 +
 .../github.com/google/gopacket/layers/tls.go  |   283 +
 .../google/gopacket/layers/tls_alert.go       |   165 +
 .../google/gopacket/layers/tls_appdata.go     |    34 +
 .../google/gopacket/layers/tls_cipherspec.go  |    64 +
 .../google/gopacket/layers/tls_handshake.go   |    28 +
 .../github.com/google/gopacket/layers/udp.go  |   133 +
 .../google/gopacket/layers/udplite.go         |    44 +
 .../github.com/google/gopacket/layers/usb.go  |   292 +
 .../github.com/google/gopacket/layers/vrrp.go |   156 +
 .../google/gopacket/layers/vxlan.go           |   123 +
 .../google/gopacket/layers_decoder.go         |   101 +
 .../github.com/google/gopacket/layertype.go   |   111 +
 .../github.com/google/gopacket/packet.go      |   864 ++
 .../github.com/google/gopacket/parser.go      |   350 +
 .../vendor/github.com/google/gopacket/time.go |    72 +
 .../github.com/google/gopacket/writer.go      |   232 +
 go-controller/vendor/modules.txt              |     4 +
 116 files changed, 41325 insertions(+), 5 deletions(-)
 create mode 100644 go-controller/cmd/ovnkube-observ/ovnkubeobserv.go
 create mode 100644 go-controller/observability-lib/ovsdb/.gitignore
 create mode 100644 go-controller/observability-lib/ovsdb/bridge.go
 create mode 100644 go-controller/observability-lib/ovsdb/flow_sample_collector_set.go
 create mode 100644 go-controller/observability-lib/ovsdb/gen.go
 create mode 100644 go-controller/observability-lib/ovsdb/observ_model.go
 create mode 100644 go-controller/observability-lib/parse_sample.go
 create mode 100644 go-controller/observability-lib/sampledecoder/db_client.go
 create mode 100644 go-controller/observability-lib/sampledecoder/sample_decoder.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.gitignore
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.gofmt.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.golint.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.govet.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.install.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.script.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/.travis.yml
 create mode 100644 go-controller/vendor/github.com/google/gopacket/AUTHORS
 create mode 100644 go-controller/vendor/github.com/google/gopacket/CONTRIBUTING.md
 create mode 100644 go-controller/vendor/github.com/google/gopacket/LICENSE
 create mode 100644 go-controller/vendor/github.com/google/gopacket/README.md
 create mode 100644 go-controller/vendor/github.com/google/gopacket/base.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/decode.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/doc.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/flows.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/gc
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layerclass.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/.lint_blacklist
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/arp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/asf.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/asf_presencepong.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/base.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/bfd.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/cdp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ctp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dhcpv4.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dhcpv6.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dhcpv6_options.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dns.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/doc.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dot11.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/dot1q.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/eap.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/eapol.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/endpoints.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/enums.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/enums_generated.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/erspan2.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/etherip.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ethernet.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/fddi.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/fuzz_layer.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/gen_linted.sh
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/geneve.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/gre.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/gtp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/iana_ports.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/icmp4.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/icmp6.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/icmp6msg.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/igmp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ip4.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ip6.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ipsec.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/layertypes.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/lcm.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/linux_sll.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/llc.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/lldp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/loopback.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/mldv1.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/mldv2.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/modbustcp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/mpls.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ndp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ntp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ospf.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/pflog.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ports.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/ppp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/pppoe.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/prism.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/radiotap.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/radius.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/rmcp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/rudp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/sctp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/sflow.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/sip.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/stp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tcp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tcpip.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/test_creator.py
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tls.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tls_alert.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tls_appdata.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tls_cipherspec.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/tls_handshake.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/udp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/udplite.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/usb.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/vrrp.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers/vxlan.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layers_decoder.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/layertype.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/packet.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/parser.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/time.go
 create mode 100644 go-controller/vendor/github.com/google/gopacket/writer.go

diff --git a/dist/images/Dockerfile.fedora b/dist/images/Dockerfile.fedora
index efdfb4d57d7..37c68a13ba9 100644
--- a/dist/images/Dockerfile.fedora
+++ b/dist/images/Dockerfile.fedora
@@ -37,14 +37,14 @@ RUN ln -s /usr/bin/python3 /usr/libexec/platform-python
 RUN mkdir -p /var/run/openvswitch
 
 RUN if [ "$TARGETPLATFORM" = "linux/amd64" ] || [ -z "$TARGETPLATFORM"] ; then koji download-build $ovnver --arch=x86_64  ; \
-    else koji download-build $ovnver --arch=aarch64 ; fi 
+    else koji download-build $ovnver --arch=aarch64 ; fi
 
 RUN rpm -Uhv --nodeps --force *.rpm
 
 # Built in ../../go_controller, then the binaries are copied here.
 # put things where they are in the pkg
 RUN mkdir -p /usr/libexec/cni/
-COPY ovnkube ovn-kube-util ovndbchecker hybrid-overlay-node ovnkube-identity /usr/bin/
+COPY ovnkube ovn-kube-util ovndbchecker hybrid-overlay-node ovnkube-identity ovnkube-observ /usr/bin/
 COPY ovn-k8s-cni-overlay /usr/libexec/cni/ovn-k8s-cni-overlay
 
 # ovnkube.sh is the entry point. This script examines environment
diff --git a/dist/images/Dockerfile.fedora.dev b/dist/images/Dockerfile.fedora.dev
index 6e9ec9c71f0..f9bd16ee70e 100644
--- a/dist/images/Dockerfile.fedora.dev
+++ b/dist/images/Dockerfile.fedora.dev
@@ -83,6 +83,7 @@ COPY ovn-kube-util /usr/bin/
 COPY ovndbchecker /usr/bin/
 COPY hybrid-overlay-node /usr/bin
 COPY ovnkube-identity /usr/bin/
+COPY ovnkube-observ /usr/bin
 COPY ovn-k8s-cni-overlay /usr/libexec/cni/ovn-k8s-cni-overlay
 
 # ovnkube.sh is the entry point. This script examines environment
diff --git a/dist/images/Dockerfile.ubuntu b/dist/images/Dockerfile.ubuntu
index 684ce2c0406..d7f1a26f226 100644
--- a/dist/images/Dockerfile.ubuntu
+++ b/dist/images/Dockerfile.ubuntu
@@ -27,7 +27,7 @@ RUN mkdir -p /var/run/openvswitch
 # Built in ../../go_controller, then the binaries are copied here.
 # put things where they are in the pkg
 RUN mkdir -p /usr/libexec/cni/
-COPY ovnkube ovn-kube-util ovndbchecker hybrid-overlay-node ovnkube-identity /usr/bin/
+COPY ovnkube ovn-kube-util ovndbchecker hybrid-overlay-node ovnkube-identity ovnkube-observ /usr/bin/
 COPY ovn-k8s-cni-overlay /usr/libexec/cni/ovn-k8s-cni-overlay
 
 # ovnkube.sh is the entry point. This script examines environment
diff --git a/go-controller/Makefile b/go-controller/Makefile
index 5d73ccd4c2d..465c1dd488d 100644
--- a/go-controller/Makefile
+++ b/go-controller/Makefile
@@ -50,7 +50,7 @@ export NOROOT
 #       (disables symbol table and DWARF generation when building ovnk binaries)
 
 all build:
-	hack/build-go.sh cmd/ovnkube cmd/ovn-k8s-cni-overlay cmd/ovn-kube-util hybrid-overlay/cmd/hybrid-overlay-node cmd/ovndbchecker cmd/ovnkube-trace cmd/ovnkube-identity
+	hack/build-go.sh cmd/ovnkube cmd/ovn-k8s-cni-overlay cmd/ovn-kube-util hybrid-overlay/cmd/hybrid-overlay-node cmd/ovndbchecker cmd/ovnkube-trace cmd/ovnkube-identity cmd/ovnkube-observ
 
 windows:
 	WINDOWS_BUILD="yes" hack/build-go.sh hybrid-overlay/cmd/hybrid-overlay-node
diff --git a/go-controller/cmd/ovnkube-observ/ovnkubeobserv.go b/go-controller/cmd/ovnkube-observ/ovnkubeobserv.go
new file mode 100644
index 00000000000..1c55dcee0c6
--- /dev/null
+++ b/go-controller/cmd/ovnkube-observ/ovnkubeobserv.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"os"
+	"os/signal"
+	"syscall"
+
+	observ "github.com/ovn-org/ovn-kubernetes/go-controller/observability-lib"
+)
+
+func main() {
+	ctx, cancel := context.WithCancel(context.Background())
+	sigc := make(chan os.Signal, 1)
+	signal.Notify(sigc,
+		syscall.SIGHUP,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+		syscall.SIGQUIT)
+	go func() {
+		<-sigc
+		fmt.Println("Received a signal, terminating.")
+		cancel()
+	}()
+	enableDecoder := flag.Bool("enable-enrichment", true, "Enrich samples with nbdb data.")
+	logCookie := flag.Bool("log-cookie", false, "Print raw sample cookie with psample group_id.")
+	printPacket := flag.Bool("print-full-packet", false, "Print full received packet. When false, only src and dst ips are printed with every sample.")
+	addOVSCollector := flag.Bool("add-ovs-collector", false, "Add ovs collector to enable sampling. Use with caution. Make sure no one else is using observability.")
+	outputFile := flag.String("output-file", "", "Output file to write the samples to.")
+	filterSrcIP := flag.String("filter-src-ip", "", "Filter in only packets from a given source ip.")
+	filterDstIP := flag.String("filter-dst-ip", "", "Filter in only packets to a given destination ip.")
+	flag.Parse()
+
+	reader := observ.NewSampleReader(*enableDecoder, *logCookie, *printPacket, *addOVSCollector, *filterSrcIP, *filterDstIP, *outputFile)
+	err := reader.ReadSamples(ctx)
+	if err != nil {
+		fmt.Println(err.Error())
+	}
+}
diff --git a/go-controller/go.mod b/go-controller/go.mod
index c7521ac77f1..d041714d3c5 100644
--- a/go-controller/go.mod
+++ b/go-controller/go.mod
@@ -16,6 +16,7 @@ require (
 	github.com/go-logr/logr v1.4.1
 	github.com/go-logr/stdr v1.2.2
 	github.com/google/go-cmp v0.6.0
+	github.com/google/gopacket v1.1.19
 	github.com/google/uuid v1.6.0
 	github.com/gorilla/mux v1.8.0
 	github.com/k8snetworkplumbingwg/govdpa v0.1.5-0.20230926073613-07c1031aea47
diff --git a/go-controller/go.sum b/go-controller/go.sum
index 29eb1042665..116c2efab14 100644
--- a/go-controller/go.sum
+++ b/go-controller/go.sum
@@ -401,6 +401,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
+github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
 github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
diff --git a/go-controller/hack/update-modelgen.sh b/go-controller/hack/update-modelgen.sh
index 00f0156c480..c7a24cd2e75 100755
--- a/go-controller/hack/update-modelgen.sh
+++ b/go-controller/hack/update-modelgen.sh
@@ -9,7 +9,7 @@ if  ! ( command -v modelgen > /dev/null ); then
   builddir="$(mktemp -d)"
   cd "${builddir}"
   # ensure the hash value is not outdated, if wrong bindings are being generated re-install modelgen
-  GO111MODULE=on go install github.com/ovn-org/libovsdb/cmd/modelgen@03f787b1a8922c112936f4f4d1d75db04967d1be
+  GO111MODULE=on go install github.com/ovn-org/libovsdb/cmd/modelgen@v0.7.0
   cd "${olddir}"
   if [[ "${builddir}" == /tmp/* ]]; then #paranoia
       rm -rf "${builddir}"
diff --git a/go-controller/observability-lib/ovsdb/.gitignore b/go-controller/observability-lib/ovsdb/.gitignore
new file mode 100644
index 00000000000..734ba1effcb
--- /dev/null
+++ b/go-controller/observability-lib/ovsdb/.gitignore
@@ -0,0 +1 @@
+*.ovsschema
diff --git a/go-controller/observability-lib/ovsdb/bridge.go b/go-controller/observability-lib/ovsdb/bridge.go
new file mode 100644
index 00000000000..d0135c4886a
--- /dev/null
+++ b/go-controller/observability-lib/ovsdb/bridge.go
@@ -0,0 +1,570 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package ovsdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const BridgeTable = "Bridge"
+
+type (
+	BridgeFailMode  = string
+	BridgeProtocols = string
+)
+
+var (
+	BridgeFailModeStandalone  BridgeFailMode  = "standalone"
+	BridgeFailModeSecure      BridgeFailMode  = "secure"
+	BridgeProtocolsOpenflow10 BridgeProtocols = "OpenFlow10"
+	BridgeProtocolsOpenflow11 BridgeProtocols = "OpenFlow11"
+	BridgeProtocolsOpenflow12 BridgeProtocols = "OpenFlow12"
+	BridgeProtocolsOpenflow13 BridgeProtocols = "OpenFlow13"
+	BridgeProtocolsOpenflow14 BridgeProtocols = "OpenFlow14"
+	BridgeProtocolsOpenflow15 BridgeProtocols = "OpenFlow15"
+)
+
+// Bridge defines an object in Bridge table
+type Bridge struct {
+	UUID                string            `ovsdb:"_uuid"`
+	AutoAttach          *string           `ovsdb:"auto_attach"`
+	Controller          []string          `ovsdb:"controller"`
+	DatapathID          *string           `ovsdb:"datapath_id"`
+	DatapathType        string            `ovsdb:"datapath_type"`
+	DatapathVersion     string            `ovsdb:"datapath_version"`
+	ExternalIDs         map[string]string `ovsdb:"external_ids"`
+	FailMode            *BridgeFailMode   `ovsdb:"fail_mode"`
+	FloodVLANs          []int             `ovsdb:"flood_vlans"`
+	FlowTables          map[int]string    `ovsdb:"flow_tables"`
+	IPFIX               *string           `ovsdb:"ipfix"`
+	McastSnoopingEnable bool              `ovsdb:"mcast_snooping_enable"`
+	Mirrors             []string          `ovsdb:"mirrors"`
+	Name                string            `ovsdb:"name"`
+	Netflow             *string           `ovsdb:"netflow"`
+	OtherConfig         map[string]string `ovsdb:"other_config"`
+	Ports               []string          `ovsdb:"ports"`
+	Protocols           []BridgeProtocols `ovsdb:"protocols"`
+	RSTPEnable          bool              `ovsdb:"rstp_enable"`
+	RSTPStatus          map[string]string `ovsdb:"rstp_status"`
+	Sflow               *string           `ovsdb:"sflow"`
+	Status              map[string]string `ovsdb:"status"`
+	STPEnable           bool              `ovsdb:"stp_enable"`
+}
+
+func (a *Bridge) GetUUID() string {
+	return a.UUID
+}
+
+func (a *Bridge) GetAutoAttach() *string {
+	return a.AutoAttach
+}
+
+func copyBridgeAutoAttach(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeAutoAttach(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetController() []string {
+	return a.Controller
+}
+
+func copyBridgeController(a []string) []string {
+	if a == nil {
+		return nil
+	}
+	b := make([]string, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalBridgeController(a, b []string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetDatapathID() *string {
+	return a.DatapathID
+}
+
+func copyBridgeDatapathID(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeDatapathID(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetDatapathType() string {
+	return a.DatapathType
+}
+
+func (a *Bridge) GetDatapathVersion() string {
+	return a.DatapathVersion
+}
+
+func (a *Bridge) GetExternalIDs() map[string]string {
+	return a.ExternalIDs
+}
+
+func copyBridgeExternalIDs(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalBridgeExternalIDs(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetFailMode() *BridgeFailMode {
+	return a.FailMode
+}
+
+func copyBridgeFailMode(a *BridgeFailMode) *BridgeFailMode {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeFailMode(a, b *BridgeFailMode) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetFloodVLANs() []int {
+	return a.FloodVLANs
+}
+
+func copyBridgeFloodVLANs(a []int) []int {
+	if a == nil {
+		return nil
+	}
+	b := make([]int, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalBridgeFloodVLANs(a, b []int) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetFlowTables() map[int]string {
+	return a.FlowTables
+}
+
+func copyBridgeFlowTables(a map[int]string) map[int]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[int]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalBridgeFlowTables(a, b map[int]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetIPFIX() *string {
+	return a.IPFIX
+}
+
+func copyBridgeIPFIX(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeIPFIX(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetMcastSnoopingEnable() bool {
+	return a.McastSnoopingEnable
+}
+
+func (a *Bridge) GetMirrors() []string {
+	return a.Mirrors
+}
+
+func copyBridgeMirrors(a []string) []string {
+	if a == nil {
+		return nil
+	}
+	b := make([]string, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalBridgeMirrors(a, b []string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetName() string {
+	return a.Name
+}
+
+func (a *Bridge) GetNetflow() *string {
+	return a.Netflow
+}
+
+func copyBridgeNetflow(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeNetflow(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetOtherConfig() map[string]string {
+	return a.OtherConfig
+}
+
+func copyBridgeOtherConfig(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalBridgeOtherConfig(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetPorts() []string {
+	return a.Ports
+}
+
+func copyBridgePorts(a []string) []string {
+	if a == nil {
+		return nil
+	}
+	b := make([]string, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalBridgePorts(a, b []string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetProtocols() []BridgeProtocols {
+	return a.Protocols
+}
+
+func copyBridgeProtocols(a []BridgeProtocols) []BridgeProtocols {
+	if a == nil {
+		return nil
+	}
+	b := make([]BridgeProtocols, len(a))
+	copy(b, a)
+	return b
+}
+
+func equalBridgeProtocols(a, b []BridgeProtocols) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for i, v := range a {
+		if b[i] != v {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetRSTPEnable() bool {
+	return a.RSTPEnable
+}
+
+func (a *Bridge) GetRSTPStatus() map[string]string {
+	return a.RSTPStatus
+}
+
+func copyBridgeRSTPStatus(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalBridgeRSTPStatus(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetSflow() *string {
+	return a.Sflow
+}
+
+func copyBridgeSflow(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalBridgeSflow(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *Bridge) GetStatus() map[string]string {
+	return a.Status
+}
+
+func copyBridgeStatus(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalBridgeStatus(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *Bridge) GetSTPEnable() bool {
+	return a.STPEnable
+}
+
+func (a *Bridge) DeepCopyInto(b *Bridge) {
+	*b = *a
+	b.AutoAttach = copyBridgeAutoAttach(a.AutoAttach)
+	b.Controller = copyBridgeController(a.Controller)
+	b.DatapathID = copyBridgeDatapathID(a.DatapathID)
+	b.ExternalIDs = copyBridgeExternalIDs(a.ExternalIDs)
+	b.FailMode = copyBridgeFailMode(a.FailMode)
+	b.FloodVLANs = copyBridgeFloodVLANs(a.FloodVLANs)
+	b.FlowTables = copyBridgeFlowTables(a.FlowTables)
+	b.IPFIX = copyBridgeIPFIX(a.IPFIX)
+	b.Mirrors = copyBridgeMirrors(a.Mirrors)
+	b.Netflow = copyBridgeNetflow(a.Netflow)
+	b.OtherConfig = copyBridgeOtherConfig(a.OtherConfig)
+	b.Ports = copyBridgePorts(a.Ports)
+	b.Protocols = copyBridgeProtocols(a.Protocols)
+	b.RSTPStatus = copyBridgeRSTPStatus(a.RSTPStatus)
+	b.Sflow = copyBridgeSflow(a.Sflow)
+	b.Status = copyBridgeStatus(a.Status)
+}
+
+func (a *Bridge) DeepCopy() *Bridge {
+	b := new(Bridge)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *Bridge) CloneModelInto(b model.Model) {
+	c := b.(*Bridge)
+	a.DeepCopyInto(c)
+}
+
+func (a *Bridge) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *Bridge) Equals(b *Bridge) bool {
+	return a.UUID == b.UUID &&
+		equalBridgeAutoAttach(a.AutoAttach, b.AutoAttach) &&
+		equalBridgeController(a.Controller, b.Controller) &&
+		equalBridgeDatapathID(a.DatapathID, b.DatapathID) &&
+		a.DatapathType == b.DatapathType &&
+		a.DatapathVersion == b.DatapathVersion &&
+		equalBridgeExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		equalBridgeFailMode(a.FailMode, b.FailMode) &&
+		equalBridgeFloodVLANs(a.FloodVLANs, b.FloodVLANs) &&
+		equalBridgeFlowTables(a.FlowTables, b.FlowTables) &&
+		equalBridgeIPFIX(a.IPFIX, b.IPFIX) &&
+		a.McastSnoopingEnable == b.McastSnoopingEnable &&
+		equalBridgeMirrors(a.Mirrors, b.Mirrors) &&
+		a.Name == b.Name &&
+		equalBridgeNetflow(a.Netflow, b.Netflow) &&
+		equalBridgeOtherConfig(a.OtherConfig, b.OtherConfig) &&
+		equalBridgePorts(a.Ports, b.Ports) &&
+		equalBridgeProtocols(a.Protocols, b.Protocols) &&
+		a.RSTPEnable == b.RSTPEnable &&
+		equalBridgeRSTPStatus(a.RSTPStatus, b.RSTPStatus) &&
+		equalBridgeSflow(a.Sflow, b.Sflow) &&
+		equalBridgeStatus(a.Status, b.Status) &&
+		a.STPEnable == b.STPEnable
+}
+
+func (a *Bridge) EqualsModel(b model.Model) bool {
+	c := b.(*Bridge)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &Bridge{}
+var _ model.ComparableModel = &Bridge{}
diff --git a/go-controller/observability-lib/ovsdb/flow_sample_collector_set.go b/go-controller/observability-lib/ovsdb/flow_sample_collector_set.go
new file mode 100644
index 00000000000..57a26e805d9
--- /dev/null
+++ b/go-controller/observability-lib/ovsdb/flow_sample_collector_set.go
@@ -0,0 +1,143 @@
+// Code generated by "libovsdb.modelgen"
+// DO NOT EDIT.
+
+package ovsdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+const FlowSampleCollectorSetTable = "Flow_Sample_Collector_Set"
+
+// FlowSampleCollectorSet defines an object in Flow_Sample_Collector_Set table
+type FlowSampleCollectorSet struct {
+	UUID         string            `ovsdb:"_uuid"`
+	Bridge       string            `ovsdb:"bridge"`
+	ExternalIDs  map[string]string `ovsdb:"external_ids"`
+	ID           int               `ovsdb:"id"`
+	IPFIX        *string           `ovsdb:"ipfix"`
+	LocalGroupID *int              `ovsdb:"local_group_id"`
+}
+
+func (a *FlowSampleCollectorSet) GetUUID() string {
+	return a.UUID
+}
+
+func (a *FlowSampleCollectorSet) GetBridge() string {
+	return a.Bridge
+}
+
+func (a *FlowSampleCollectorSet) GetExternalIDs() map[string]string {
+	return a.ExternalIDs
+}
+
+func copyFlowSampleCollectorSetExternalIDs(a map[string]string) map[string]string {
+	if a == nil {
+		return nil
+	}
+	b := make(map[string]string, len(a))
+	for k, v := range a {
+		b[k] = v
+	}
+	return b
+}
+
+func equalFlowSampleCollectorSetExternalIDs(a, b map[string]string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if len(a) != len(b) {
+		return false
+	}
+	for k, v := range a {
+		if w, ok := b[k]; !ok || v != w {
+			return false
+		}
+	}
+	return true
+}
+
+func (a *FlowSampleCollectorSet) GetID() int {
+	return a.ID
+}
+
+func (a *FlowSampleCollectorSet) GetIPFIX() *string {
+	return a.IPFIX
+}
+
+func copyFlowSampleCollectorSetIPFIX(a *string) *string {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalFlowSampleCollectorSetIPFIX(a, b *string) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *FlowSampleCollectorSet) GetLocalGroupID() *int {
+	return a.LocalGroupID
+}
+
+func copyFlowSampleCollectorSetLocalGroupID(a *int) *int {
+	if a == nil {
+		return nil
+	}
+	b := *a
+	return &b
+}
+
+func equalFlowSampleCollectorSetLocalGroupID(a, b *int) bool {
+	if (a == nil) != (b == nil) {
+		return false
+	}
+	if a == b {
+		return true
+	}
+	return *a == *b
+}
+
+func (a *FlowSampleCollectorSet) DeepCopyInto(b *FlowSampleCollectorSet) {
+	*b = *a
+	b.ExternalIDs = copyFlowSampleCollectorSetExternalIDs(a.ExternalIDs)
+	b.IPFIX = copyFlowSampleCollectorSetIPFIX(a.IPFIX)
+	b.LocalGroupID = copyFlowSampleCollectorSetLocalGroupID(a.LocalGroupID)
+}
+
+func (a *FlowSampleCollectorSet) DeepCopy() *FlowSampleCollectorSet {
+	b := new(FlowSampleCollectorSet)
+	a.DeepCopyInto(b)
+	return b
+}
+
+func (a *FlowSampleCollectorSet) CloneModelInto(b model.Model) {
+	c := b.(*FlowSampleCollectorSet)
+	a.DeepCopyInto(c)
+}
+
+func (a *FlowSampleCollectorSet) CloneModel() model.Model {
+	return a.DeepCopy()
+}
+
+func (a *FlowSampleCollectorSet) Equals(b *FlowSampleCollectorSet) bool {
+	return a.UUID == b.UUID &&
+		a.Bridge == b.Bridge &&
+		equalFlowSampleCollectorSetExternalIDs(a.ExternalIDs, b.ExternalIDs) &&
+		a.ID == b.ID &&
+		equalFlowSampleCollectorSetIPFIX(a.IPFIX, b.IPFIX) &&
+		equalFlowSampleCollectorSetLocalGroupID(a.LocalGroupID, b.LocalGroupID)
+}
+
+func (a *FlowSampleCollectorSet) EqualsModel(b model.Model) bool {
+	c := b.(*FlowSampleCollectorSet)
+	return a.Equals(c)
+}
+
+var _ model.CloneableModel = &FlowSampleCollectorSet{}
+var _ model.ComparableModel = &FlowSampleCollectorSet{}
diff --git a/go-controller/observability-lib/ovsdb/gen.go b/go-controller/observability-lib/ovsdb/gen.go
new file mode 100644
index 00000000000..c5aabca468b
--- /dev/null
+++ b/go-controller/observability-lib/ovsdb/gen.go
@@ -0,0 +1,3 @@
+package ovsdb
+
+//go:generate modelgen --extended -p ovsdb -o . vswitch.ovsschema
diff --git a/go-controller/observability-lib/ovsdb/observ_model.go b/go-controller/observability-lib/ovsdb/observ_model.go
new file mode 100644
index 00000000000..7ba2329e342
--- /dev/null
+++ b/go-controller/observability-lib/ovsdb/observ_model.go
@@ -0,0 +1,11 @@
+package ovsdb
+
+import "github.com/ovn-org/libovsdb/model"
+
+// ObservDatabaseModel returns the DatabaseModel object to be used by observability library.
+func ObservDatabaseModel() (model.ClientDBModel, error) {
+	return model.NewClientDBModel("Open_vSwitch", map[string]model.Model{
+		"Bridge":                    &Bridge{},
+		"Flow_Sample_Collector_Set": &FlowSampleCollectorSet{},
+	})
+}
diff --git a/go-controller/observability-lib/parse_sample.go b/go-controller/observability-lib/parse_sample.go
new file mode 100644
index 00000000000..5f3c712aba5
--- /dev/null
+++ b/go-controller/observability-lib/parse_sample.go
@@ -0,0 +1,243 @@
+package observability_lib
+
+import (
+	"bufio"
+	"bytes"
+	"context"
+	"encoding/binary"
+	"fmt"
+	"golang.org/x/sys/unix"
+	"io"
+	"log"
+	"os"
+	"strings"
+	"syscall"
+	"unsafe"
+
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/vishvananda/netlink"
+	"github.com/vishvananda/netlink/nl"
+
+	"github.com/ovn-org/ovn-kubernetes/go-controller/observability-lib/sampledecoder"
+)
+
+const (
+	PSAMPLE_GENL_NAME            = "psample"
+	PSAMPLE_NL_MCGRP_SAMPLE_NAME = "packets"
+)
+
+const (
+	PSAMPLE_ATTR_IIFINDEX = iota
+	PSAMPLE_ATTR_OIFINDEX
+	PSAMPLE_ATTR_ORIGSIZE
+	PSAMPLE_ATTR_SAMPLE_GROUP
+	PSAMPLE_ATTR_GROUP_SEQ
+	PSAMPLE_ATTR_SAMPLE_RATE
+	PSAMPLE_ATTR_DATA
+	PSAMPLE_ATTR_GROUP_REFCOUNT
+	PSAMPLE_ATTR_TUNNEL
+	PSAMPLE_ATTR_PAD
+	PSAMPLE_ATTR_OUT_TC     /* u16 */
+	PSAMPLE_ATTR_OUT_TC_OCC /* u64, bytes */
+	PSAMPLE_ATTR_LATENCY    /* u64, nanoseconds */
+	PSAMPLE_ATTR_TIMESTAMP  /* u64, nanoseconds */
+	PSAMPLE_ATTR_PROTO      /* u16 */
+	PSAMPLE_ATTR_USER_COOKIE
+	__PSAMPLE_ATTR_MAX
+)
+
+type SampleReader struct {
+	enableDecoder   bool
+	logCookie       bool
+	printFullPacket bool
+	addOVSCollector bool
+	srcIP, dstIP    string
+	outputFile      string
+
+	decoder   *sampledecoder.SampleDecoder
+	cookieStr []string
+}
+
+func NewSampleReader(enableDecoder, logCookie, printFullPacket, addOVSCollector bool, srcIP, dstIP, outputFile string) *SampleReader {
+	r := &SampleReader{
+		enableDecoder:   enableDecoder,
+		logCookie:       logCookie,
+		printFullPacket: printFullPacket,
+		addOVSCollector: addOVSCollector,
+		srcIP:           srcIP,
+		dstIP:           dstIP,
+		outputFile:      outputFile,
+	}
+	if logCookie {
+		r.cookieStr = make([]string, 2)
+	}
+	return r
+}
+
+func (r *SampleReader) ReadSamples(ctx context.Context) error {
+	if r.enableDecoder {
+		var err error
+		// currently only local nbdb connection is supported.
+		nbdbSocketPath := "/var/run/ovn/ovnnb_db.sock"
+		if r.addOVSCollector {
+			r.decoder, err = sampledecoder.NewSampleDecoderWithDefaultCollector(ctx, nbdbSocketPath, "ovnk-debug", 123)
+			if err != nil {
+				return fmt.Errorf("error creating decoder: %w", err)
+			}
+			defer r.decoder.Shutdown()
+		} else {
+			r.decoder, err = sampledecoder.NewSampleDecoder(ctx, nbdbSocketPath)
+			if err != nil {
+				return fmt.Errorf("error creating decoder: %w", err)
+			}
+		}
+	}
+	var writer io.Writer
+	if r.outputFile != "" {
+		file, err := os.Create(r.outputFile)
+		if err != nil {
+			return fmt.Errorf("error creating output file: %w", err)
+		}
+		defer file.Close()
+		writer = bufio.NewWriter(file)
+	} else {
+		writer = os.Stdout
+	}
+	l := log.New(writer, "", log.Ldate|log.Ltime|log.Lmicroseconds)
+	printlnFunc := func(a ...any) {
+		l.Println(a...)
+	}
+
+	fam, err := netlink.GenlFamilyGet(PSAMPLE_GENL_NAME)
+	if err != nil {
+		return fmt.Errorf("error getting netlink family %s: %w", PSAMPLE_GENL_NAME, err)
+	}
+	if len(fam.Groups) == 0 {
+		return fmt.Errorf("no mcast groups found for %s", PSAMPLE_GENL_NAME)
+	}
+	var ovsGroupID uint32
+	for _, group := range fam.Groups {
+		if group.Name == PSAMPLE_NL_MCGRP_SAMPLE_NAME {
+			ovsGroupID = group.ID
+		}
+	}
+	if ovsGroupID == 0 {
+		return fmt.Errorf("no mcast group found for %s", PSAMPLE_NL_MCGRP_SAMPLE_NAME)
+	} else {
+		fmt.Printf("Found group %s, id %d\n", PSAMPLE_NL_MCGRP_SAMPLE_NAME, ovsGroupID)
+	}
+	sock, err := nl.Subscribe(nl.GENL_ID_CTRL, uint(ovsGroupID))
+	if err != nil {
+		return fmt.Errorf("error subscribing to netlink group %d: %w", ovsGroupID, err)
+	}
+
+	// Otherwise sock.Receive() will be blocking and won't return on context close
+	if err = unix.SetNonblock(sock.GetFd(), true); err != nil {
+		return fmt.Errorf("error setting non-blocking mode: %w", err)
+	}
+
+	defer func() {
+		sock.Close()
+	}()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		default:
+			msgs, _, err := sock.Receive()
+			if err != nil {
+				if err == syscall.EAGAIN {
+					continue
+				}
+				printlnFunc("ERROR: receive failed:", err)
+				continue
+			}
+			if err = r.parseMsg(msgs, printlnFunc); err != nil {
+				printlnFunc("ERROR: ", err)
+			}
+		}
+	}
+}
+
+func getHostEndian() binary.ByteOrder {
+	buf := [2]byte{}
+	*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
+
+	switch buf {
+	case [2]byte{0xCD, 0xAB}:
+		return binary.LittleEndian
+	case [2]byte{0xAB, 0xCD}:
+		return binary.BigEndian
+	default:
+		panic("Could not determine native endianness.")
+	}
+}
+
+var hostEndian = getHostEndian()
+
+func (r *SampleReader) parseMsg(msgs []syscall.NetlinkMessage, printlnFunc func(a ...any)) error {
+	for _, msg := range msgs {
+		var packetStr, sampleStr string
+		data := msg.Data[nl.SizeofGenlmsg:]
+		for attr := range nl.ParseAttributes(data) {
+			if r.logCookie && attr.Type == PSAMPLE_ATTR_SAMPLE_GROUP {
+				if uint64(len(attr.Value)) == 4 {
+					g := uint32(0)
+					// group is encoded using host endian
+					err := binary.Read(bytes.NewReader(attr.Value), hostEndian, &g)
+					if err != nil {
+						return err
+					}
+					r.cookieStr[0] = fmt.Sprintf("group_id=%v", g)
+				}
+			}
+			if attr.Type == PSAMPLE_ATTR_USER_COOKIE && (r.logCookie || r.decoder != nil) {
+				if uint64(len(attr.Value)) == sampledecoder.CookieSize {
+					c := sampledecoder.Cookie{}
+					err := binary.Read(bytes.NewReader(attr.Value), sampledecoder.SampleEndian, &c)
+					if err != nil {
+						return err
+					}
+					if r.logCookie {
+						r.cookieStr[1] = fmt.Sprintf("obs_domain=%v, obs_point=%v",
+							c.ObsDomainID, c.ObsPointID)
+					}
+					if r.decoder != nil {
+						decoded, err := r.decoder.DecodeCookieIDs(c.ObsDomainID, c.ObsPointID)
+						if err != nil {
+							sampleStr = fmt.Sprintf("decoding failed: %v", err)
+						} else {
+							sampleStr = fmt.Sprintf("OVN-K message: %s", decoded)
+						}
+					}
+				}
+			}
+			if attr.Type == PSAMPLE_ATTR_DATA {
+				packet := gopacket.NewPacket(attr.Value, layers.LayerTypeEthernet, gopacket.Lazy)
+				networkLayer := packet.NetworkLayer().NetworkFlow()
+				if r.printFullPacket {
+					packetStr = packet.String()
+				} else {
+					packetStr = fmt.Sprintf("src=%s, dst=%v\n",
+						networkLayer.Src().String(), networkLayer.Dst().String())
+				}
+				if r.srcIP != "" && r.srcIP != networkLayer.Src().String() {
+					return nil
+				}
+				if r.dstIP != "" && r.dstIP != networkLayer.Dst().String() {
+					return nil
+				}
+			}
+		}
+		if r.logCookie {
+			printlnFunc(strings.Join(r.cookieStr, ", "))
+		}
+		if r.decoder != nil {
+			printlnFunc(sampleStr)
+		}
+		printlnFunc(packetStr)
+	}
+	return nil
+}
diff --git a/go-controller/observability-lib/sampledecoder/db_client.go b/go-controller/observability-lib/sampledecoder/db_client.go
new file mode 100644
index 00000000000..5ff1587a6f4
--- /dev/null
+++ b/go-controller/observability-lib/sampledecoder/db_client.go
@@ -0,0 +1,118 @@
+package sampledecoder
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/cenkalti/backoff/v4"
+	"github.com/ovn-org/libovsdb/client"
+	"github.com/ovn-org/libovsdb/model"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/observability-lib/ovsdb"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+	"k8s.io/klog/v2/textlogger"
+)
+
+const OVSDBTimeout = 10 * time.Second
+
+func NewNBClientWithConfig(ctx context.Context, cfg dbConfig) (client.Client, error) {
+	dbModel, err := nbdb.FullDatabaseModel()
+	if err != nil {
+		return nil, err
+	}
+
+	// define client indexes for ACLs to quickly find them by sample_new or sample_est column.
+	dbModel.SetIndexes(map[string][]model.ClientIndex{
+		nbdb.ACLTable: {
+			{Columns: []model.ColumnKey{{Column: "sample_new"}}},
+			{Columns: []model.ColumnKey{{Column: "sample_est"}}},
+		},
+	})
+
+	c, err := newClient(cfg, dbModel)
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = c.Monitor(ctx,
+		c.NewMonitor(
+			client.WithTable(&nbdb.ACL{}),
+			client.WithTable(&nbdb.Sample{}),
+		),
+	)
+
+	if err != nil {
+		c.Close()
+		return nil, err
+	}
+
+	return c, nil
+}
+
+func NewOVSDBClientWithConfig(ctx context.Context, cfg dbConfig) (client.Client, error) {
+	dbModel, err := ovsdb.ObservDatabaseModel()
+	if err != nil {
+		return nil, err
+	}
+
+	c, err := newClient(cfg, dbModel)
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = c.Monitor(ctx,
+		c.NewMonitor(
+			client.WithTable(&ovsdb.FlowSampleCollectorSet{}),
+			client.WithTable(&ovsdb.Bridge{}),
+		),
+	)
+	if err != nil {
+		c.Close()
+		return nil, err
+	}
+
+	return c, nil
+}
+
+// newClient creates a new client object given the provided config
+// the stopCh is required to ensure the goroutine for ssl cert
+// update is not leaked
+func newClient(cfg dbConfig, dbModel model.ClientDBModel) (client.Client, error) {
+	const connectTimeout = OVSDBTimeout * 2
+	const inactivityTimeout = OVSDBTimeout * 18
+	// Don't log anything from the libovsdb client by default
+	config := textlogger.NewConfig(textlogger.Verbosity(0))
+	logger := textlogger.NewLogger(config)
+
+	options := []client.Option{
+		// Reading and parsing the DB after reconnect at scale can (unsurprisingly)
+		// take longer than a normal ovsdb operation. Give it a bit more time, so
+		// we don't time out and enter a reconnect loop. In addition, it also enables
+		// inactivity check on the ovsdb connection.
+		client.WithInactivityCheck(inactivityTimeout, connectTimeout, &backoff.ZeroBackOff{}),
+		client.WithLeaderOnly(true),
+		client.WithLogger(&logger),
+	}
+
+	for _, endpoint := range strings.Split(cfg.address, ",") {
+		options = append(options, client.WithEndpoint(endpoint))
+	}
+	if cfg.scheme != "unix" {
+		return nil, fmt.Errorf("only unix scheme is supported for now")
+	}
+
+	client, err := client.NewOVSDBClient(dbModel, options...)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), connectTimeout)
+	defer cancel()
+	err = client.Connect(ctx)
+	if err != nil {
+		return nil, err
+	}
+
+	return client, nil
+}
diff --git a/go-controller/observability-lib/sampledecoder/sample_decoder.go b/go-controller/observability-lib/sampledecoder/sample_decoder.go
new file mode 100644
index 00000000000..0bf419bd5c0
--- /dev/null
+++ b/go-controller/observability-lib/sampledecoder/sample_decoder.go
@@ -0,0 +1,290 @@
+package sampledecoder
+
+import (
+	"bytes"
+	"context"
+	"encoding/binary"
+	"fmt"
+
+	"github.com/ovn-org/libovsdb/client"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/observability-lib/ovsdb"
+	libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/observability"
+)
+
+type SampleDecoder struct {
+	nbClient          client.Client
+	ovsdbClient       client.Client
+	cleanupCollectors []int
+}
+
+type dbConfig struct {
+	address string
+	scheme  string
+}
+
+type Cookie struct {
+	ObsDomainID uint32
+	ObsPointID  uint32
+}
+
+const CookieSize = 8
+const bridgeName = "br-int"
+
+var SampleEndian = getEndian()
+
+func getEndian() binary.ByteOrder {
+	// Use network bite order
+	return binary.BigEndian
+}
+
+// getLocalNBClient only supports connecting to nbdb via unix socket.
+// address is the path to the unix socket, e.g. "/var/run/ovn/ovnnb_db.sock"
+func getLocalNBClient(ctx context.Context, address string) (client.Client, error) {
+	config := dbConfig{
+		address: "unix:" + address,
+		scheme:  "unix",
+	}
+	libovsdbOvnNBClient, err := NewNBClientWithConfig(ctx, config)
+	if err != nil {
+		return nil, fmt.Errorf("error creating libovsdb client: %w ", err)
+	}
+	return libovsdbOvnNBClient, nil
+}
+
+func getLocalOVSDBClient(ctx context.Context) (client.Client, error) {
+	config := dbConfig{
+		address: "unix:/var/run/openvswitch/db.sock",
+		scheme:  "unix",
+	}
+	return NewOVSDBClientWithConfig(ctx, config)
+}
+
+// NewSampleDecoderWithDefaultCollector creates a new SampleDecoder, initializes the OVSDB client and adds the default collector.
+// It allows to set the groupID and ownerName for the created default collector.
+// If the default collector already exists with a different owner or different groupID an error will be returned.
+// Shutdown should be called to clean up the collector from the OVSDB.
+func NewSampleDecoderWithDefaultCollector(ctx context.Context, nbdbSocketPath string, ownerName string, groupID int) (*SampleDecoder, error) {
+	nbClient, err := getLocalNBClient(ctx, nbdbSocketPath)
+	if err != nil {
+		return nil, err
+	}
+	ovsdbClient, err := getLocalOVSDBClient(ctx)
+	if err != nil {
+		return nil, err
+	}
+	decoder := &SampleDecoder{
+		nbClient:    nbClient,
+		ovsdbClient: ovsdbClient,
+	}
+	err = decoder.AddCollector(observability.DefaultObservabilityCollectorSetID, groupID, ownerName)
+	if err != nil {
+		return nil, err
+	}
+	decoder.cleanupCollectors = append(decoder.cleanupCollectors, observability.DefaultObservabilityCollectorSetID)
+	return decoder, nil
+}
+
+// NewSampleDecoder creates a new SampleDecoder and initializes the OVSDB client.
+func NewSampleDecoder(ctx context.Context, nbdbSocketPath string) (*SampleDecoder, error) {
+	nbClient, err := getLocalNBClient(ctx, nbdbSocketPath)
+	if err != nil {
+		return nil, err
+	}
+	return &SampleDecoder{
+		nbClient: nbClient,
+	}, nil
+}
+
+func (d *SampleDecoder) Shutdown() {
+	for _, collectorID := range d.cleanupCollectors {
+		err := d.DeleteCollector(collectorID)
+		if err != nil {
+			fmt.Printf("Error deleting collector with ID=%d: %v", collectorID, err)
+		}
+	}
+}
+
+func getObservAppID(obsDomainID uint32) uint8 {
+	return uint8(obsDomainID >> 24)
+}
+
+// findACLBySample relies on the client index based on sample_new and sample_est column.
+func findACLBySample(nbClient client.Client, acl *nbdb.ACL) ([]*nbdb.ACL, error) {
+	found := []*nbdb.ACL{}
+	err := nbClient.Where(acl).List(context.Background(), &found)
+	return found, err
+}
+
+func (d *SampleDecoder) DecodeCookieIDs(obsDomainID, obsPointID uint32) (string, error) {
+	// Find sample using obsPointID
+	sample, err := libovsdbops.FindSample(d.nbClient, int(obsPointID))
+	if err != nil || sample == nil {
+		return "", fmt.Errorf("find sample failed: %w", err)
+	}
+	// find db object using observ application ID
+	// Since ACL is indexed both by sample_new and sample_est, when searching by one of them,
+	// we need to make sure the other one will not match.
+	// nil is a valid index value, therefore we have to use non-existing UUID.
+	wrongUUID := "wrongUUID"
+	var dbObj interface{}
+	switch getObservAppID(obsDomainID) {
+	case observability.ACLNewTrafficSamplingID:
+		acls, err := findACLBySample(d.nbClient, &nbdb.ACL{SampleNew: &sample.UUID, SampleEst: &wrongUUID})
+		if err != nil {
+			return "", fmt.Errorf("find acl for sample failed: %w", err)
+		}
+		if len(acls) != 1 {
+			return "", fmt.Errorf("expected 1 ACL, got %d", len(acls))
+		}
+		dbObj = acls[0]
+	case observability.ACLEstTrafficSamplingID:
+		acls, err := findACLBySample(d.nbClient, &nbdb.ACL{SampleNew: &wrongUUID, SampleEst: &sample.UUID})
+		if err != nil {
+			return "", fmt.Errorf("find acl for sample failed: %w", err)
+		}
+		if len(acls) != 1 {
+			return "", fmt.Errorf("expected 1 ACL, got %d", len(acls))
+		}
+		dbObj = acls[0]
+	default:
+		return "", fmt.Errorf("unknown app ID: %d", getObservAppID(obsDomainID))
+	}
+	msg := getMessage(dbObj)
+	if msg == "" {
+		return "", fmt.Errorf("failed to get message for db object %v", dbObj)
+	}
+	return msg, nil
+}
+
+func getMessage(dbObj interface{}) string {
+	switch o := dbObj.(type) {
+	case *nbdb.ACL:
+		var action string
+		switch o.Action {
+		case nbdb.ACLActionAllow, nbdb.ACLActionAllowRelated, nbdb.ACLActionAllowStateless:
+			action = "Allowed"
+		case nbdb.ACLActionDrop:
+			action = "Dropped"
+		case nbdb.ACLActionPass:
+			action = "Delegated to network policy"
+		default:
+			action = "Action " + o.Action
+		}
+		actor := o.ExternalIDs[libovsdbops.OwnerTypeKey.String()]
+		var msg string
+		switch actor {
+		case libovsdbops.AdminNetworkPolicyOwnerType:
+			msg = fmt.Sprintf("admin network policy %s, direction %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()], o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.BaselineAdminNetworkPolicyOwnerType:
+			msg = fmt.Sprintf("baseline admin network policy %s, direction %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()], o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.MulticastNamespaceOwnerType:
+			msg = fmt.Sprintf("multicast in namespace %s, direction %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()], o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.MulticastClusterOwnerType:
+			msg = fmt.Sprintf("cluster multicast policy, direction %s", o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.NetpolNodeOwnerType:
+			msg = "default allow from local node policy, direction ingress"
+		case libovsdbops.NetworkPolicyOwnerType:
+			msg = fmt.Sprintf("network policy %s, direction %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()], o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.NetpolNamespaceOwnerType:
+			msg = fmt.Sprintf("network policies isolation in namespace %s, direction %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()], o.ExternalIDs[libovsdbops.PolicyDirectionKey.String()])
+		case libovsdbops.EgressFirewallOwnerType:
+			msg = fmt.Sprintf("egress firewall in namespace %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()])
+		case libovsdbops.UDNIsolationOwnerType:
+			msg = fmt.Sprintf("UDN isolation of type %s", o.ExternalIDs[libovsdbops.ObjectNameKey.String()])
+		}
+		return fmt.Sprintf("%s by %s", action, msg)
+	default:
+		return ""
+	}
+}
+
+func (d *SampleDecoder) DecodeCookieBytes(cookie []byte) (string, error) {
+	if uint64(len(cookie)) != CookieSize {
+		return "", fmt.Errorf("invalid cookie size: %d", len(cookie))
+	}
+	c := Cookie{}
+	err := binary.Read(bytes.NewReader(cookie), SampleEndian, &c)
+	if err != nil {
+		return "", err
+	}
+	return d.DecodeCookieIDs(c.ObsDomainID, c.ObsPointID)
+}
+
+func (d *SampleDecoder) DecodeCookie8Bytes(cookie [8]byte) (string, error) {
+	c := Cookie{}
+	err := binary.Read(bytes.NewReader(cookie[:]), SampleEndian, &c)
+	if err != nil {
+		return "", err
+	}
+	return d.DecodeCookieIDs(c.ObsDomainID, c.ObsPointID)
+}
+
+func getGroupID(groupID *int) string {
+	if groupID == nil {
+		return "unset"
+	}
+	return fmt.Sprintf("%d", *groupID)
+}
+
+func (d *SampleDecoder) AddCollector(collectorID, groupID int, ownerName string) error {
+	if d.ovsdbClient == nil {
+		return fmt.Errorf("OVSDB client is not initialized")
+	}
+	// find existing collector with the same ID
+	collectors := []*ovsdb.FlowSampleCollectorSet{}
+	err := d.ovsdbClient.WhereCache(func(item *ovsdb.FlowSampleCollectorSet) bool {
+		return item.ID == collectorID
+	}).List(context.Background(), &collectors)
+	if err != nil {
+		return fmt.Errorf("failed finding existing collector: %w", err)
+	}
+	if len(collectors) > 0 && (collectors[0].ExternalIDs["owner"] != ownerName ||
+		collectors[0].LocalGroupID == nil || *collectors[0].LocalGroupID != groupID) {
+		return fmt.Errorf("requested collector with id=%v already exists "+
+			"with the external_ids=%+v, local_group_id=%v", collectorID, collectors[0].ExternalIDs["owner"], getGroupID(collectors[0].LocalGroupID))
+	}
+
+	// find br-int UUID to attach collector
+	bridges := []*ovsdb.Bridge{}
+	err = d.ovsdbClient.WhereCache(func(item *ovsdb.Bridge) bool {
+		return item.Name == bridgeName
+	}).List(context.Background(), &bridges)
+	if err != nil || len(bridges) != 1 {
+		return fmt.Errorf("failed finding br-int: %w", err)
+	}
+
+	ops, err := d.ovsdbClient.Create(&ovsdb.FlowSampleCollectorSet{
+		ID:           collectorID,
+		Bridge:       bridges[0].UUID,
+		LocalGroupID: &groupID,
+		ExternalIDs:  map[string]string{"owner": ownerName},
+	})
+	if err != nil {
+		return fmt.Errorf("failed creating collector: %w", err)
+	}
+	_, err = d.ovsdbClient.Transact(context.Background(), ops...)
+	return err
+}
+
+func (d *SampleDecoder) DeleteCollector(collectorID int) error {
+	collectors := []*ovsdb.FlowSampleCollectorSet{}
+	err := d.ovsdbClient.WhereCache(func(item *ovsdb.FlowSampleCollectorSet) bool {
+		return item.ID == collectorID
+	}).List(context.Background(), &collectors)
+	if err != nil {
+		return fmt.Errorf("failed finding exisiting collector: %w", err)
+	}
+	if len(collectors) != 1 {
+		return fmt.Errorf("expected only 1 collector with given id")
+	}
+
+	ops, err := d.ovsdbClient.Where(collectors[0]).Delete()
+	if err != nil {
+		return fmt.Errorf("failed creating collector: %w", err)
+	}
+	res, err := d.ovsdbClient.Transact(context.Background(), ops...)
+	fmt.Println("res: ", res)
+	return err
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/.gitignore b/go-controller/vendor/github.com/google/gopacket/.gitignore
new file mode 100644
index 00000000000..149266fdb65
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.gitignore
@@ -0,0 +1,38 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+#*
+*~
+
+# examples binaries
+examples/synscan/synscan
+examples/pfdump/pfdump
+examples/pcapdump/pcapdump
+examples/httpassembly/httpassembly
+examples/statsassembly/statsassembly
+examples/arpscan/arpscan
+examples/bidirectional/bidirectional
+examples/bytediff/bytediff
+examples/reassemblydump/reassemblydump
+layers/gen
+macs/gen
+pcap/pcap_tester
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.gofmt.sh b/go-controller/vendor/github.com/google/gopacket/.travis.gofmt.sh
new file mode 100644
index 00000000000..e341a1cb78f
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.gofmt.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+if [ -n "$(go fmt ./...)" ]; then
+  echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
+  exit 1
+fi
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.golint.sh b/go-controller/vendor/github.com/google/gopacket/.travis.golint.sh
new file mode 100644
index 00000000000..0e267f5216a
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.golint.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+go get golang.org/x/lint/golint
+DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
+# Add subdirectories here as we clean up golint on each.
+for subdir in $DIRS; do
+  pushd $subdir
+  if golint |
+      grep -v CannotSetRFMon |  # pcap exported error name
+      grep -v DataLost |        # tcpassembly/tcpreader exported error name
+      grep .; then
+    exit 1
+  fi
+  popd
+done
+
+pushd layers
+for file in *.go; do
+  if cat .lint_blacklist | grep -q $file; then
+    echo "Skipping lint of $file due to .lint_blacklist"
+  elif golint $file | grep .; then
+    echo "Lint error in file $file"
+    exit 1
+  fi
+done
+popd
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.govet.sh b/go-controller/vendor/github.com/google/gopacket/.travis.govet.sh
new file mode 100644
index 00000000000..a5c13544ca2
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.govet.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+DIRS=". layers pcap pcapgo tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag"
+set -e
+for subdir in $DIRS; do
+  pushd $subdir
+  go vet
+  popd
+done
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.install.sh b/go-controller/vendor/github.com/google/gopacket/.travis.install.sh
new file mode 100644
index 00000000000..648c901638d
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.install.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+set -ev
+
+go get github.com/google/gopacket
+go get github.com/google/gopacket/layers
+go get github.com/google/gopacket/tcpassembly
+go get github.com/google/gopacket/reassembly
+go get github.com/google/gopacket/pcapgo
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.script.sh b/go-controller/vendor/github.com/google/gopacket/.travis.script.sh
new file mode 100644
index 00000000000..a483f4f7c6c
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.script.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -ev
+
+go test github.com/google/gopacket
+go test github.com/google/gopacket/layers
+go test github.com/google/gopacket/tcpassembly
+go test github.com/google/gopacket/reassembly
+go test github.com/google/gopacket/pcapgo 
+go test github.com/google/gopacket/pcap
diff --git a/go-controller/vendor/github.com/google/gopacket/.travis.yml b/go-controller/vendor/github.com/google/gopacket/.travis.yml
new file mode 100644
index 00000000000..84f1f4945ab
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/.travis.yml
@@ -0,0 +1,57 @@
+language: go
+go:
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
+ - master
+
+addons:
+  apt:
+    packages:
+      libpcap-dev
+
+# use modules except for older versions (see below)
+install: true
+
+env:
+  - GO111MODULE=on
+
+script: ./.travis.script.sh
+
+matrix:
+  fast_finish: true
+  allow_failures:
+    - go: master
+
+jobs:
+  include:
+    - go: 1.5.x
+      install: ./.travis.install.sh
+    - go: 1.6.x
+      install: ./.travis.install.sh
+    - go: 1.7.x
+      install: ./.travis.install.sh
+    - go: 1.8.x
+      install: ./.travis.install.sh
+    - go: 1.9.x
+      install: ./.travis.install.sh
+    - go: 1.10.x
+      install: ./.travis.install.sh
+    - os: osx
+      go: 1.x
+# windows doesn't work on travis (package installation just hangs and then errors out)
+#    - os: windows
+#      go: 1.x
+#      # We don't need nmap - but that's the only way to get npcap:
+#      before_install: choco install npcap --version 0.86 -y
+    - stage: style
+      name: "fmt/vet/lint"
+      go: 1.x
+      script:
+        - ./.travis.gofmt.sh
+        - ./.travis.govet.sh
+        - ./.travis.golint.sh
+
+stages:
+  - style
+  - test
diff --git a/go-controller/vendor/github.com/google/gopacket/AUTHORS b/go-controller/vendor/github.com/google/gopacket/AUTHORS
new file mode 100644
index 00000000000..24e834e4512
--- /dev/null
+++ b/go-controller/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>
diff --git a/go-controller/vendor/github.com/google/gopacket/CONTRIBUTING.md b/go-controller/vendor/github.com/google/gopacket/CONTRIBUTING.md
new file mode 100644
index 00000000000..99ab7a2e4fb
--- /dev/null
+++ b/go-controller/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.
diff --git a/go-controller/vendor/github.com/google/gopacket/LICENSE b/go-controller/vendor/github.com/google/gopacket/LICENSE
new file mode 100644
index 00000000000..2100d524d9e
--- /dev/null
+++ b/go-controller/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.
diff --git a/go-controller/vendor/github.com/google/gopacket/README.md b/go-controller/vendor/github.com/google/gopacket/README.md
new file mode 100644
index 00000000000..efe462ee100
--- /dev/null
+++ b/go-controller/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).
diff --git a/go-controller/vendor/github.com/google/gopacket/base.go b/go-controller/vendor/github.com/google/gopacket/base.go
new file mode 100644
index 00000000000..91e150c215b
--- /dev/null
+++ b/go-controller/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
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/decode.go b/go-controller/vendor/github.com/google/gopacket/decode.go
new file mode 100644
index 00000000000..2633f848ede
--- /dev/null
+++ b/go-controller/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")
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/doc.go b/go-controller/vendor/github.com/google/gopacket/doc.go
new file mode 100644
index 00000000000..b46e43dfa53
--- /dev/null
+++ b/go-controller/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
diff --git a/go-controller/vendor/github.com/google/gopacket/flows.go b/go-controller/vendor/github.com/google/gopacket/flows.go
new file mode 100644
index 00000000000..a00c88398e5
--- /dev/null
+++ b/go-controller/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)
diff --git a/go-controller/vendor/github.com/google/gopacket/gc b/go-controller/vendor/github.com/google/gopacket/gc
new file mode 100644
index 00000000000..b1d8d2e1f67
--- /dev/null
+++ b/go-controller/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
diff --git a/go-controller/vendor/github.com/google/gopacket/layerclass.go b/go-controller/vendor/github.com/google/gopacket/layerclass.go
new file mode 100644
index 00000000000..775cd098770
--- /dev/null
+++ b/go-controller/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)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/.lint_blacklist b/go-controller/vendor/github.com/google/gopacket/layers/.lint_blacklist
new file mode 100644
index 00000000000..fded4f6650f
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/.lint_blacklist
@@ -0,0 +1,39 @@
+dot11.go
+eap.go
+endpoints.go
+enums_generated.go
+enums.go
+ethernet.go
+geneve.go
+icmp4.go
+icmp6.go
+igmp.go
+ip4.go
+ip6.go
+layertypes.go
+linux_sll.go
+llc.go
+lldp.go
+mpls.go
+ndp.go
+ntp.go
+ospf.go
+pflog.go
+pppoe.go
+prism.go
+radiotap.go
+rudp.go
+sctp.go
+sflow.go
+tcp.go
+tcpip.go
+tls.go
+tls_alert.go
+tls_appdata.go
+tls_cipherspec.go
+tls_hanshake.go
+tls_test.go
+udp.go
+udplite.go
+usb.go
+vrrp.go
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/arp.go b/go-controller/vendor/github.com/google/gopacket/layers/arp.go
new file mode 100644
index 00000000000..0775ac0b6d4
--- /dev/null
+++ b/go-controller/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)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/asf.go b/go-controller/vendor/github.com/google/gopacket/layers/asf.go
new file mode 100644
index 00000000000..d698bd0e53a
--- /dev/null
+++ b/go-controller/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)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/asf_presencepong.go b/go-controller/vendor/github.com/google/gopacket/layers/asf_presencepong.go
new file mode 100644
index 00000000000..e9a8baf16c5
--- /dev/null
+++ b/go-controller/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)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/base.go b/go-controller/vendor/github.com/google/gopacket/layers/base.go
new file mode 100644
index 00000000000..cd59b467861
--- /dev/null
+++ b/go-controller/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
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/bfd.go b/go-controller/vendor/github.com/google/gopacket/layers/bfd.go
new file mode 100644
index 00000000000..43030fb6a55
--- /dev/null
+++ b/go-controller/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
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/cdp.go b/go-controller/vendor/github.com/google/gopacket/layers/cdp.go
new file mode 100644
index 00000000000..095f9261250
--- /dev/null
+++ b/go-controller/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
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ctp.go b/go-controller/vendor/github.com/google/gopacket/layers/ctp.go
new file mode 100644
index 00000000000..82875845a72
--- /dev/null
+++ b/go-controller/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)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dhcpv4.go b/go-controller/vendor/github.com/google/gopacket/layers/dhcpv4.go
new file mode 100644
index 00000000000..d79c5915043
--- /dev/null
+++ b/go-controller/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")
+)
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dhcpv6.go b/go-controller/vendor/github.com/google/gopacket/layers/dhcpv6.go
new file mode 100644
index 00000000000..2698cfb196d
--- /dev/null
+++ b/go-controller/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
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dhcpv6_options.go b/go-controller/vendor/github.com/google/gopacket/layers/dhcpv6_options.go
new file mode 100644
index 00000000000..5a1f9919b1f
--- /dev/null
+++ b/go-controller/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
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dns.go b/go-controller/vendor/github.com/google/gopacket/layers/dns.go
new file mode 100644
index 00000000000..de55294b5bb
--- /dev/null
+++ b/go-controller/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")
+)
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/doc.go b/go-controller/vendor/github.com/google/gopacket/layers/doc.go
new file mode 100644
index 00000000000..3c882c3fa0d
--- /dev/null
+++ b/go-controller/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
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dot11.go b/go-controller/vendor/github.com/google/gopacket/layers/dot11.go
new file mode 100644
index 00000000000..3e649106105
--- /dev/null
+++ b/go-controller/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 }
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/dot1q.go b/go-controller/vendor/github.com/google/gopacket/layers/dot1q.go
new file mode 100644
index 00000000000..5cdd2f8d683
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/dot1q.go
@@ -0,0 +1,75 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"fmt"
+	"github.com/google/gopacket"
+)
+
+// Dot1Q is the packet layer for 802.1Q VLAN headers.
+type Dot1Q struct {
+	BaseLayer
+	Priority       uint8
+	DropEligible   bool
+	VLANIdentifier uint16
+	Type           EthernetType
+}
+
+// LayerType returns gopacket.LayerTypeDot1Q
+func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("802.1Q tag length %d too short", len(data))
+	}
+	d.Priority = (data[0] & 0xE0) >> 5
+	d.DropEligible = data[0]&0x10 != 0
+	d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
+	d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4]))
+	d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (d *Dot1Q) CanDecode() gopacket.LayerClass {
+	return LayerTypeDot1Q
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (d *Dot1Q) NextLayerType() gopacket.LayerType {
+	return d.Type.LayerType()
+}
+
+func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error {
+	d := &Dot1Q{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	if d.VLANIdentifier > 0xFFF {
+		return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier)
+	}
+	firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier
+	if d.DropEligible {
+		firstBytes |= 0x1000
+	}
+	binary.BigEndian.PutUint16(bytes, firstBytes)
+	binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type))
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/eap.go b/go-controller/vendor/github.com/google/gopacket/layers/eap.go
new file mode 100644
index 00000000000..54238e8c73b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/eap.go
@@ -0,0 +1,114 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"fmt"
+	"github.com/google/gopacket"
+)
+
+type EAPCode uint8
+type EAPType uint8
+
+const (
+	EAPCodeRequest  EAPCode = 1
+	EAPCodeResponse EAPCode = 2
+	EAPCodeSuccess  EAPCode = 3
+	EAPCodeFailure  EAPCode = 4
+
+	// EAPTypeNone means that this EAP layer has no Type or TypeData.
+	// Success and Failure EAPs will have this set.
+	EAPTypeNone EAPType = 0
+
+	EAPTypeIdentity     EAPType = 1
+	EAPTypeNotification EAPType = 2
+	EAPTypeNACK         EAPType = 3
+	EAPTypeOTP          EAPType = 4
+	EAPTypeTokenCard    EAPType = 5
+)
+
+// EAP defines an Extensible Authentication Protocol (rfc 3748) layer.
+type EAP struct {
+	BaseLayer
+	Code     EAPCode
+	Id       uint8
+	Length   uint16
+	Type     EAPType
+	TypeData []byte
+}
+
+// LayerType returns LayerTypeEAP.
+func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("EAP length %d too short", len(data))
+	}
+	e.Code = EAPCode(data[0])
+	e.Id = data[1]
+	e.Length = binary.BigEndian.Uint16(data[2:4])
+	if len(data) < int(e.Length) {
+		df.SetTruncated()
+		return fmt.Errorf("EAP length %d too short, %d expected", len(data), e.Length)
+	}
+	switch {
+	case e.Length > 4:
+		e.Type = EAPType(data[4])
+		e.TypeData = data[5:]
+	case e.Length == 4:
+		e.Type = 0
+		e.TypeData = nil
+	default:
+		return fmt.Errorf("invalid EAP length %d", e.Length)
+	}
+	e.BaseLayer.Contents = data[:e.Length]
+	e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if opts.FixLengths {
+		e.Length = uint16(len(e.TypeData) + 1)
+	}
+	size := len(e.TypeData) + 4
+	if size > 4 {
+		size++
+	}
+	bytes, err := b.PrependBytes(size)
+	if err != nil {
+		return err
+	}
+	bytes[0] = byte(e.Code)
+	bytes[1] = e.Id
+	binary.BigEndian.PutUint16(bytes[2:], e.Length)
+	if size > 4 {
+		bytes[4] = byte(e.Type)
+		copy(bytes[5:], e.TypeData)
+	}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (e *EAP) CanDecode() gopacket.LayerClass {
+	return LayerTypeEAP
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (e *EAP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+func decodeEAP(data []byte, p gopacket.PacketBuilder) error {
+	e := &EAP{}
+	return decodingLayerDecoder(e, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/eapol.go b/go-controller/vendor/github.com/google/gopacket/layers/eapol.go
new file mode 100644
index 00000000000..902598a2069
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/eapol.go
@@ -0,0 +1,302 @@
+// 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"
+)
+
+// EAPOL defines an EAP over LAN (802.1x) layer.
+type EAPOL struct {
+	BaseLayer
+	Version uint8
+	Type    EAPOLType
+	Length  uint16
+}
+
+// LayerType returns LayerTypeEAPOL.
+func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("EAPOL length %d too short", len(data))
+	}
+	e.Version = data[0]
+	e.Type = EAPOLType(data[1])
+	e.Length = binary.BigEndian.Uint16(data[2:4])
+	e.BaseLayer = BaseLayer{data[:4], data[4:]}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer
+func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, _ := b.PrependBytes(4)
+	bytes[0] = e.Version
+	bytes[1] = byte(e.Type)
+	binary.BigEndian.PutUint16(bytes[2:], e.Length)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (e *EAPOL) CanDecode() gopacket.LayerClass {
+	return LayerTypeEAPOL
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (e *EAPOL) NextLayerType() gopacket.LayerType {
+	return e.Type.LayerType()
+}
+
+func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error {
+	e := &EAPOL{}
+	return decodingLayerDecoder(e, data, p)
+}
+
+// EAPOLKeyDescriptorType is an enumeration of key descriptor types
+// as specified by 802.1x in the EAPOL-Key frame
+type EAPOLKeyDescriptorType uint8
+
+// Enumeration of EAPOLKeyDescriptorType
+const (
+	EAPOLKeyDescriptorTypeRC4   EAPOLKeyDescriptorType = 1
+	EAPOLKeyDescriptorTypeDot11 EAPOLKeyDescriptorType = 2
+	EAPOLKeyDescriptorTypeWPA   EAPOLKeyDescriptorType = 254
+)
+
+func (kdt EAPOLKeyDescriptorType) String() string {
+	switch kdt {
+	case EAPOLKeyDescriptorTypeRC4:
+		return "RC4"
+	case EAPOLKeyDescriptorTypeDot11:
+		return "802.11"
+	case EAPOLKeyDescriptorTypeWPA:
+		return "WPA"
+	default:
+		return fmt.Sprintf("unknown descriptor type %d", kdt)
+	}
+}
+
+// EAPOLKeyDescriptorVersion is an enumeration of versions specifying the
+// encryption algorithm for the key data and the authentication for the
+// message integrity code (MIC)
+type EAPOLKeyDescriptorVersion uint8
+
+// Enumeration of EAPOLKeyDescriptorVersion
+const (
+	EAPOLKeyDescriptorVersionOther       EAPOLKeyDescriptorVersion = 0
+	EAPOLKeyDescriptorVersionRC4HMACMD5  EAPOLKeyDescriptorVersion = 1
+	EAPOLKeyDescriptorVersionAESHMACSHA1 EAPOLKeyDescriptorVersion = 2
+	EAPOLKeyDescriptorVersionAES128CMAC  EAPOLKeyDescriptorVersion = 3
+)
+
+func (v EAPOLKeyDescriptorVersion) String() string {
+	switch v {
+	case EAPOLKeyDescriptorVersionOther:
+		return "Other"
+	case EAPOLKeyDescriptorVersionRC4HMACMD5:
+		return "RC4-HMAC-MD5"
+	case EAPOLKeyDescriptorVersionAESHMACSHA1:
+		return "AES-HMAC-SHA1-128"
+	case EAPOLKeyDescriptorVersionAES128CMAC:
+		return "AES-128-CMAC"
+	default:
+		return fmt.Sprintf("unknown version %d", v)
+	}
+}
+
+// EAPOLKeyType is an enumeration of key derivation types describing
+// the purpose of the keys being derived.
+type EAPOLKeyType uint8
+
+// Enumeration of EAPOLKeyType
+const (
+	EAPOLKeyTypeGroupSMK EAPOLKeyType = 0
+	EAPOLKeyTypePairwise EAPOLKeyType = 1
+)
+
+func (kt EAPOLKeyType) String() string {
+	switch kt {
+	case EAPOLKeyTypeGroupSMK:
+		return "Group/SMK"
+	case EAPOLKeyTypePairwise:
+		return "Pairwise"
+	default:
+		return fmt.Sprintf("unknown key type %d", kt)
+	}
+}
+
+// EAPOLKey defines an EAPOL-Key frame for 802.1x authentication
+type EAPOLKey struct {
+	BaseLayer
+	KeyDescriptorType    EAPOLKeyDescriptorType
+	KeyDescriptorVersion EAPOLKeyDescriptorVersion
+	KeyType              EAPOLKeyType
+	KeyIndex             uint8
+	Install              bool
+	KeyACK               bool
+	KeyMIC               bool
+	Secure               bool
+	MICError             bool
+	Request              bool
+	HasEncryptedKeyData  bool
+	SMKMessage           bool
+	KeyLength            uint16
+	ReplayCounter        uint64
+	Nonce                []byte
+	IV                   []byte
+	RSC                  uint64
+	ID                   uint64
+	MIC                  []byte
+	KeyDataLength        uint16
+	EncryptedKeyData     []byte
+}
+
+// LayerType returns LayerTypeEAPOLKey.
+func (ek *EAPOLKey) LayerType() gopacket.LayerType {
+	return LayerTypeEAPOLKey
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (ek *EAPOLKey) CanDecode() gopacket.LayerType {
+	return LayerTypeEAPOLKey
+}
+
+// NextLayerType returns layers.LayerTypeDot11InformationElement if the key
+// data exists and is unencrypted, otherwise it does not expect a next layer.
+func (ek *EAPOLKey) NextLayerType() gopacket.LayerType {
+	if !ek.HasEncryptedKeyData && ek.KeyDataLength > 0 {
+		return LayerTypeDot11InformationElement
+	}
+	return gopacket.LayerTypePayload
+}
+
+const eapolKeyFrameLen = 95
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (ek *EAPOLKey) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < eapolKeyFrameLen {
+		df.SetTruncated()
+		return fmt.Errorf("EAPOLKey length %v too short, %v required",
+			len(data), eapolKeyFrameLen)
+	}
+
+	ek.KeyDescriptorType = EAPOLKeyDescriptorType(data[0])
+
+	info := binary.BigEndian.Uint16(data[1:3])
+	ek.KeyDescriptorVersion = EAPOLKeyDescriptorVersion(info & 0x0007)
+	ek.KeyType = EAPOLKeyType((info & 0x0008) >> 3)
+	ek.KeyIndex = uint8((info & 0x0030) >> 4)
+	ek.Install = (info & 0x0040) != 0
+	ek.KeyACK = (info & 0x0080) != 0
+	ek.KeyMIC = (info & 0x0100) != 0
+	ek.Secure = (info & 0x0200) != 0
+	ek.MICError = (info & 0x0400) != 0
+	ek.Request = (info & 0x0800) != 0
+	ek.HasEncryptedKeyData = (info & 0x1000) != 0
+	ek.SMKMessage = (info & 0x2000) != 0
+
+	ek.KeyLength = binary.BigEndian.Uint16(data[3:5])
+	ek.ReplayCounter = binary.BigEndian.Uint64(data[5:13])
+
+	ek.Nonce = data[13:45]
+	ek.IV = data[45:61]
+	ek.RSC = binary.BigEndian.Uint64(data[61:69])
+	ek.ID = binary.BigEndian.Uint64(data[69:77])
+	ek.MIC = data[77:93]
+
+	ek.KeyDataLength = binary.BigEndian.Uint16(data[93:95])
+
+	totalLength := eapolKeyFrameLen + int(ek.KeyDataLength)
+	if len(data) < totalLength {
+		df.SetTruncated()
+		return fmt.Errorf("EAPOLKey data length %d too short, %d required",
+			len(data)-eapolKeyFrameLen, ek.KeyDataLength)
+	}
+
+	if ek.HasEncryptedKeyData {
+		ek.EncryptedKeyData = data[eapolKeyFrameLen:totalLength]
+		ek.BaseLayer = BaseLayer{
+			Contents: data[:totalLength],
+			Payload:  data[totalLength:],
+		}
+	} else {
+		ek.BaseLayer = BaseLayer{
+			Contents: data[:eapolKeyFrameLen],
+			Payload:  data[eapolKeyFrameLen:],
+		}
+	}
+
+	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 (ek *EAPOLKey) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(eapolKeyFrameLen + len(ek.EncryptedKeyData))
+	if err != nil {
+		return err
+	}
+
+	buf[0] = byte(ek.KeyDescriptorType)
+
+	var info uint16
+	info |= uint16(ek.KeyDescriptorVersion)
+	info |= uint16(ek.KeyType) << 3
+	info |= uint16(ek.KeyIndex) << 4
+	if ek.Install {
+		info |= 0x0040
+	}
+	if ek.KeyACK {
+		info |= 0x0080
+	}
+	if ek.KeyMIC {
+		info |= 0x0100
+	}
+	if ek.Secure {
+		info |= 0x0200
+	}
+	if ek.MICError {
+		info |= 0x0400
+	}
+	if ek.Request {
+		info |= 0x0800
+	}
+	if ek.HasEncryptedKeyData {
+		info |= 0x1000
+	}
+	if ek.SMKMessage {
+		info |= 0x2000
+	}
+	binary.BigEndian.PutUint16(buf[1:3], info)
+
+	binary.BigEndian.PutUint16(buf[3:5], ek.KeyLength)
+	binary.BigEndian.PutUint64(buf[5:13], ek.ReplayCounter)
+
+	copy(buf[13:45], ek.Nonce)
+	copy(buf[45:61], ek.IV)
+	binary.BigEndian.PutUint64(buf[61:69], ek.RSC)
+	binary.BigEndian.PutUint64(buf[69:77], ek.ID)
+	copy(buf[77:93], ek.MIC)
+
+	binary.BigEndian.PutUint16(buf[93:95], ek.KeyDataLength)
+	if len(ek.EncryptedKeyData) > 0 {
+		copy(buf[95:95+len(ek.EncryptedKeyData)], ek.EncryptedKeyData)
+	}
+
+	return nil
+}
+
+func decodeEAPOLKey(data []byte, p gopacket.PacketBuilder) error {
+	ek := &EAPOLKey{}
+	return decodingLayerDecoder(ek, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/endpoints.go b/go-controller/vendor/github.com/google/gopacket/layers/endpoints.go
new file mode 100644
index 00000000000..4c91cc33247
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/endpoints.go
@@ -0,0 +1,97 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"github.com/google/gopacket"
+	"net"
+	"strconv"
+)
+
+var (
+	// We use two different endpoint types for IPv4 vs IPv6 addresses, so that
+	// ordering with endpointA.LessThan(endpointB) sanely groups all IPv4
+	// addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses.
+	EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string {
+		return net.IP(b).String()
+	}})
+	EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string {
+		return net.IP(b).String()
+	}})
+
+	EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string {
+		return net.HardwareAddr(b).String()
+	}})
+	EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string {
+		return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
+	}})
+	EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string {
+		return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
+	}})
+	EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string {
+		return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
+	}})
+	EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string {
+		return strconv.Itoa(int(b[0]))
+	}})
+	EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string {
+		return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
+	}})
+	EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string {
+		return "point"
+	}})
+)
+
+// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address.
+// It returns gopacket.InvalidEndpoint if the IP address is invalid.
+func NewIPEndpoint(a net.IP) gopacket.Endpoint {
+	ipv4 := a.To4()
+	if ipv4 != nil {
+		return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4))
+	}
+
+	ipv6 := a.To16()
+	if ipv6 != nil {
+		return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6))
+	}
+
+	return gopacket.InvalidEndpoint
+}
+
+// NewMACEndpoint returns a new MAC address endpoint.
+func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint {
+	return gopacket.NewEndpoint(EndpointMAC, []byte(a))
+}
+func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint {
+	return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)})
+}
+
+// NewTCPPortEndpoint returns an endpoint based on a TCP port.
+func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint {
+	return newPortEndpoint(EndpointTCPPort, uint16(p))
+}
+
+// NewUDPPortEndpoint returns an endpoint based on a UDP port.
+func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint {
+	return newPortEndpoint(EndpointUDPPort, uint16(p))
+}
+
+// NewSCTPPortEndpoint returns an endpoint based on a SCTP port.
+func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint {
+	return newPortEndpoint(EndpointSCTPPort, uint16(p))
+}
+
+// NewRUDPPortEndpoint returns an endpoint based on a RUDP port.
+func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint {
+	return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)})
+}
+
+// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port.
+func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint {
+	return newPortEndpoint(EndpointUDPLitePort, uint16(p))
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/enums.go b/go-controller/vendor/github.com/google/gopacket/layers/enums.go
new file mode 100644
index 00000000000..8427bdaf47d
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/enums.go
@@ -0,0 +1,443 @@
+// 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 (
+	"fmt"
+	"runtime"
+
+	"github.com/google/gopacket"
+)
+
+// EnumMetadata keeps track of a set of metadata for each enumeration value
+// for protocol enumerations.
+type EnumMetadata struct {
+	// DecodeWith is the decoder to use to decode this protocol's data.
+	DecodeWith gopacket.Decoder
+	// Name is the name of the enumeration value.
+	Name string
+	// LayerType is the layer type implied by the given enum.
+	LayerType gopacket.LayerType
+}
+
+// EthernetType is an enumeration of ethernet type values, and acts as a decoder
+// for any type it supports.
+type EthernetType uint16
+
+const (
+	// EthernetTypeLLC is not an actual ethernet type.  It is instead a
+	// placeholder we use in Ethernet frames that use the 802.3 standard of
+	// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
+	EthernetTypeLLC                         EthernetType = 0
+	EthernetTypeIPv4                        EthernetType = 0x0800
+	EthernetTypeARP                         EthernetType = 0x0806
+	EthernetTypeIPv6                        EthernetType = 0x86DD
+	EthernetTypeCiscoDiscovery              EthernetType = 0x2000
+	EthernetTypeNortelDiscovery             EthernetType = 0x01a2
+	EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
+	EthernetTypeDot1Q                       EthernetType = 0x8100
+	EthernetTypePPP                         EthernetType = 0x880b
+	EthernetTypePPPoEDiscovery              EthernetType = 0x8863
+	EthernetTypePPPoESession                EthernetType = 0x8864
+	EthernetTypeMPLSUnicast                 EthernetType = 0x8847
+	EthernetTypeMPLSMulticast               EthernetType = 0x8848
+	EthernetTypeEAPOL                       EthernetType = 0x888e
+	EthernetTypeERSPAN                      EthernetType = 0x88be
+	EthernetTypeQinQ                        EthernetType = 0x88a8
+	EthernetTypeLinkLayerDiscovery          EthernetType = 0x88cc
+	EthernetTypeEthernetCTP                 EthernetType = 0x9000
+)
+
+// IPProtocol is an enumeration of IP protocol values, and acts as a decoder
+// for any type it supports.
+type IPProtocol uint8
+
+const (
+	IPProtocolIPv6HopByHop    IPProtocol = 0
+	IPProtocolICMPv4          IPProtocol = 1
+	IPProtocolIGMP            IPProtocol = 2
+	IPProtocolIPv4            IPProtocol = 4
+	IPProtocolTCP             IPProtocol = 6
+	IPProtocolUDP             IPProtocol = 17
+	IPProtocolRUDP            IPProtocol = 27
+	IPProtocolIPv6            IPProtocol = 41
+	IPProtocolIPv6Routing     IPProtocol = 43
+	IPProtocolIPv6Fragment    IPProtocol = 44
+	IPProtocolGRE             IPProtocol = 47
+	IPProtocolESP             IPProtocol = 50
+	IPProtocolAH              IPProtocol = 51
+	IPProtocolICMPv6          IPProtocol = 58
+	IPProtocolNoNextHeader    IPProtocol = 59
+	IPProtocolIPv6Destination IPProtocol = 60
+	IPProtocolOSPF            IPProtocol = 89
+	IPProtocolIPIP            IPProtocol = 94
+	IPProtocolEtherIP         IPProtocol = 97
+	IPProtocolVRRP            IPProtocol = 112
+	IPProtocolSCTP            IPProtocol = 132
+	IPProtocolUDPLite         IPProtocol = 136
+	IPProtocolMPLSInIP        IPProtocol = 137
+)
+
+// LinkType is an enumeration of link types, and acts as a decoder for any
+// link type it supports.
+type LinkType uint8
+
+const (
+	// According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html
+	LinkTypeNull           LinkType = 0
+	LinkTypeEthernet       LinkType = 1
+	LinkTypeAX25           LinkType = 3
+	LinkTypeTokenRing      LinkType = 6
+	LinkTypeArcNet         LinkType = 7
+	LinkTypeSLIP           LinkType = 8
+	LinkTypePPP            LinkType = 9
+	LinkTypeFDDI           LinkType = 10
+	LinkTypePPP_HDLC       LinkType = 50
+	LinkTypePPPEthernet    LinkType = 51
+	LinkTypeATM_RFC1483    LinkType = 100
+	LinkTypeRaw            LinkType = 101
+	LinkTypeC_HDLC         LinkType = 104
+	LinkTypeIEEE802_11     LinkType = 105
+	LinkTypeFRelay         LinkType = 107
+	LinkTypeLoop           LinkType = 108
+	LinkTypeLinuxSLL       LinkType = 113
+	LinkTypeLTalk          LinkType = 114
+	LinkTypePFLog          LinkType = 117
+	LinkTypePrismHeader    LinkType = 119
+	LinkTypeIPOverFC       LinkType = 122
+	LinkTypeSunATM         LinkType = 123
+	LinkTypeIEEE80211Radio LinkType = 127
+	LinkTypeARCNetLinux    LinkType = 129
+	LinkTypeIPOver1394     LinkType = 138
+	LinkTypeMTP2Phdr       LinkType = 139
+	LinkTypeMTP2           LinkType = 140
+	LinkTypeMTP3           LinkType = 141
+	LinkTypeSCCP           LinkType = 142
+	LinkTypeDOCSIS         LinkType = 143
+	LinkTypeLinuxIRDA      LinkType = 144
+	LinkTypeLinuxLAPD      LinkType = 177
+	LinkTypeLinuxUSB       LinkType = 220
+	LinkTypeFC2            LinkType = 224
+	LinkTypeFC2Framed      LinkType = 225
+	LinkTypeIPv4           LinkType = 228
+	LinkTypeIPv6           LinkType = 229
+)
+
+// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516
+type PPPoECode uint8
+
+const (
+	PPPoECodePADI    PPPoECode = 0x09
+	PPPoECodePADO    PPPoECode = 0x07
+	PPPoECodePADR    PPPoECode = 0x19
+	PPPoECodePADS    PPPoECode = 0x65
+	PPPoECodePADT    PPPoECode = 0xA7
+	PPPoECodeSession PPPoECode = 0x00
+)
+
+// PPPType is an enumeration of PPP type values, and acts as a decoder for any
+// type it supports.
+type PPPType uint16
+
+const (
+	PPPTypeIPv4          PPPType = 0x0021
+	PPPTypeIPv6          PPPType = 0x0057
+	PPPTypeMPLSUnicast   PPPType = 0x0281
+	PPPTypeMPLSMulticast PPPType = 0x0283
+)
+
+// SCTPChunkType is an enumeration of chunk types inside SCTP packets.
+type SCTPChunkType uint8
+
+const (
+	SCTPChunkTypeData             SCTPChunkType = 0
+	SCTPChunkTypeInit             SCTPChunkType = 1
+	SCTPChunkTypeInitAck          SCTPChunkType = 2
+	SCTPChunkTypeSack             SCTPChunkType = 3
+	SCTPChunkTypeHeartbeat        SCTPChunkType = 4
+	SCTPChunkTypeHeartbeatAck     SCTPChunkType = 5
+	SCTPChunkTypeAbort            SCTPChunkType = 6
+	SCTPChunkTypeShutdown         SCTPChunkType = 7
+	SCTPChunkTypeShutdownAck      SCTPChunkType = 8
+	SCTPChunkTypeError            SCTPChunkType = 9
+	SCTPChunkTypeCookieEcho       SCTPChunkType = 10
+	SCTPChunkTypeCookieAck        SCTPChunkType = 11
+	SCTPChunkTypeShutdownComplete SCTPChunkType = 14
+)
+
+// FDDIFrameControl is an enumeration of FDDI frame control bytes.
+type FDDIFrameControl uint8
+
+const (
+	FDDIFrameControlLLC FDDIFrameControl = 0x50
+)
+
+// EAPOLType is an enumeration of EAPOL packet types.
+type EAPOLType uint8
+
+const (
+	EAPOLTypeEAP      EAPOLType = 0
+	EAPOLTypeStart    EAPOLType = 1
+	EAPOLTypeLogOff   EAPOLType = 2
+	EAPOLTypeKey      EAPOLType = 3
+	EAPOLTypeASFAlert EAPOLType = 4
+)
+
+// ProtocolFamily is the set of values defined as PF_* in sys/socket.h
+type ProtocolFamily uint8
+
+const (
+	ProtocolFamilyIPv4 ProtocolFamily = 2
+	// BSDs use different values for INET6... glory be.  These values taken from
+	// tcpdump 4.3.0.
+	ProtocolFamilyIPv6BSD     ProtocolFamily = 24
+	ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28
+	ProtocolFamilyIPv6Darwin  ProtocolFamily = 30
+	ProtocolFamilyIPv6Linux   ProtocolFamily = 10
+)
+
+// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields.
+// By combining these two fields together into a single type, we're able to
+// provide a String function that correctly displays the subtype given the
+// top-level type.
+//
+// If you just care about the top-level type, use the MainType function.
+type Dot11Type uint8
+
+// MainType strips the subtype information from the given type,
+// returning just the overarching type (Mgmt, Ctrl, Data, Reserved).
+func (d Dot11Type) MainType() Dot11Type {
+	return d & dot11TypeMask
+}
+
+func (d Dot11Type) QOS() bool {
+	return d&dot11QOSMask == Dot11TypeDataQOSData
+}
+
+const (
+	Dot11TypeMgmt     Dot11Type = 0x00
+	Dot11TypeCtrl     Dot11Type = 0x01
+	Dot11TypeData     Dot11Type = 0x02
+	Dot11TypeReserved Dot11Type = 0x03
+	dot11TypeMask               = 0x03
+	dot11QOSMask                = 0x23
+
+	// The following are type/subtype conglomerations.
+
+	// Management
+	Dot11TypeMgmtAssociationReq    Dot11Type = 0x00
+	Dot11TypeMgmtAssociationResp   Dot11Type = 0x04
+	Dot11TypeMgmtReassociationReq  Dot11Type = 0x08
+	Dot11TypeMgmtReassociationResp Dot11Type = 0x0c
+	Dot11TypeMgmtProbeReq          Dot11Type = 0x10
+	Dot11TypeMgmtProbeResp         Dot11Type = 0x14
+	Dot11TypeMgmtMeasurementPilot  Dot11Type = 0x18
+	Dot11TypeMgmtBeacon            Dot11Type = 0x20
+	Dot11TypeMgmtATIM              Dot11Type = 0x24
+	Dot11TypeMgmtDisassociation    Dot11Type = 0x28
+	Dot11TypeMgmtAuthentication    Dot11Type = 0x2c
+	Dot11TypeMgmtDeauthentication  Dot11Type = 0x30
+	Dot11TypeMgmtAction            Dot11Type = 0x34
+	Dot11TypeMgmtActionNoAck       Dot11Type = 0x38
+
+	// Control
+	Dot11TypeCtrlWrapper       Dot11Type = 0x1d
+	Dot11TypeCtrlBlockAckReq   Dot11Type = 0x21
+	Dot11TypeCtrlBlockAck      Dot11Type = 0x25
+	Dot11TypeCtrlPowersavePoll Dot11Type = 0x29
+	Dot11TypeCtrlRTS           Dot11Type = 0x2d
+	Dot11TypeCtrlCTS           Dot11Type = 0x31
+	Dot11TypeCtrlAck           Dot11Type = 0x35
+	Dot11TypeCtrlCFEnd         Dot11Type = 0x39
+	Dot11TypeCtrlCFEndAck      Dot11Type = 0x3d
+
+	// Data
+	Dot11TypeDataCFAck              Dot11Type = 0x06
+	Dot11TypeDataCFPoll             Dot11Type = 0x0a
+	Dot11TypeDataCFAckPoll          Dot11Type = 0x0e
+	Dot11TypeDataNull               Dot11Type = 0x12
+	Dot11TypeDataCFAckNoData        Dot11Type = 0x16
+	Dot11TypeDataCFPollNoData       Dot11Type = 0x1a
+	Dot11TypeDataCFAckPollNoData    Dot11Type = 0x1e
+	Dot11TypeDataQOSData            Dot11Type = 0x22
+	Dot11TypeDataQOSDataCFAck       Dot11Type = 0x26
+	Dot11TypeDataQOSDataCFPoll      Dot11Type = 0x2a
+	Dot11TypeDataQOSDataCFAckPoll   Dot11Type = 0x2e
+	Dot11TypeDataQOSNull            Dot11Type = 0x32
+	Dot11TypeDataQOSCFPollNoData    Dot11Type = 0x3a
+	Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e
+)
+
+// Decode a raw v4 or v6 IP packet.
+func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error {
+	version := data[0] >> 4
+	switch version {
+	case 4:
+		return decodeIPv4(data, p)
+	case 6:
+		return decodeIPv6(data, p)
+	}
+	return fmt.Errorf("Invalid IP packet version %v", version)
+}
+
+func initActualTypeData() {
+	// Each of the XXXTypeMetadata arrays contains mappings of how to handle enum
+	// values for various enum types in gopacket/layers.
+	// These arrays are actually created by gen2.go and stored in
+	// enums_generated.go.
+	//
+	// So, EthernetTypeMetadata[2] contains information on how to handle EthernetType
+	// 2, including which name to give it and which decoder to use to decode
+	// packet data of that type.  These arrays are filled by default with all of the
+	// protocols gopacket/layers knows how to handle, but users of the library can
+	// add new decoders or override existing ones.  For example, if you write a better
+	// TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith
+	// with your new decoder, and all gopacket/layers decoding will use your new
+	// decoder whenever they encounter that IPProtocol.
+
+	// Here we link up all enumerations with their respective names and decoders.
+	EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC}
+	EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
+	EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+	EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP}
+	EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
+	EthernetTypeMetadata[EthernetTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP", LayerType: LayerTypePPP}
+	EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE}
+	EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE}
+	EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP}
+	EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery}
+	EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery}
+	EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery}
+	EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS}
+	EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS}
+	EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
+	EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
+	EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
+	EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII}
+
+	IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
+	IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
+	IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP}
+	IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4}
+	IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6}
+	IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP}
+	IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+	IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
+	IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP}
+	IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP}
+	IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE}
+	IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop}
+	IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing}
+	IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment}
+	IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination}
+	IPProtocolMetadata[IPProtocolOSPF] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeOSPF), Name: "OSPF", LayerType: LayerTypeOSPF}
+	IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH}
+	IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP}
+	IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite}
+	IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS}
+	IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload}
+	IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP}
+	IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP}
+
+	SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"}
+	SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"}
+
+	PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"}
+	PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"}
+	PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"}
+	PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"}
+
+	PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
+
+	LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
+	LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
+	LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"}
+	LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"}
+	LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"}
+	LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"}
+	LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"}
+	LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
+	// See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85
+	// Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508
+	if runtime.GOOS == "openbsd" {
+		LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
+	} else {
+		LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
+	}
+	LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
+	LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
+	LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}
+	LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"}
+	LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"}
+
+	FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"}
+
+	EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP}
+	EAPOLTypeMetadata[EAPOLTypeKey] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOLKey), Name: "EAPOLKey", LayerType: LayerTypeEAPOLKey}
+
+	ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
+	ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+	ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+	ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+	ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
+
+	Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq}
+	Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp}
+	Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq}
+	Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp}
+	Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq}
+	Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp}
+	Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot}
+	Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon}
+	Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM}
+	Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation}
+	Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication}
+	Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication}
+	Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction}
+	Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck}
+	Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl}
+	Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl}
+	Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq}
+	Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck}
+	Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll}
+	Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS}
+	Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS}
+	Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck}
+	Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd}
+	Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck}
+	Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data}
+	Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck}
+	Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll}
+	Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll}
+	Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull}
+	Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData}
+	Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData}
+	Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData}
+	Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData}
+	Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck}
+	Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll}
+	Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll}
+	Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull}
+	Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData}
+	Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData}
+
+	USBTransportTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
+	USBTransportTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
+	USBTransportTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/enums_generated.go b/go-controller/vendor/github.com/google/gopacket/layers/enums_generated.go
new file mode 100644
index 00000000000..bf77aac5013
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/enums_generated.go
@@ -0,0 +1,434 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+
+package layers
+
+// Created by gen2.go, don't edit manually
+// Generated at 2017-10-23 10:20:24.458771856 -0600 MDT m=+0.001159033
+
+import (
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+func init() {
+	initUnknownTypesForLinkType()
+	initUnknownTypesForEthernetType()
+	initUnknownTypesForPPPType()
+	initUnknownTypesForIPProtocol()
+	initUnknownTypesForSCTPChunkType()
+	initUnknownTypesForPPPoECode()
+	initUnknownTypesForFDDIFrameControl()
+	initUnknownTypesForEAPOLType()
+	initUnknownTypesForProtocolFamily()
+	initUnknownTypesForDot11Type()
+	initUnknownTypesForUSBTransportType()
+	initActualTypeData()
+}
+
+// Decoder calls LinkTypeMetadata.DecodeWith's decoder.
+func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns LinkTypeMetadata.Name.
+func (a LinkType) String() string {
+	return LinkTypeMetadata[a].Name
+}
+
+// LayerType returns LinkTypeMetadata.LayerType.
+func (a LinkType) LayerType() gopacket.LayerType {
+	return LinkTypeMetadata[a].LayerType
+}
+
+type errorDecoderForLinkType int
+
+func (a *errorDecoderForLinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForLinkType) Error() string {
+	return fmt.Sprintf("Unable to decode LinkType %d", int(*a))
+}
+
+var errorDecodersForLinkType [256]errorDecoderForLinkType
+var LinkTypeMetadata [256]EnumMetadata
+
+func initUnknownTypesForLinkType() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForLinkType[i] = errorDecoderForLinkType(i)
+		LinkTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForLinkType[i],
+			Name:       "UnknownLinkType",
+		}
+	}
+}
+
+// Decoder calls EthernetTypeMetadata.DecodeWith's decoder.
+func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return EthernetTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns EthernetTypeMetadata.Name.
+func (a EthernetType) String() string {
+	return EthernetTypeMetadata[a].Name
+}
+
+// LayerType returns EthernetTypeMetadata.LayerType.
+func (a EthernetType) LayerType() gopacket.LayerType {
+	return EthernetTypeMetadata[a].LayerType
+}
+
+type errorDecoderForEthernetType int
+
+func (a *errorDecoderForEthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForEthernetType) Error() string {
+	return fmt.Sprintf("Unable to decode EthernetType %d", int(*a))
+}
+
+var errorDecodersForEthernetType [65536]errorDecoderForEthernetType
+var EthernetTypeMetadata [65536]EnumMetadata
+
+func initUnknownTypesForEthernetType() {
+	for i := 0; i < 65536; i++ {
+		errorDecodersForEthernetType[i] = errorDecoderForEthernetType(i)
+		EthernetTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForEthernetType[i],
+			Name:       "UnknownEthernetType",
+		}
+	}
+}
+
+// Decoder calls PPPTypeMetadata.DecodeWith's decoder.
+func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return PPPTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns PPPTypeMetadata.Name.
+func (a PPPType) String() string {
+	return PPPTypeMetadata[a].Name
+}
+
+// LayerType returns PPPTypeMetadata.LayerType.
+func (a PPPType) LayerType() gopacket.LayerType {
+	return PPPTypeMetadata[a].LayerType
+}
+
+type errorDecoderForPPPType int
+
+func (a *errorDecoderForPPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForPPPType) Error() string {
+	return fmt.Sprintf("Unable to decode PPPType %d", int(*a))
+}
+
+var errorDecodersForPPPType [65536]errorDecoderForPPPType
+var PPPTypeMetadata [65536]EnumMetadata
+
+func initUnknownTypesForPPPType() {
+	for i := 0; i < 65536; i++ {
+		errorDecodersForPPPType[i] = errorDecoderForPPPType(i)
+		PPPTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForPPPType[i],
+			Name:       "UnknownPPPType",
+		}
+	}
+}
+
+// Decoder calls IPProtocolMetadata.DecodeWith's decoder.
+func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return IPProtocolMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns IPProtocolMetadata.Name.
+func (a IPProtocol) String() string {
+	return IPProtocolMetadata[a].Name
+}
+
+// LayerType returns IPProtocolMetadata.LayerType.
+func (a IPProtocol) LayerType() gopacket.LayerType {
+	return IPProtocolMetadata[a].LayerType
+}
+
+type errorDecoderForIPProtocol int
+
+func (a *errorDecoderForIPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForIPProtocol) Error() string {
+	return fmt.Sprintf("Unable to decode IPProtocol %d", int(*a))
+}
+
+var errorDecodersForIPProtocol [256]errorDecoderForIPProtocol
+var IPProtocolMetadata [256]EnumMetadata
+
+func initUnknownTypesForIPProtocol() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForIPProtocol[i] = errorDecoderForIPProtocol(i)
+		IPProtocolMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForIPProtocol[i],
+			Name:       "UnknownIPProtocol",
+		}
+	}
+}
+
+// Decoder calls SCTPChunkTypeMetadata.DecodeWith's decoder.
+func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns SCTPChunkTypeMetadata.Name.
+func (a SCTPChunkType) String() string {
+	return SCTPChunkTypeMetadata[a].Name
+}
+
+// LayerType returns SCTPChunkTypeMetadata.LayerType.
+func (a SCTPChunkType) LayerType() gopacket.LayerType {
+	return SCTPChunkTypeMetadata[a].LayerType
+}
+
+type errorDecoderForSCTPChunkType int
+
+func (a *errorDecoderForSCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForSCTPChunkType) Error() string {
+	return fmt.Sprintf("Unable to decode SCTPChunkType %d", int(*a))
+}
+
+var errorDecodersForSCTPChunkType [256]errorDecoderForSCTPChunkType
+var SCTPChunkTypeMetadata [256]EnumMetadata
+
+func initUnknownTypesForSCTPChunkType() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForSCTPChunkType[i] = errorDecoderForSCTPChunkType(i)
+		SCTPChunkTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForSCTPChunkType[i],
+			Name:       "UnknownSCTPChunkType",
+		}
+	}
+}
+
+// Decoder calls PPPoECodeMetadata.DecodeWith's decoder.
+func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return PPPoECodeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns PPPoECodeMetadata.Name.
+func (a PPPoECode) String() string {
+	return PPPoECodeMetadata[a].Name
+}
+
+// LayerType returns PPPoECodeMetadata.LayerType.
+func (a PPPoECode) LayerType() gopacket.LayerType {
+	return PPPoECodeMetadata[a].LayerType
+}
+
+type errorDecoderForPPPoECode int
+
+func (a *errorDecoderForPPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForPPPoECode) Error() string {
+	return fmt.Sprintf("Unable to decode PPPoECode %d", int(*a))
+}
+
+var errorDecodersForPPPoECode [256]errorDecoderForPPPoECode
+var PPPoECodeMetadata [256]EnumMetadata
+
+func initUnknownTypesForPPPoECode() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForPPPoECode[i] = errorDecoderForPPPoECode(i)
+		PPPoECodeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForPPPoECode[i],
+			Name:       "UnknownPPPoECode",
+		}
+	}
+}
+
+// Decoder calls FDDIFrameControlMetadata.DecodeWith's decoder.
+func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns FDDIFrameControlMetadata.Name.
+func (a FDDIFrameControl) String() string {
+	return FDDIFrameControlMetadata[a].Name
+}
+
+// LayerType returns FDDIFrameControlMetadata.LayerType.
+func (a FDDIFrameControl) LayerType() gopacket.LayerType {
+	return FDDIFrameControlMetadata[a].LayerType
+}
+
+type errorDecoderForFDDIFrameControl int
+
+func (a *errorDecoderForFDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForFDDIFrameControl) Error() string {
+	return fmt.Sprintf("Unable to decode FDDIFrameControl %d", int(*a))
+}
+
+var errorDecodersForFDDIFrameControl [256]errorDecoderForFDDIFrameControl
+var FDDIFrameControlMetadata [256]EnumMetadata
+
+func initUnknownTypesForFDDIFrameControl() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForFDDIFrameControl[i] = errorDecoderForFDDIFrameControl(i)
+		FDDIFrameControlMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForFDDIFrameControl[i],
+			Name:       "UnknownFDDIFrameControl",
+		}
+	}
+}
+
+// Decoder calls EAPOLTypeMetadata.DecodeWith's decoder.
+func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns EAPOLTypeMetadata.Name.
+func (a EAPOLType) String() string {
+	return EAPOLTypeMetadata[a].Name
+}
+
+// LayerType returns EAPOLTypeMetadata.LayerType.
+func (a EAPOLType) LayerType() gopacket.LayerType {
+	return EAPOLTypeMetadata[a].LayerType
+}
+
+type errorDecoderForEAPOLType int
+
+func (a *errorDecoderForEAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForEAPOLType) Error() string {
+	return fmt.Sprintf("Unable to decode EAPOLType %d", int(*a))
+}
+
+var errorDecodersForEAPOLType [256]errorDecoderForEAPOLType
+var EAPOLTypeMetadata [256]EnumMetadata
+
+func initUnknownTypesForEAPOLType() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForEAPOLType[i] = errorDecoderForEAPOLType(i)
+		EAPOLTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForEAPOLType[i],
+			Name:       "UnknownEAPOLType",
+		}
+	}
+}
+
+// Decoder calls ProtocolFamilyMetadata.DecodeWith's decoder.
+func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns ProtocolFamilyMetadata.Name.
+func (a ProtocolFamily) String() string {
+	return ProtocolFamilyMetadata[a].Name
+}
+
+// LayerType returns ProtocolFamilyMetadata.LayerType.
+func (a ProtocolFamily) LayerType() gopacket.LayerType {
+	return ProtocolFamilyMetadata[a].LayerType
+}
+
+type errorDecoderForProtocolFamily int
+
+func (a *errorDecoderForProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForProtocolFamily) Error() string {
+	return fmt.Sprintf("Unable to decode ProtocolFamily %d", int(*a))
+}
+
+var errorDecodersForProtocolFamily [256]errorDecoderForProtocolFamily
+var ProtocolFamilyMetadata [256]EnumMetadata
+
+func initUnknownTypesForProtocolFamily() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForProtocolFamily[i] = errorDecoderForProtocolFamily(i)
+		ProtocolFamilyMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForProtocolFamily[i],
+			Name:       "UnknownProtocolFamily",
+		}
+	}
+}
+
+// Decoder calls Dot11TypeMetadata.DecodeWith's decoder.
+func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return Dot11TypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns Dot11TypeMetadata.Name.
+func (a Dot11Type) String() string {
+	return Dot11TypeMetadata[a].Name
+}
+
+// LayerType returns Dot11TypeMetadata.LayerType.
+func (a Dot11Type) LayerType() gopacket.LayerType {
+	return Dot11TypeMetadata[a].LayerType
+}
+
+type errorDecoderForDot11Type int
+
+func (a *errorDecoderForDot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForDot11Type) Error() string {
+	return fmt.Sprintf("Unable to decode Dot11Type %d", int(*a))
+}
+
+var errorDecodersForDot11Type [256]errorDecoderForDot11Type
+var Dot11TypeMetadata [256]EnumMetadata
+
+func initUnknownTypesForDot11Type() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForDot11Type[i] = errorDecoderForDot11Type(i)
+		Dot11TypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForDot11Type[i],
+			Name:       "UnknownDot11Type",
+		}
+	}
+}
+
+// Decoder calls USBTransportTypeMetadata.DecodeWith's decoder.
+func (a USBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return USBTransportTypeMetadata[a].DecodeWith.Decode(data, p)
+}
+
+// String returns USBTransportTypeMetadata.Name.
+func (a USBTransportType) String() string {
+	return USBTransportTypeMetadata[a].Name
+}
+
+// LayerType returns USBTransportTypeMetadata.LayerType.
+func (a USBTransportType) LayerType() gopacket.LayerType {
+	return USBTransportTypeMetadata[a].LayerType
+}
+
+type errorDecoderForUSBTransportType int
+
+func (a *errorDecoderForUSBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
+	return a
+}
+func (a *errorDecoderForUSBTransportType) Error() string {
+	return fmt.Sprintf("Unable to decode USBTransportType %d", int(*a))
+}
+
+var errorDecodersForUSBTransportType [256]errorDecoderForUSBTransportType
+var USBTransportTypeMetadata [256]EnumMetadata
+
+func initUnknownTypesForUSBTransportType() {
+	for i := 0; i < 256; i++ {
+		errorDecodersForUSBTransportType[i] = errorDecoderForUSBTransportType(i)
+		USBTransportTypeMetadata[i] = EnumMetadata{
+			DecodeWith: &errorDecodersForUSBTransportType[i],
+			Name:       "UnknownUSBTransportType",
+		}
+	}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/erspan2.go b/go-controller/vendor/github.com/google/gopacket/layers/erspan2.go
new file mode 100644
index 00000000000..154436205ef
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/erspan2.go
@@ -0,0 +1,86 @@
+// 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"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	//ERSPANIIVersionObsolete - The obsolete value for the version field
+	ERSPANIIVersionObsolete = 0x0
+	// ERSPANIIVersion - The current value for the version field
+	ERSPANIIVersion = 0x1
+)
+
+// ERSPANII contains all of the fields found in an ERSPAN Type II header
+// https://tools.ietf.org/html/draft-foschiano-erspan-03
+type ERSPANII struct {
+	BaseLayer
+	IsTruncated                         bool
+	Version, CoS, TrunkEncap            uint8
+	VLANIdentifier, SessionID, Reserved uint16
+	Index                               uint32
+}
+
+func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPANII }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	erspan2Length := 8
+	erspan2.Version = data[0] & 0xF0 >> 4
+	erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
+	erspan2.CoS = data[2] & 0xE0 >> 5
+	erspan2.TrunkEncap = data[2] & 0x18 >> 3
+	erspan2.IsTruncated = data[2]&0x4>>2 != 0
+	erspan2.SessionID = binary.BigEndian.Uint16(data[2:4]) & 0x03FF
+	erspan2.Reserved = binary.BigEndian.Uint16(data[4:6]) & 0xFFF0 >> 4
+	erspan2.Index = binary.BigEndian.Uint32(data[4:8]) & 0x000FFFFF
+	erspan2.Contents = data[:erspan2Length]
+	erspan2.Payload = data[erspan2Length:]
+	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 (erspan2 *ERSPANII) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+
+	twoByteInt := uint16(erspan2.Version&0xF)<<12 | erspan2.VLANIdentifier&0x0FFF
+	binary.BigEndian.PutUint16(bytes, twoByteInt)
+
+	twoByteInt = uint16(erspan2.CoS&0x7)<<13 | uint16(erspan2.TrunkEncap&0x3)<<11 | erspan2.SessionID&0x03FF
+	if erspan2.IsTruncated {
+		twoByteInt |= 0x400
+	}
+	binary.BigEndian.PutUint16(bytes[2:], twoByteInt)
+
+	fourByteInt := uint32(erspan2.Reserved&0x0FFF)<<20 | erspan2.Index&0x000FFFFF
+	binary.BigEndian.PutUint32(bytes[4:], fourByteInt)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (erspan2 *ERSPANII) CanDecode() gopacket.LayerClass {
+	return LayerTypeERSPANII
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (erspan2 *ERSPANII) NextLayerType() gopacket.LayerType {
+	return LayerTypeEthernet
+}
+
+func decodeERSPANII(data []byte, p gopacket.PacketBuilder) error {
+	erspan2 := &ERSPANII{}
+	return decodingLayerDecoder(erspan2, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/etherip.go b/go-controller/vendor/github.com/google/gopacket/layers/etherip.go
new file mode 100644
index 00000000000..5b7b7229eca
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/etherip.go
@@ -0,0 +1,45 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"github.com/google/gopacket"
+)
+
+// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
+type EtherIP struct {
+	BaseLayer
+	Version  uint8
+	Reserved uint16
+}
+
+// LayerType returns gopacket.LayerTypeEtherIP.
+func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	e.Version = data[0] >> 4
+	e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
+	e.BaseLayer = BaseLayer{data[:2], data[2:]}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (e *EtherIP) CanDecode() gopacket.LayerClass {
+	return LayerTypeEtherIP
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (e *EtherIP) NextLayerType() gopacket.LayerType {
+	return LayerTypeEthernet
+}
+
+func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
+	e := &EtherIP{}
+	return decodingLayerDecoder(e, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ethernet.go b/go-controller/vendor/github.com/google/gopacket/layers/ethernet.go
new file mode 100644
index 00000000000..b73748f2f7d
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ethernet.go
@@ -0,0 +1,123 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"github.com/google/gopacket"
+	"net"
+)
+
+// EthernetBroadcast is the broadcast MAC address used by Ethernet.
+var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+
+// Ethernet is the layer for Ethernet frame headers.
+type Ethernet struct {
+	BaseLayer
+	SrcMAC, DstMAC net.HardwareAddr
+	EthernetType   EthernetType
+	// Length is only set if a length field exists within this header.  Ethernet
+	// headers follow two different standards, one that uses an EthernetType, the
+	// other which defines a length the follows with a LLC header (802.3).  If the
+	// former is the case, we set EthernetType and Length stays 0.  In the latter
+	// case, we set Length and EthernetType = EthernetTypeLLC.
+	Length uint16
+}
+
+// LayerType returns LayerTypeEthernet
+func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet }
+
+func (e *Ethernet) LinkFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC)
+}
+
+func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 14 {
+		return errors.New("Ethernet packet too small")
+	}
+	eth.DstMAC = net.HardwareAddr(data[0:6])
+	eth.SrcMAC = net.HardwareAddr(data[6:12])
+	eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
+	eth.BaseLayer = BaseLayer{data[:14], data[14:]}
+	eth.Length = 0
+	if eth.EthernetType < 0x0600 {
+		eth.Length = uint16(eth.EthernetType)
+		eth.EthernetType = EthernetTypeLLC
+		if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
+			df.SetTruncated()
+		} else if cmp > 0 {
+			// Strip off bytes at the end, since we have too many bytes
+			eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
+		}
+		//	fmt.Println(eth)
+	}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if len(eth.DstMAC) != 6 {
+		return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC)
+	}
+	if len(eth.SrcMAC) != 6 {
+		return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC)
+	}
+	payload := b.Bytes()
+	bytes, err := b.PrependBytes(14)
+	if err != nil {
+		return err
+	}
+	copy(bytes, eth.DstMAC)
+	copy(bytes[6:], eth.SrcMAC)
+	if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC {
+		if opts.FixLengths {
+			eth.Length = uint16(len(payload))
+		}
+		if eth.EthernetType != EthernetTypeLLC {
+			return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length)
+		} else if eth.Length > 0x0600 {
+			return fmt.Errorf("invalid ethernet length %v", eth.Length)
+		}
+		binary.BigEndian.PutUint16(bytes[12:], eth.Length)
+	} else {
+		binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType))
+	}
+	length := len(b.Bytes())
+	if length < 60 {
+		// Pad out to 60 bytes.
+		padding, err := b.AppendBytes(60 - length)
+		if err != nil {
+			return err
+		}
+		copy(padding, lotsOfZeros[:])
+	}
+	return nil
+}
+
+func (eth *Ethernet) CanDecode() gopacket.LayerClass {
+	return LayerTypeEthernet
+}
+
+func (eth *Ethernet) NextLayerType() gopacket.LayerType {
+	return eth.EthernetType.LayerType()
+}
+
+func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
+	eth := &Ethernet{}
+	err := eth.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(eth)
+	p.SetLinkLayer(eth)
+	return p.NextDecoder(eth.EthernetType)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/fddi.go b/go-controller/vendor/github.com/google/gopacket/layers/fddi.go
new file mode 100644
index 00000000000..ed9e1957b92
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/fddi.go
@@ -0,0 +1,41 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"github.com/google/gopacket"
+	"net"
+)
+
+// FDDI contains the header for FDDI frames.
+type FDDI struct {
+	BaseLayer
+	FrameControl   FDDIFrameControl
+	Priority       uint8
+	SrcMAC, DstMAC net.HardwareAddr
+}
+
+// LayerType returns LayerTypeFDDI.
+func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI }
+
+// LinkFlow returns a new flow of type EndpointMAC.
+func (f *FDDI) LinkFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC)
+}
+
+func decodeFDDI(data []byte, p gopacket.PacketBuilder) error {
+	f := &FDDI{
+		FrameControl: FDDIFrameControl(data[0] & 0xF8),
+		Priority:     data[0] & 0x07,
+		SrcMAC:       net.HardwareAddr(data[1:7]),
+		DstMAC:       net.HardwareAddr(data[7:13]),
+		BaseLayer:    BaseLayer{data[:13], data[13:]},
+	}
+	p.SetLinkLayer(f)
+	p.AddLayer(f)
+	return p.NextDecoder(f.FrameControl)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/fuzz_layer.go b/go-controller/vendor/github.com/google/gopacket/layers/fuzz_layer.go
new file mode 100644
index 00000000000..606e45d24cf
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/fuzz_layer.go
@@ -0,0 +1,39 @@
+// 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
+
+import (
+	"encoding/binary"
+
+	"github.com/google/gopacket"
+)
+
+// FuzzLayer is a fuzz target for the layers package of gopacket
+// A fuzz target is a function processing a binary blob (byte slice)
+// The process here is to interpret this data as a packet, and print the layers contents.
+// The decoding options and the starting layer are encoded in the first bytes.
+// The function returns 1 if this is a valid packet (no error layer)
+func FuzzLayer(data []byte) int {
+	if len(data) < 3 {
+		return 0
+	}
+	// use the first two bytes to choose the top level layer
+	startLayer := binary.BigEndian.Uint16(data[:2])
+	var fuzzOpts = gopacket.DecodeOptions{
+		Lazy:                     data[2]&0x1 != 0,
+		NoCopy:                   data[2]&0x2 != 0,
+		SkipDecodeRecovery:       data[2]&0x4 != 0,
+		DecodeStreamsAsDatagrams: data[2]&0x8 != 0,
+	}
+	p := gopacket.NewPacket(data[3:], gopacket.LayerType(startLayer), fuzzOpts)
+	for _, l := range p.Layers() {
+		gopacket.LayerString(l)
+	}
+	if p.ErrorLayer() != nil {
+		return 0
+	}
+	return 1
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/gen_linted.sh b/go-controller/vendor/github.com/google/gopacket/layers/gen_linted.sh
new file mode 100644
index 00000000000..75c701f4d5e
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/gen_linted.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/geneve.go b/go-controller/vendor/github.com/google/gopacket/layers/geneve.go
new file mode 100644
index 00000000000..e9a1428809a
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/geneve.go
@@ -0,0 +1,121 @@
+// Copyright 2016 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
+// Geneve Header:
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |Ver|  Opt Len  |O|C|    Rsvd.  |          Protocol Type        |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |        Virtual Network Identifier (VNI)       |    Reserved   |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                    Variable Length Options                    |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type Geneve struct {
+	BaseLayer
+	Version        uint8        // 2 bits
+	OptionsLength  uint8        // 6 bits
+	OAMPacket      bool         // 1 bits
+	CriticalOption bool         // 1 bits
+	Protocol       EthernetType // 16 bits
+	VNI            uint32       // 24bits
+	Options        []*GeneveOption
+}
+
+// Geneve Tunnel Options
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |          Option Class         |      Type     |R|R|R| Length  |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                      Variable Option Data                     |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type GeneveOption struct {
+	Class  uint16 // 16 bits
+	Type   uint8  // 8 bits
+	Flags  uint8  // 3 bits
+	Length uint8  // 5 bits
+	Data   []byte
+}
+
+// LayerType returns LayerTypeGeneve
+func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
+
+func decodeGeneveOption(data []byte, gn *Geneve, df gopacket.DecodeFeedback) (*GeneveOption, uint8, error) {
+	if len(data) < 3 {
+		df.SetTruncated()
+		return nil, 0, errors.New("geneve option too small")
+	}
+	opt := &GeneveOption{}
+
+	opt.Class = binary.BigEndian.Uint16(data[0:2])
+	opt.Type = data[2]
+	opt.Flags = data[3] >> 4
+	opt.Length = (data[3]&0xf)*4 + 4
+
+	if len(data) < int(opt.Length) {
+		df.SetTruncated()
+		return nil, 0, errors.New("geneve option too small")
+	}
+	opt.Data = make([]byte, opt.Length-4)
+	copy(opt.Data, data[4:opt.Length])
+
+	return opt, opt.Length, nil
+}
+
+func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 7 {
+		df.SetTruncated()
+		return errors.New("geneve packet too short")
+	}
+
+	gn.Version = data[0] >> 7
+	gn.OptionsLength = (data[0] & 0x3f) * 4
+
+	gn.OAMPacket = data[1]&0x80 > 0
+	gn.CriticalOption = data[1]&0x40 > 0
+	gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
+
+	var buf [4]byte
+	copy(buf[1:], data[4:7])
+	gn.VNI = binary.BigEndian.Uint32(buf[:])
+
+	offset, length := uint8(8), int32(gn.OptionsLength)
+	if len(data) < int(length+7) {
+		df.SetTruncated()
+		return errors.New("geneve packet too short")
+	}
+
+	for length > 0 {
+		opt, len, err := decodeGeneveOption(data[offset:], gn, df)
+		if err != nil {
+			return err
+		}
+		gn.Options = append(gn.Options, opt)
+
+		length -= int32(len)
+		offset += len
+	}
+
+	gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}
+
+	return nil
+}
+
+func (gn *Geneve) NextLayerType() gopacket.LayerType {
+	return gn.Protocol.LayerType()
+}
+
+func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
+	gn := &Geneve{}
+	return decodingLayerDecoder(gn, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/gre.go b/go-controller/vendor/github.com/google/gopacket/layers/gre.go
new file mode 100644
index 00000000000..9c5e7d246fa
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/gre.go
@@ -0,0 +1,200 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+
+	"github.com/google/gopacket"
+)
+
+// GRE is a Generic Routing Encapsulation header.
+type GRE struct {
+	BaseLayer
+	ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute, AckPresent bool
+	RecursionControl, Flags, Version                                                       uint8
+	Protocol                                                                               EthernetType
+	Checksum, Offset                                                                       uint16
+	Key, Seq, Ack                                                                          uint32
+	*GRERouting
+}
+
+// GRERouting is GRE routing information, present if the RoutingPresent flag is
+// set.
+type GRERouting struct {
+	AddressFamily        uint16
+	SREOffset, SRELength uint8
+	RoutingInformation   []byte
+	Next                 *GRERouting
+}
+
+// LayerType returns gopacket.LayerTypeGRE.
+func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	g.ChecksumPresent = data[0]&0x80 != 0
+	g.RoutingPresent = data[0]&0x40 != 0
+	g.KeyPresent = data[0]&0x20 != 0
+	g.SeqPresent = data[0]&0x10 != 0
+	g.StrictSourceRoute = data[0]&0x08 != 0
+	g.AckPresent = data[1]&0x80 != 0
+	g.RecursionControl = data[0] & 0x7
+	g.Flags = data[1] >> 3
+	g.Version = data[1] & 0x7
+	g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
+	offset := 4
+	if g.ChecksumPresent || g.RoutingPresent {
+		g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2])
+		g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4])
+		offset += 4
+	}
+	if g.KeyPresent {
+		g.Key = binary.BigEndian.Uint32(data[offset : offset+4])
+		offset += 4
+	}
+	if g.SeqPresent {
+		g.Seq = binary.BigEndian.Uint32(data[offset : offset+4])
+		offset += 4
+	}
+	if g.RoutingPresent {
+		tail := &g.GRERouting
+		for {
+			sre := &GRERouting{
+				AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]),
+				SREOffset:     data[offset+2],
+				SRELength:     data[offset+3],
+			}
+			sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)]
+			offset += 4 + int(sre.SRELength)
+			if sre.AddressFamily == 0 && sre.SRELength == 0 {
+				break
+			}
+			(*tail) = sre
+			tail = &sre.Next
+		}
+	}
+	if g.AckPresent {
+		g.Ack = binary.BigEndian.Uint32(data[offset : offset+4])
+		offset += 4
+	}
+	g.BaseLayer = BaseLayer{data[:offset], data[offset:]}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the SerializationBuffer,
+// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info.
+func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	size := 4
+	if g.ChecksumPresent || g.RoutingPresent {
+		size += 4
+	}
+	if g.KeyPresent {
+		size += 4
+	}
+	if g.SeqPresent {
+		size += 4
+	}
+	if g.RoutingPresent {
+		r := g.GRERouting
+		for r != nil {
+			size += 4 + int(r.SRELength)
+			r = r.Next
+		}
+		size += 4
+	}
+	if g.AckPresent {
+		size += 4
+	}
+	buf, err := b.PrependBytes(size)
+	if err != nil {
+		return err
+	}
+	// Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags.
+	buf[0] = 0
+	buf[1] = 0
+	if g.ChecksumPresent {
+		buf[0] |= 0x80
+	}
+	if g.RoutingPresent {
+		buf[0] |= 0x40
+	}
+	if g.KeyPresent {
+		buf[0] |= 0x20
+	}
+	if g.SeqPresent {
+		buf[0] |= 0x10
+	}
+	if g.StrictSourceRoute {
+		buf[0] |= 0x08
+	}
+	if g.AckPresent {
+		buf[1] |= 0x80
+	}
+	buf[0] |= g.RecursionControl
+	buf[1] |= g.Flags << 3
+	buf[1] |= g.Version
+	binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol))
+	offset := 4
+	if g.ChecksumPresent || g.RoutingPresent {
+		// Don't write the checksum value yet, as we may need to compute it,
+		// which requires the entire header be complete.
+		// Instead we zeroize the memory in case it is dirty.
+		buf[offset] = 0
+		buf[offset+1] = 0
+		binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset)
+		offset += 4
+	}
+	if g.KeyPresent {
+		binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key)
+		offset += 4
+	}
+	if g.SeqPresent {
+		binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq)
+		offset += 4
+	}
+	if g.RoutingPresent {
+		sre := g.GRERouting
+		for sre != nil {
+			binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily)
+			buf[offset+2] = sre.SREOffset
+			buf[offset+3] = sre.SRELength
+			copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation)
+			offset += 4 + int(sre.SRELength)
+			sre = sre.Next
+		}
+		// Terminate routing field with a "NULL" SRE.
+		binary.BigEndian.PutUint32(buf[offset:offset+4], 0)
+	}
+	if g.AckPresent {
+		binary.BigEndian.PutUint32(buf[offset:offset+4], g.Ack)
+		offset += 4
+	}
+	if g.ChecksumPresent {
+		if opts.ComputeChecksums {
+			g.Checksum = tcpipChecksum(b.Bytes(), 0)
+		}
+
+		binary.BigEndian.PutUint16(buf[4:6], g.Checksum)
+	}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (g *GRE) CanDecode() gopacket.LayerClass {
+	return LayerTypeGRE
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (g *GRE) NextLayerType() gopacket.LayerType {
+	return g.Protocol.LayerType()
+}
+
+func decodeGRE(data []byte, p gopacket.PacketBuilder) error {
+	g := &GRE{}
+	return decodingLayerDecoder(g, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/gtp.go b/go-controller/vendor/github.com/google/gopacket/layers/gtp.go
new file mode 100644
index 00000000000..fe3054a6d03
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/gtp.go
@@ -0,0 +1,184 @@
+// 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"
+	"fmt"
+	"github.com/google/gopacket"
+)
+
+const gtpMinimumSizeInBytes int = 8
+
+// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP  without the need to use another version number.
+type GTPExtensionHeader struct {
+	Type    uint8
+	Content []byte
+}
+
+// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
+// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
+type GTPv1U struct {
+	BaseLayer
+	Version             uint8
+	ProtocolType        uint8
+	Reserved            uint8
+	ExtensionHeaderFlag bool
+	SequenceNumberFlag  bool
+	NPDUFlag            bool
+	MessageType         uint8
+	MessageLength       uint16
+	TEID                uint32
+	SequenceNumber      uint16
+	NPDU                uint8
+	GTPExtensionHeaders []GTPExtensionHeader
+}
+
+// LayerType returns LayerTypeGTPV1U
+func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
+
+// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
+func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	hLen := gtpMinimumSizeInBytes
+	dLen := len(data)
+	if dLen < hLen {
+		return fmt.Errorf("GTP packet too small: %d bytes", dLen)
+	}
+	g.Version = (data[0] >> 5) & 0x07
+	g.ProtocolType = (data[0] >> 4) & 0x01
+	g.Reserved = (data[0] >> 3) & 0x01
+	g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
+	g.NPDUFlag = (data[0] & 0x01) == 1
+	g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
+	g.MessageType = data[1]
+	g.MessageLength = binary.BigEndian.Uint16(data[2:4])
+	pLen := 8 + g.MessageLength
+	if uint16(dLen) < pLen {
+		return fmt.Errorf("GTP packet too small: %d bytes", dLen)
+	}
+	//  Field used to multiplex different connections in the same GTP tunnel.
+	g.TEID = binary.BigEndian.Uint32(data[4:8])
+	cIndex := uint16(hLen)
+	if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
+		hLen += 4
+		cIndex += 4
+		if dLen < hLen {
+			return fmt.Errorf("GTP packet too small: %d bytes", dLen)
+		}
+		if g.SequenceNumberFlag {
+			g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
+		}
+		if g.NPDUFlag {
+			g.NPDU = data[10]
+		}
+		if g.ExtensionHeaderFlag {
+			extensionFlag := true
+			for extensionFlag {
+				extensionType := uint8(data[cIndex-1])
+				extensionLength := uint(data[cIndex])
+				if extensionLength == 0 {
+					return fmt.Errorf("GTP packet with invalid extension header")
+				}
+				// extensionLength is in 4-octet units
+				lIndex := cIndex + (uint16(extensionLength) * 4)
+				if uint16(dLen) < lIndex {
+					fmt.Println(dLen, lIndex)
+					return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen)
+				}
+				content := data[cIndex+1 : lIndex-1]
+				eh := GTPExtensionHeader{Type: extensionType, Content: content}
+				g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh)
+				cIndex = lIndex
+				// Check if coming bytes are from an extension header
+				extensionFlag = data[cIndex-1] != 0
+
+			}
+		}
+	}
+	g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]}
+	return nil
+
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (g *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	data, err := b.PrependBytes(gtpMinimumSizeInBytes)
+	if err != nil {
+		return err
+	}
+	data[0] |= (g.Version << 5)
+	data[0] |= (1 << 4)
+	if len(g.GTPExtensionHeaders) > 0 {
+		data[0] |= 0x04
+		g.ExtensionHeaderFlag = true
+	}
+	if g.SequenceNumberFlag {
+		data[0] |= 0x02
+	}
+	if g.NPDUFlag {
+		data[0] |= 0x01
+	}
+	data[1] = g.MessageType
+	binary.BigEndian.PutUint16(data[2:4], g.MessageLength)
+	binary.BigEndian.PutUint32(data[4:8], g.TEID)
+	if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag {
+		data, err := b.AppendBytes(4)
+		if err != nil {
+			return err
+		}
+		binary.BigEndian.PutUint16(data[:2], g.SequenceNumber)
+		data[2] = g.NPDU
+		for _, eh := range g.GTPExtensionHeaders {
+			data[len(data)-1] = eh.Type
+			lContent := len(eh.Content)
+			// extensionLength is in 4-octet units
+			extensionLength := (lContent + 2) / 4
+			// Get two extra byte for the next extension header type and length
+			data, err = b.AppendBytes(lContent + 2)
+			if err != nil {
+				return err
+			}
+			data[0] = byte(extensionLength)
+			copy(data[1:lContent+1], eh.Content)
+		}
+	}
+	return nil
+
+}
+
+// CanDecode returns a set of layers that GTP objects can decode.
+func (g *GTPv1U) CanDecode() gopacket.LayerClass {
+	return LayerTypeGTPv1U
+}
+
+// NextLayerType specifies the next layer that GoPacket should attempt to
+func (g *GTPv1U) NextLayerType() gopacket.LayerType {
+	if len(g.LayerPayload()) == 0 {
+		return gopacket.LayerTypeZero
+	}
+	version := uint8(g.LayerPayload()[0]) >> 4
+	if version == 4 {
+		return LayerTypeIPv4
+	} else if version == 6 {
+		return LayerTypeIPv6
+	} else {
+		return LayerTypePPP
+	}
+}
+
+func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
+	gtp := &GTPv1U{}
+	err := gtp.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(gtp)
+	return p.NextDecoder(gtp.NextLayerType())
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/iana_ports.go b/go-controller/vendor/github.com/google/gopacket/layers/iana_ports.go
new file mode 100644
index 00000000000..ddcf3ecdb7d
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/iana_ports.go
@@ -0,0 +1,11351 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+
+package layers
+
+// Created by gen.go, don't edit manually
+// Generated at 2017-10-23 09:57:28.214859163 -0600 MDT m=+1.011679290
+// Fetched from "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml"
+
+// TCPPortNames contains the port names for all TCP ports.
+var TCPPortNames = tcpPortNames
+
+// UDPPortNames contains the port names for all UDP ports.
+var UDPPortNames = udpPortNames
+
+// SCTPPortNames contains the port names for all SCTP ports.
+var SCTPPortNames = sctpPortNames
+
+var tcpPortNames = map[TCPPort]string{
+	1:     "tcpmux",
+	2:     "compressnet",
+	3:     "compressnet",
+	5:     "rje",
+	7:     "echo",
+	9:     "discard",
+	11:    "systat",
+	13:    "daytime",
+	17:    "qotd",
+	18:    "msp",
+	19:    "chargen",
+	20:    "ftp-data",
+	21:    "ftp",
+	22:    "ssh",
+	23:    "telnet",
+	25:    "smtp",
+	27:    "nsw-fe",
+	29:    "msg-icp",
+	31:    "msg-auth",
+	33:    "dsp",
+	37:    "time",
+	38:    "rap",
+	39:    "rlp",
+	41:    "graphics",
+	42:    "name",
+	43:    "nicname",
+	44:    "mpm-flags",
+	45:    "mpm",
+	46:    "mpm-snd",
+	48:    "auditd",
+	49:    "tacacs",
+	50:    "re-mail-ck",
+	52:    "xns-time",
+	53:    "domain",
+	54:    "xns-ch",
+	55:    "isi-gl",
+	56:    "xns-auth",
+	58:    "xns-mail",
+	62:    "acas",
+	63:    "whoispp",
+	64:    "covia",
+	65:    "tacacs-ds",
+	66:    "sql-net",
+	67:    "bootps",
+	68:    "bootpc",
+	69:    "tftp",
+	70:    "gopher",
+	71:    "netrjs-1",
+	72:    "netrjs-2",
+	73:    "netrjs-3",
+	74:    "netrjs-4",
+	76:    "deos",
+	78:    "vettcp",
+	79:    "finger",
+	80:    "http",
+	82:    "xfer",
+	83:    "mit-ml-dev",
+	84:    "ctf",
+	85:    "mit-ml-dev",
+	86:    "mfcobol",
+	88:    "kerberos",
+	89:    "su-mit-tg",
+	90:    "dnsix",
+	91:    "mit-dov",
+	92:    "npp",
+	93:    "dcp",
+	94:    "objcall",
+	95:    "supdup",
+	96:    "dixie",
+	97:    "swift-rvf",
+	98:    "tacnews",
+	99:    "metagram",
+	101:   "hostname",
+	102:   "iso-tsap",
+	103:   "gppitnp",
+	104:   "acr-nema",
+	105:   "cso",
+	106:   "3com-tsmux",
+	107:   "rtelnet",
+	108:   "snagas",
+	109:   "pop2",
+	110:   "pop3",
+	111:   "sunrpc",
+	112:   "mcidas",
+	113:   "ident",
+	115:   "sftp",
+	116:   "ansanotify",
+	117:   "uucp-path",
+	118:   "sqlserv",
+	119:   "nntp",
+	120:   "cfdptkt",
+	121:   "erpc",
+	122:   "smakynet",
+	123:   "ntp",
+	124:   "ansatrader",
+	125:   "locus-map",
+	126:   "nxedit",
+	127:   "locus-con",
+	128:   "gss-xlicen",
+	129:   "pwdgen",
+	130:   "cisco-fna",
+	131:   "cisco-tna",
+	132:   "cisco-sys",
+	133:   "statsrv",
+	134:   "ingres-net",
+	135:   "epmap",
+	136:   "profile",
+	137:   "netbios-ns",
+	138:   "netbios-dgm",
+	139:   "netbios-ssn",
+	140:   "emfis-data",
+	141:   "emfis-cntl",
+	142:   "bl-idm",
+	143:   "imap",
+	144:   "uma",
+	145:   "uaac",
+	146:   "iso-tp0",
+	147:   "iso-ip",
+	148:   "jargon",
+	149:   "aed-512",
+	150:   "sql-net",
+	151:   "hems",
+	152:   "bftp",
+	153:   "sgmp",
+	154:   "netsc-prod",
+	155:   "netsc-dev",
+	156:   "sqlsrv",
+	157:   "knet-cmp",
+	158:   "pcmail-srv",
+	159:   "nss-routing",
+	160:   "sgmp-traps",
+	161:   "snmp",
+	162:   "snmptrap",
+	163:   "cmip-man",
+	164:   "cmip-agent",
+	165:   "xns-courier",
+	166:   "s-net",
+	167:   "namp",
+	168:   "rsvd",
+	169:   "send",
+	170:   "print-srv",
+	171:   "multiplex",
+	172:   "cl-1",
+	173:   "xyplex-mux",
+	174:   "mailq",
+	175:   "vmnet",
+	176:   "genrad-mux",
+	177:   "xdmcp",
+	178:   "nextstep",
+	179:   "bgp",
+	180:   "ris",
+	181:   "unify",
+	182:   "audit",
+	183:   "ocbinder",
+	184:   "ocserver",
+	185:   "remote-kis",
+	186:   "kis",
+	187:   "aci",
+	188:   "mumps",
+	189:   "qft",
+	190:   "gacp",
+	191:   "prospero",
+	192:   "osu-nms",
+	193:   "srmp",
+	194:   "irc",
+	195:   "dn6-nlm-aud",
+	196:   "dn6-smm-red",
+	197:   "dls",
+	198:   "dls-mon",
+	199:   "smux",
+	200:   "src",
+	201:   "at-rtmp",
+	202:   "at-nbp",
+	203:   "at-3",
+	204:   "at-echo",
+	205:   "at-5",
+	206:   "at-zis",
+	207:   "at-7",
+	208:   "at-8",
+	209:   "qmtp",
+	210:   "z39-50",
+	211:   "914c-g",
+	212:   "anet",
+	213:   "ipx",
+	214:   "vmpwscs",
+	215:   "softpc",
+	216:   "CAIlic",
+	217:   "dbase",
+	218:   "mpp",
+	219:   "uarps",
+	220:   "imap3",
+	221:   "fln-spx",
+	222:   "rsh-spx",
+	223:   "cdc",
+	224:   "masqdialer",
+	242:   "direct",
+	243:   "sur-meas",
+	244:   "inbusiness",
+	245:   "link",
+	246:   "dsp3270",
+	247:   "subntbcst-tftp",
+	248:   "bhfhs",
+	256:   "rap",
+	257:   "set",
+	259:   "esro-gen",
+	260:   "openport",
+	261:   "nsiiops",
+	262:   "arcisdms",
+	263:   "hdap",
+	264:   "bgmp",
+	265:   "x-bone-ctl",
+	266:   "sst",
+	267:   "td-service",
+	268:   "td-replica",
+	269:   "manet",
+	271:   "pt-tls",
+	280:   "http-mgmt",
+	281:   "personal-link",
+	282:   "cableport-ax",
+	283:   "rescap",
+	284:   "corerjd",
+	286:   "fxp",
+	287:   "k-block",
+	308:   "novastorbakcup",
+	309:   "entrusttime",
+	310:   "bhmds",
+	311:   "asip-webadmin",
+	312:   "vslmp",
+	313:   "magenta-logic",
+	314:   "opalis-robot",
+	315:   "dpsi",
+	316:   "decauth",
+	317:   "zannet",
+	318:   "pkix-timestamp",
+	319:   "ptp-event",
+	320:   "ptp-general",
+	321:   "pip",
+	322:   "rtsps",
+	323:   "rpki-rtr",
+	324:   "rpki-rtr-tls",
+	333:   "texar",
+	344:   "pdap",
+	345:   "pawserv",
+	346:   "zserv",
+	347:   "fatserv",
+	348:   "csi-sgwp",
+	349:   "mftp",
+	350:   "matip-type-a",
+	351:   "matip-type-b",
+	352:   "dtag-ste-sb",
+	353:   "ndsauth",
+	354:   "bh611",
+	355:   "datex-asn",
+	356:   "cloanto-net-1",
+	357:   "bhevent",
+	358:   "shrinkwrap",
+	359:   "nsrmp",
+	360:   "scoi2odialog",
+	361:   "semantix",
+	362:   "srssend",
+	363:   "rsvp-tunnel",
+	364:   "aurora-cmgr",
+	365:   "dtk",
+	366:   "odmr",
+	367:   "mortgageware",
+	368:   "qbikgdp",
+	369:   "rpc2portmap",
+	370:   "codaauth2",
+	371:   "clearcase",
+	372:   "ulistproc",
+	373:   "legent-1",
+	374:   "legent-2",
+	375:   "hassle",
+	376:   "nip",
+	377:   "tnETOS",
+	378:   "dsETOS",
+	379:   "is99c",
+	380:   "is99s",
+	381:   "hp-collector",
+	382:   "hp-managed-node",
+	383:   "hp-alarm-mgr",
+	384:   "arns",
+	385:   "ibm-app",
+	386:   "asa",
+	387:   "aurp",
+	388:   "unidata-ldm",
+	389:   "ldap",
+	390:   "uis",
+	391:   "synotics-relay",
+	392:   "synotics-broker",
+	393:   "meta5",
+	394:   "embl-ndt",
+	395:   "netcp",
+	396:   "netware-ip",
+	397:   "mptn",
+	398:   "kryptolan",
+	399:   "iso-tsap-c2",
+	400:   "osb-sd",
+	401:   "ups",
+	402:   "genie",
+	403:   "decap",
+	404:   "nced",
+	405:   "ncld",
+	406:   "imsp",
+	407:   "timbuktu",
+	408:   "prm-sm",
+	409:   "prm-nm",
+	410:   "decladebug",
+	411:   "rmt",
+	412:   "synoptics-trap",
+	413:   "smsp",
+	414:   "infoseek",
+	415:   "bnet",
+	416:   "silverplatter",
+	417:   "onmux",
+	418:   "hyper-g",
+	419:   "ariel1",
+	420:   "smpte",
+	421:   "ariel2",
+	422:   "ariel3",
+	423:   "opc-job-start",
+	424:   "opc-job-track",
+	425:   "icad-el",
+	426:   "smartsdp",
+	427:   "svrloc",
+	428:   "ocs-cmu",
+	429:   "ocs-amu",
+	430:   "utmpsd",
+	431:   "utmpcd",
+	432:   "iasd",
+	433:   "nnsp",
+	434:   "mobileip-agent",
+	435:   "mobilip-mn",
+	436:   "dna-cml",
+	437:   "comscm",
+	438:   "dsfgw",
+	439:   "dasp",
+	440:   "sgcp",
+	441:   "decvms-sysmgt",
+	442:   "cvc-hostd",
+	443:   "https",
+	444:   "snpp",
+	445:   "microsoft-ds",
+	446:   "ddm-rdb",
+	447:   "ddm-dfm",
+	448:   "ddm-ssl",
+	449:   "as-servermap",
+	450:   "tserver",
+	451:   "sfs-smp-net",
+	452:   "sfs-config",
+	453:   "creativeserver",
+	454:   "contentserver",
+	455:   "creativepartnr",
+	456:   "macon-tcp",
+	457:   "scohelp",
+	458:   "appleqtc",
+	459:   "ampr-rcmd",
+	460:   "skronk",
+	461:   "datasurfsrv",
+	462:   "datasurfsrvsec",
+	463:   "alpes",
+	464:   "kpasswd",
+	465:   "urd",
+	466:   "digital-vrc",
+	467:   "mylex-mapd",
+	468:   "photuris",
+	469:   "rcp",
+	470:   "scx-proxy",
+	471:   "mondex",
+	472:   "ljk-login",
+	473:   "hybrid-pop",
+	474:   "tn-tl-w1",
+	475:   "tcpnethaspsrv",
+	476:   "tn-tl-fd1",
+	477:   "ss7ns",
+	478:   "spsc",
+	479:   "iafserver",
+	480:   "iafdbase",
+	481:   "ph",
+	482:   "bgs-nsi",
+	483:   "ulpnet",
+	484:   "integra-sme",
+	485:   "powerburst",
+	486:   "avian",
+	487:   "saft",
+	488:   "gss-http",
+	489:   "nest-protocol",
+	490:   "micom-pfs",
+	491:   "go-login",
+	492:   "ticf-1",
+	493:   "ticf-2",
+	494:   "pov-ray",
+	495:   "intecourier",
+	496:   "pim-rp-disc",
+	497:   "retrospect",
+	498:   "siam",
+	499:   "iso-ill",
+	500:   "isakmp",
+	501:   "stmf",
+	502:   "mbap",
+	503:   "intrinsa",
+	504:   "citadel",
+	505:   "mailbox-lm",
+	506:   "ohimsrv",
+	507:   "crs",
+	508:   "xvttp",
+	509:   "snare",
+	510:   "fcp",
+	511:   "passgo",
+	512:   "exec",
+	513:   "login",
+	514:   "shell",
+	515:   "printer",
+	516:   "videotex",
+	517:   "talk",
+	518:   "ntalk",
+	519:   "utime",
+	520:   "efs",
+	521:   "ripng",
+	522:   "ulp",
+	523:   "ibm-db2",
+	524:   "ncp",
+	525:   "timed",
+	526:   "tempo",
+	527:   "stx",
+	528:   "custix",
+	529:   "irc-serv",
+	530:   "courier",
+	531:   "conference",
+	532:   "netnews",
+	533:   "netwall",
+	534:   "windream",
+	535:   "iiop",
+	536:   "opalis-rdv",
+	537:   "nmsp",
+	538:   "gdomap",
+	539:   "apertus-ldp",
+	540:   "uucp",
+	541:   "uucp-rlogin",
+	542:   "commerce",
+	543:   "klogin",
+	544:   "kshell",
+	545:   "appleqtcsrvr",
+	546:   "dhcpv6-client",
+	547:   "dhcpv6-server",
+	548:   "afpovertcp",
+	549:   "idfp",
+	550:   "new-rwho",
+	551:   "cybercash",
+	552:   "devshr-nts",
+	553:   "pirp",
+	554:   "rtsp",
+	555:   "dsf",
+	556:   "remotefs",
+	557:   "openvms-sysipc",
+	558:   "sdnskmp",
+	559:   "teedtap",
+	560:   "rmonitor",
+	561:   "monitor",
+	562:   "chshell",
+	563:   "nntps",
+	564:   "9pfs",
+	565:   "whoami",
+	566:   "streettalk",
+	567:   "banyan-rpc",
+	568:   "ms-shuttle",
+	569:   "ms-rome",
+	570:   "meter",
+	571:   "meter",
+	572:   "sonar",
+	573:   "banyan-vip",
+	574:   "ftp-agent",
+	575:   "vemmi",
+	576:   "ipcd",
+	577:   "vnas",
+	578:   "ipdd",
+	579:   "decbsrv",
+	580:   "sntp-heartbeat",
+	581:   "bdp",
+	582:   "scc-security",
+	583:   "philips-vc",
+	584:   "keyserver",
+	586:   "password-chg",
+	587:   "submission",
+	588:   "cal",
+	589:   "eyelink",
+	590:   "tns-cml",
+	591:   "http-alt",
+	592:   "eudora-set",
+	593:   "http-rpc-epmap",
+	594:   "tpip",
+	595:   "cab-protocol",
+	596:   "smsd",
+	597:   "ptcnameservice",
+	598:   "sco-websrvrmg3",
+	599:   "acp",
+	600:   "ipcserver",
+	601:   "syslog-conn",
+	602:   "xmlrpc-beep",
+	603:   "idxp",
+	604:   "tunnel",
+	605:   "soap-beep",
+	606:   "urm",
+	607:   "nqs",
+	608:   "sift-uft",
+	609:   "npmp-trap",
+	610:   "npmp-local",
+	611:   "npmp-gui",
+	612:   "hmmp-ind",
+	613:   "hmmp-op",
+	614:   "sshell",
+	615:   "sco-inetmgr",
+	616:   "sco-sysmgr",
+	617:   "sco-dtmgr",
+	618:   "dei-icda",
+	619:   "compaq-evm",
+	620:   "sco-websrvrmgr",
+	621:   "escp-ip",
+	622:   "collaborator",
+	623:   "oob-ws-http",
+	624:   "cryptoadmin",
+	625:   "dec-dlm",
+	626:   "asia",
+	627:   "passgo-tivoli",
+	628:   "qmqp",
+	629:   "3com-amp3",
+	630:   "rda",
+	631:   "ipp",
+	632:   "bmpp",
+	633:   "servstat",
+	634:   "ginad",
+	635:   "rlzdbase",
+	636:   "ldaps",
+	637:   "lanserver",
+	638:   "mcns-sec",
+	639:   "msdp",
+	640:   "entrust-sps",
+	641:   "repcmd",
+	642:   "esro-emsdp",
+	643:   "sanity",
+	644:   "dwr",
+	645:   "pssc",
+	646:   "ldp",
+	647:   "dhcp-failover",
+	648:   "rrp",
+	649:   "cadview-3d",
+	650:   "obex",
+	651:   "ieee-mms",
+	652:   "hello-port",
+	653:   "repscmd",
+	654:   "aodv",
+	655:   "tinc",
+	656:   "spmp",
+	657:   "rmc",
+	658:   "tenfold",
+	660:   "mac-srvr-admin",
+	661:   "hap",
+	662:   "pftp",
+	663:   "purenoise",
+	664:   "oob-ws-https",
+	665:   "sun-dr",
+	666:   "mdqs",
+	667:   "disclose",
+	668:   "mecomm",
+	669:   "meregister",
+	670:   "vacdsm-sws",
+	671:   "vacdsm-app",
+	672:   "vpps-qua",
+	673:   "cimplex",
+	674:   "acap",
+	675:   "dctp",
+	676:   "vpps-via",
+	677:   "vpp",
+	678:   "ggf-ncp",
+	679:   "mrm",
+	680:   "entrust-aaas",
+	681:   "entrust-aams",
+	682:   "xfr",
+	683:   "corba-iiop",
+	684:   "corba-iiop-ssl",
+	685:   "mdc-portmapper",
+	686:   "hcp-wismar",
+	687:   "asipregistry",
+	688:   "realm-rusd",
+	689:   "nmap",
+	690:   "vatp",
+	691:   "msexch-routing",
+	692:   "hyperwave-isp",
+	693:   "connendp",
+	694:   "ha-cluster",
+	695:   "ieee-mms-ssl",
+	696:   "rushd",
+	697:   "uuidgen",
+	698:   "olsr",
+	699:   "accessnetwork",
+	700:   "epp",
+	701:   "lmp",
+	702:   "iris-beep",
+	704:   "elcsd",
+	705:   "agentx",
+	706:   "silc",
+	707:   "borland-dsj",
+	709:   "entrust-kmsh",
+	710:   "entrust-ash",
+	711:   "cisco-tdp",
+	712:   "tbrpf",
+	713:   "iris-xpc",
+	714:   "iris-xpcs",
+	715:   "iris-lwz",
+	729:   "netviewdm1",
+	730:   "netviewdm2",
+	731:   "netviewdm3",
+	741:   "netgw",
+	742:   "netrcs",
+	744:   "flexlm",
+	747:   "fujitsu-dev",
+	748:   "ris-cm",
+	749:   "kerberos-adm",
+	750:   "rfile",
+	751:   "pump",
+	752:   "qrh",
+	753:   "rrh",
+	754:   "tell",
+	758:   "nlogin",
+	759:   "con",
+	760:   "ns",
+	761:   "rxe",
+	762:   "quotad",
+	763:   "cycleserv",
+	764:   "omserv",
+	765:   "webster",
+	767:   "phonebook",
+	769:   "vid",
+	770:   "cadlock",
+	771:   "rtip",
+	772:   "cycleserv2",
+	773:   "submit",
+	774:   "rpasswd",
+	775:   "entomb",
+	776:   "wpages",
+	777:   "multiling-http",
+	780:   "wpgs",
+	800:   "mdbs-daemon",
+	801:   "device",
+	802:   "mbap-s",
+	810:   "fcp-udp",
+	828:   "itm-mcell-s",
+	829:   "pkix-3-ca-ra",
+	830:   "netconf-ssh",
+	831:   "netconf-beep",
+	832:   "netconfsoaphttp",
+	833:   "netconfsoapbeep",
+	847:   "dhcp-failover2",
+	848:   "gdoi",
+	853:   "domain-s",
+	854:   "dlep",
+	860:   "iscsi",
+	861:   "owamp-control",
+	862:   "twamp-control",
+	873:   "rsync",
+	886:   "iclcnet-locate",
+	887:   "iclcnet-svinfo",
+	888:   "accessbuilder",
+	900:   "omginitialrefs",
+	901:   "smpnameres",
+	902:   "ideafarm-door",
+	903:   "ideafarm-panic",
+	910:   "kink",
+	911:   "xact-backup",
+	912:   "apex-mesh",
+	913:   "apex-edge",
+	953:   "rndc",
+	989:   "ftps-data",
+	990:   "ftps",
+	991:   "nas",
+	992:   "telnets",
+	993:   "imaps",
+	995:   "pop3s",
+	996:   "vsinet",
+	997:   "maitrd",
+	998:   "busboy",
+	999:   "garcon",
+	1000:  "cadlock2",
+	1001:  "webpush",
+	1010:  "surf",
+	1021:  "exp1",
+	1022:  "exp2",
+	1025:  "blackjack",
+	1026:  "cap",
+	1029:  "solid-mux",
+	1033:  "netinfo-local",
+	1034:  "activesync",
+	1035:  "mxxrlogin",
+	1036:  "nsstp",
+	1037:  "ams",
+	1038:  "mtqp",
+	1039:  "sbl",
+	1040:  "netarx",
+	1041:  "danf-ak2",
+	1042:  "afrog",
+	1043:  "boinc-client",
+	1044:  "dcutility",
+	1045:  "fpitp",
+	1046:  "wfremotertm",
+	1047:  "neod1",
+	1048:  "neod2",
+	1049:  "td-postman",
+	1050:  "cma",
+	1051:  "optima-vnet",
+	1052:  "ddt",
+	1053:  "remote-as",
+	1054:  "brvread",
+	1055:  "ansyslmd",
+	1056:  "vfo",
+	1057:  "startron",
+	1058:  "nim",
+	1059:  "nimreg",
+	1060:  "polestar",
+	1061:  "kiosk",
+	1062:  "veracity",
+	1063:  "kyoceranetdev",
+	1064:  "jstel",
+	1065:  "syscomlan",
+	1066:  "fpo-fns",
+	1067:  "instl-boots",
+	1068:  "instl-bootc",
+	1069:  "cognex-insight",
+	1070:  "gmrupdateserv",
+	1071:  "bsquare-voip",
+	1072:  "cardax",
+	1073:  "bridgecontrol",
+	1074:  "warmspotMgmt",
+	1075:  "rdrmshc",
+	1076:  "dab-sti-c",
+	1077:  "imgames",
+	1078:  "avocent-proxy",
+	1079:  "asprovatalk",
+	1080:  "socks",
+	1081:  "pvuniwien",
+	1082:  "amt-esd-prot",
+	1083:  "ansoft-lm-1",
+	1084:  "ansoft-lm-2",
+	1085:  "webobjects",
+	1086:  "cplscrambler-lg",
+	1087:  "cplscrambler-in",
+	1088:  "cplscrambler-al",
+	1089:  "ff-annunc",
+	1090:  "ff-fms",
+	1091:  "ff-sm",
+	1092:  "obrpd",
+	1093:  "proofd",
+	1094:  "rootd",
+	1095:  "nicelink",
+	1096:  "cnrprotocol",
+	1097:  "sunclustermgr",
+	1098:  "rmiactivation",
+	1099:  "rmiregistry",
+	1100:  "mctp",
+	1101:  "pt2-discover",
+	1102:  "adobeserver-1",
+	1103:  "adobeserver-2",
+	1104:  "xrl",
+	1105:  "ftranhc",
+	1106:  "isoipsigport-1",
+	1107:  "isoipsigport-2",
+	1108:  "ratio-adp",
+	1110:  "webadmstart",
+	1111:  "lmsocialserver",
+	1112:  "icp",
+	1113:  "ltp-deepspace",
+	1114:  "mini-sql",
+	1115:  "ardus-trns",
+	1116:  "ardus-cntl",
+	1117:  "ardus-mtrns",
+	1118:  "sacred",
+	1119:  "bnetgame",
+	1120:  "bnetfile",
+	1121:  "rmpp",
+	1122:  "availant-mgr",
+	1123:  "murray",
+	1124:  "hpvmmcontrol",
+	1125:  "hpvmmagent",
+	1126:  "hpvmmdata",
+	1127:  "kwdb-commn",
+	1128:  "saphostctrl",
+	1129:  "saphostctrls",
+	1130:  "casp",
+	1131:  "caspssl",
+	1132:  "kvm-via-ip",
+	1133:  "dfn",
+	1134:  "aplx",
+	1135:  "omnivision",
+	1136:  "hhb-gateway",
+	1137:  "trim",
+	1138:  "encrypted-admin",
+	1139:  "evm",
+	1140:  "autonoc",
+	1141:  "mxomss",
+	1142:  "edtools",
+	1143:  "imyx",
+	1144:  "fuscript",
+	1145:  "x9-icue",
+	1146:  "audit-transfer",
+	1147:  "capioverlan",
+	1148:  "elfiq-repl",
+	1149:  "bvtsonar",
+	1150:  "blaze",
+	1151:  "unizensus",
+	1152:  "winpoplanmess",
+	1153:  "c1222-acse",
+	1154:  "resacommunity",
+	1155:  "nfa",
+	1156:  "iascontrol-oms",
+	1157:  "iascontrol",
+	1158:  "dbcontrol-oms",
+	1159:  "oracle-oms",
+	1160:  "olsv",
+	1161:  "health-polling",
+	1162:  "health-trap",
+	1163:  "sddp",
+	1164:  "qsm-proxy",
+	1165:  "qsm-gui",
+	1166:  "qsm-remote",
+	1167:  "cisco-ipsla",
+	1168:  "vchat",
+	1169:  "tripwire",
+	1170:  "atc-lm",
+	1171:  "atc-appserver",
+	1172:  "dnap",
+	1173:  "d-cinema-rrp",
+	1174:  "fnet-remote-ui",
+	1175:  "dossier",
+	1176:  "indigo-server",
+	1177:  "dkmessenger",
+	1178:  "sgi-storman",
+	1179:  "b2n",
+	1180:  "mc-client",
+	1181:  "3comnetman",
+	1182:  "accelenet",
+	1183:  "llsurfup-http",
+	1184:  "llsurfup-https",
+	1185:  "catchpole",
+	1186:  "mysql-cluster",
+	1187:  "alias",
+	1188:  "hp-webadmin",
+	1189:  "unet",
+	1190:  "commlinx-avl",
+	1191:  "gpfs",
+	1192:  "caids-sensor",
+	1193:  "fiveacross",
+	1194:  "openvpn",
+	1195:  "rsf-1",
+	1196:  "netmagic",
+	1197:  "carrius-rshell",
+	1198:  "cajo-discovery",
+	1199:  "dmidi",
+	1200:  "scol",
+	1201:  "nucleus-sand",
+	1202:  "caiccipc",
+	1203:  "ssslic-mgr",
+	1204:  "ssslog-mgr",
+	1205:  "accord-mgc",
+	1206:  "anthony-data",
+	1207:  "metasage",
+	1208:  "seagull-ais",
+	1209:  "ipcd3",
+	1210:  "eoss",
+	1211:  "groove-dpp",
+	1212:  "lupa",
+	1213:  "mpc-lifenet",
+	1214:  "kazaa",
+	1215:  "scanstat-1",
+	1216:  "etebac5",
+	1217:  "hpss-ndapi",
+	1218:  "aeroflight-ads",
+	1219:  "aeroflight-ret",
+	1220:  "qt-serveradmin",
+	1221:  "sweetware-apps",
+	1222:  "nerv",
+	1223:  "tgp",
+	1224:  "vpnz",
+	1225:  "slinkysearch",
+	1226:  "stgxfws",
+	1227:  "dns2go",
+	1228:  "florence",
+	1229:  "zented",
+	1230:  "periscope",
+	1231:  "menandmice-lpm",
+	1232:  "first-defense",
+	1233:  "univ-appserver",
+	1234:  "search-agent",
+	1235:  "mosaicsyssvc1",
+	1236:  "bvcontrol",
+	1237:  "tsdos390",
+	1238:  "hacl-qs",
+	1239:  "nmsd",
+	1240:  "instantia",
+	1241:  "nessus",
+	1242:  "nmasoverip",
+	1243:  "serialgateway",
+	1244:  "isbconference1",
+	1245:  "isbconference2",
+	1246:  "payrouter",
+	1247:  "visionpyramid",
+	1248:  "hermes",
+	1249:  "mesavistaco",
+	1250:  "swldy-sias",
+	1251:  "servergraph",
+	1252:  "bspne-pcc",
+	1253:  "q55-pcc",
+	1254:  "de-noc",
+	1255:  "de-cache-query",
+	1256:  "de-server",
+	1257:  "shockwave2",
+	1258:  "opennl",
+	1259:  "opennl-voice",
+	1260:  "ibm-ssd",
+	1261:  "mpshrsv",
+	1262:  "qnts-orb",
+	1263:  "dka",
+	1264:  "prat",
+	1265:  "dssiapi",
+	1266:  "dellpwrappks",
+	1267:  "epc",
+	1268:  "propel-msgsys",
+	1269:  "watilapp",
+	1270:  "opsmgr",
+	1271:  "excw",
+	1272:  "cspmlockmgr",
+	1273:  "emc-gateway",
+	1274:  "t1distproc",
+	1275:  "ivcollector",
+	1277:  "miva-mqs",
+	1278:  "dellwebadmin-1",
+	1279:  "dellwebadmin-2",
+	1280:  "pictrography",
+	1281:  "healthd",
+	1282:  "emperion",
+	1283:  "productinfo",
+	1284:  "iee-qfx",
+	1285:  "neoiface",
+	1286:  "netuitive",
+	1287:  "routematch",
+	1288:  "navbuddy",
+	1289:  "jwalkserver",
+	1290:  "winjaserver",
+	1291:  "seagulllms",
+	1292:  "dsdn",
+	1293:  "pkt-krb-ipsec",
+	1294:  "cmmdriver",
+	1295:  "ehtp",
+	1296:  "dproxy",
+	1297:  "sdproxy",
+	1298:  "lpcp",
+	1299:  "hp-sci",
+	1300:  "h323hostcallsc",
+	1301:  "ci3-software-1",
+	1302:  "ci3-software-2",
+	1303:  "sftsrv",
+	1304:  "boomerang",
+	1305:  "pe-mike",
+	1306:  "re-conn-proto",
+	1307:  "pacmand",
+	1308:  "odsi",
+	1309:  "jtag-server",
+	1310:  "husky",
+	1311:  "rxmon",
+	1312:  "sti-envision",
+	1313:  "bmc-patroldb",
+	1314:  "pdps",
+	1315:  "els",
+	1316:  "exbit-escp",
+	1317:  "vrts-ipcserver",
+	1318:  "krb5gatekeeper",
+	1319:  "amx-icsp",
+	1320:  "amx-axbnet",
+	1321:  "pip",
+	1322:  "novation",
+	1323:  "brcd",
+	1324:  "delta-mcp",
+	1325:  "dx-instrument",
+	1326:  "wimsic",
+	1327:  "ultrex",
+	1328:  "ewall",
+	1329:  "netdb-export",
+	1330:  "streetperfect",
+	1331:  "intersan",
+	1332:  "pcia-rxp-b",
+	1333:  "passwrd-policy",
+	1334:  "writesrv",
+	1335:  "digital-notary",
+	1336:  "ischat",
+	1337:  "menandmice-dns",
+	1338:  "wmc-log-svc",
+	1339:  "kjtsiteserver",
+	1340:  "naap",
+	1341:  "qubes",
+	1342:  "esbroker",
+	1343:  "re101",
+	1344:  "icap",
+	1345:  "vpjp",
+	1346:  "alta-ana-lm",
+	1347:  "bbn-mmc",
+	1348:  "bbn-mmx",
+	1349:  "sbook",
+	1350:  "editbench",
+	1351:  "equationbuilder",
+	1352:  "lotusnote",
+	1353:  "relief",
+	1354:  "XSIP-network",
+	1355:  "intuitive-edge",
+	1356:  "cuillamartin",
+	1357:  "pegboard",
+	1358:  "connlcli",
+	1359:  "ftsrv",
+	1360:  "mimer",
+	1361:  "linx",
+	1362:  "timeflies",
+	1363:  "ndm-requester",
+	1364:  "ndm-server",
+	1365:  "adapt-sna",
+	1366:  "netware-csp",
+	1367:  "dcs",
+	1368:  "screencast",
+	1369:  "gv-us",
+	1370:  "us-gv",
+	1371:  "fc-cli",
+	1372:  "fc-ser",
+	1373:  "chromagrafx",
+	1374:  "molly",
+	1375:  "bytex",
+	1376:  "ibm-pps",
+	1377:  "cichlid",
+	1378:  "elan",
+	1379:  "dbreporter",
+	1380:  "telesis-licman",
+	1381:  "apple-licman",
+	1382:  "udt-os",
+	1383:  "gwha",
+	1384:  "os-licman",
+	1385:  "atex-elmd",
+	1386:  "checksum",
+	1387:  "cadsi-lm",
+	1388:  "objective-dbc",
+	1389:  "iclpv-dm",
+	1390:  "iclpv-sc",
+	1391:  "iclpv-sas",
+	1392:  "iclpv-pm",
+	1393:  "iclpv-nls",
+	1394:  "iclpv-nlc",
+	1395:  "iclpv-wsm",
+	1396:  "dvl-activemail",
+	1397:  "audio-activmail",
+	1398:  "video-activmail",
+	1399:  "cadkey-licman",
+	1400:  "cadkey-tablet",
+	1401:  "goldleaf-licman",
+	1402:  "prm-sm-np",
+	1403:  "prm-nm-np",
+	1404:  "igi-lm",
+	1405:  "ibm-res",
+	1406:  "netlabs-lm",
+	1407:  "tibet-server",
+	1408:  "sophia-lm",
+	1409:  "here-lm",
+	1410:  "hiq",
+	1411:  "af",
+	1412:  "innosys",
+	1413:  "innosys-acl",
+	1414:  "ibm-mqseries",
+	1415:  "dbstar",
+	1416:  "novell-lu6-2",
+	1417:  "timbuktu-srv1",
+	1418:  "timbuktu-srv2",
+	1419:  "timbuktu-srv3",
+	1420:  "timbuktu-srv4",
+	1421:  "gandalf-lm",
+	1422:  "autodesk-lm",
+	1423:  "essbase",
+	1424:  "hybrid",
+	1425:  "zion-lm",
+	1426:  "sais",
+	1427:  "mloadd",
+	1428:  "informatik-lm",
+	1429:  "nms",
+	1430:  "tpdu",
+	1431:  "rgtp",
+	1432:  "blueberry-lm",
+	1433:  "ms-sql-s",
+	1434:  "ms-sql-m",
+	1435:  "ibm-cics",
+	1436:  "saism",
+	1437:  "tabula",
+	1438:  "eicon-server",
+	1439:  "eicon-x25",
+	1440:  "eicon-slp",
+	1441:  "cadis-1",
+	1442:  "cadis-2",
+	1443:  "ies-lm",
+	1444:  "marcam-lm",
+	1445:  "proxima-lm",
+	1446:  "ora-lm",
+	1447:  "apri-lm",
+	1448:  "oc-lm",
+	1449:  "peport",
+	1450:  "dwf",
+	1451:  "infoman",
+	1452:  "gtegsc-lm",
+	1453:  "genie-lm",
+	1454:  "interhdl-elmd",
+	1455:  "esl-lm",
+	1456:  "dca",
+	1457:  "valisys-lm",
+	1458:  "nrcabq-lm",
+	1459:  "proshare1",
+	1460:  "proshare2",
+	1461:  "ibm-wrless-lan",
+	1462:  "world-lm",
+	1463:  "nucleus",
+	1464:  "msl-lmd",
+	1465:  "pipes",
+	1466:  "oceansoft-lm",
+	1467:  "csdmbase",
+	1468:  "csdm",
+	1469:  "aal-lm",
+	1470:  "uaiact",
+	1471:  "csdmbase",
+	1472:  "csdm",
+	1473:  "openmath",
+	1474:  "telefinder",
+	1475:  "taligent-lm",
+	1476:  "clvm-cfg",
+	1477:  "ms-sna-server",
+	1478:  "ms-sna-base",
+	1479:  "dberegister",
+	1480:  "pacerforum",
+	1481:  "airs",
+	1482:  "miteksys-lm",
+	1483:  "afs",
+	1484:  "confluent",
+	1485:  "lansource",
+	1486:  "nms-topo-serv",
+	1487:  "localinfosrvr",
+	1488:  "docstor",
+	1489:  "dmdocbroker",
+	1490:  "insitu-conf",
+	1492:  "stone-design-1",
+	1493:  "netmap-lm",
+	1494:  "ica",
+	1495:  "cvc",
+	1496:  "liberty-lm",
+	1497:  "rfx-lm",
+	1498:  "sybase-sqlany",
+	1499:  "fhc",
+	1500:  "vlsi-lm",
+	1501:  "saiscm",
+	1502:  "shivadiscovery",
+	1503:  "imtc-mcs",
+	1504:  "evb-elm",
+	1505:  "funkproxy",
+	1506:  "utcd",
+	1507:  "symplex",
+	1508:  "diagmond",
+	1509:  "robcad-lm",
+	1510:  "mvx-lm",
+	1511:  "3l-l1",
+	1512:  "wins",
+	1513:  "fujitsu-dtc",
+	1514:  "fujitsu-dtcns",
+	1515:  "ifor-protocol",
+	1516:  "vpad",
+	1517:  "vpac",
+	1518:  "vpvd",
+	1519:  "vpvc",
+	1520:  "atm-zip-office",
+	1521:  "ncube-lm",
+	1522:  "ricardo-lm",
+	1523:  "cichild-lm",
+	1524:  "ingreslock",
+	1525:  "orasrv",
+	1526:  "pdap-np",
+	1527:  "tlisrv",
+	1529:  "coauthor",
+	1530:  "rap-service",
+	1531:  "rap-listen",
+	1532:  "miroconnect",
+	1533:  "virtual-places",
+	1534:  "micromuse-lm",
+	1535:  "ampr-info",
+	1536:  "ampr-inter",
+	1537:  "sdsc-lm",
+	1538:  "3ds-lm",
+	1539:  "intellistor-lm",
+	1540:  "rds",
+	1541:  "rds2",
+	1542:  "gridgen-elmd",
+	1543:  "simba-cs",
+	1544:  "aspeclmd",
+	1545:  "vistium-share",
+	1546:  "abbaccuray",
+	1547:  "laplink",
+	1548:  "axon-lm",
+	1549:  "shivahose",
+	1550:  "3m-image-lm",
+	1551:  "hecmtl-db",
+	1552:  "pciarray",
+	1553:  "sna-cs",
+	1554:  "caci-lm",
+	1555:  "livelan",
+	1556:  "veritas-pbx",
+	1557:  "arbortext-lm",
+	1558:  "xingmpeg",
+	1559:  "web2host",
+	1560:  "asci-val",
+	1561:  "facilityview",
+	1562:  "pconnectmgr",
+	1563:  "cadabra-lm",
+	1564:  "pay-per-view",
+	1565:  "winddlb",
+	1566:  "corelvideo",
+	1567:  "jlicelmd",
+	1568:  "tsspmap",
+	1569:  "ets",
+	1570:  "orbixd",
+	1571:  "rdb-dbs-disp",
+	1572:  "chip-lm",
+	1573:  "itscomm-ns",
+	1574:  "mvel-lm",
+	1575:  "oraclenames",
+	1576:  "moldflow-lm",
+	1577:  "hypercube-lm",
+	1578:  "jacobus-lm",
+	1579:  "ioc-sea-lm",
+	1580:  "tn-tl-r1",
+	1581:  "mil-2045-47001",
+	1582:  "msims",
+	1583:  "simbaexpress",
+	1584:  "tn-tl-fd2",
+	1585:  "intv",
+	1586:  "ibm-abtact",
+	1587:  "pra-elmd",
+	1588:  "triquest-lm",
+	1589:  "vqp",
+	1590:  "gemini-lm",
+	1591:  "ncpm-pm",
+	1592:  "commonspace",
+	1593:  "mainsoft-lm",
+	1594:  "sixtrak",
+	1595:  "radio",
+	1596:  "radio-sm",
+	1597:  "orbplus-iiop",
+	1598:  "picknfs",
+	1599:  "simbaservices",
+	1600:  "issd",
+	1601:  "aas",
+	1602:  "inspect",
+	1603:  "picodbc",
+	1604:  "icabrowser",
+	1605:  "slp",
+	1606:  "slm-api",
+	1607:  "stt",
+	1608:  "smart-lm",
+	1609:  "isysg-lm",
+	1610:  "taurus-wh",
+	1611:  "ill",
+	1612:  "netbill-trans",
+	1613:  "netbill-keyrep",
+	1614:  "netbill-cred",
+	1615:  "netbill-auth",
+	1616:  "netbill-prod",
+	1617:  "nimrod-agent",
+	1618:  "skytelnet",
+	1619:  "xs-openstorage",
+	1620:  "faxportwinport",
+	1621:  "softdataphone",
+	1622:  "ontime",
+	1623:  "jaleosnd",
+	1624:  "udp-sr-port",
+	1625:  "svs-omagent",
+	1626:  "shockwave",
+	1627:  "t128-gateway",
+	1628:  "lontalk-norm",
+	1629:  "lontalk-urgnt",
+	1630:  "oraclenet8cman",
+	1631:  "visitview",
+	1632:  "pammratc",
+	1633:  "pammrpc",
+	1634:  "loaprobe",
+	1635:  "edb-server1",
+	1636:  "isdc",
+	1637:  "islc",
+	1638:  "ismc",
+	1639:  "cert-initiator",
+	1640:  "cert-responder",
+	1641:  "invision",
+	1642:  "isis-am",
+	1643:  "isis-ambc",
+	1644:  "saiseh",
+	1645:  "sightline",
+	1646:  "sa-msg-port",
+	1647:  "rsap",
+	1648:  "concurrent-lm",
+	1649:  "kermit",
+	1650:  "nkd",
+	1651:  "shiva-confsrvr",
+	1652:  "xnmp",
+	1653:  "alphatech-lm",
+	1654:  "stargatealerts",
+	1655:  "dec-mbadmin",
+	1656:  "dec-mbadmin-h",
+	1657:  "fujitsu-mmpdc",
+	1658:  "sixnetudr",
+	1659:  "sg-lm",
+	1660:  "skip-mc-gikreq",
+	1661:  "netview-aix-1",
+	1662:  "netview-aix-2",
+	1663:  "netview-aix-3",
+	1664:  "netview-aix-4",
+	1665:  "netview-aix-5",
+	1666:  "netview-aix-6",
+	1667:  "netview-aix-7",
+	1668:  "netview-aix-8",
+	1669:  "netview-aix-9",
+	1670:  "netview-aix-10",
+	1671:  "netview-aix-11",
+	1672:  "netview-aix-12",
+	1673:  "proshare-mc-1",
+	1674:  "proshare-mc-2",
+	1675:  "pdp",
+	1676:  "netcomm1",
+	1677:  "groupwise",
+	1678:  "prolink",
+	1679:  "darcorp-lm",
+	1680:  "microcom-sbp",
+	1681:  "sd-elmd",
+	1682:  "lanyon-lantern",
+	1683:  "ncpm-hip",
+	1684:  "snaresecure",
+	1685:  "n2nremote",
+	1686:  "cvmon",
+	1687:  "nsjtp-ctrl",
+	1688:  "nsjtp-data",
+	1689:  "firefox",
+	1690:  "ng-umds",
+	1691:  "empire-empuma",
+	1692:  "sstsys-lm",
+	1693:  "rrirtr",
+	1694:  "rrimwm",
+	1695:  "rrilwm",
+	1696:  "rrifmm",
+	1697:  "rrisat",
+	1698:  "rsvp-encap-1",
+	1699:  "rsvp-encap-2",
+	1700:  "mps-raft",
+	1701:  "l2f",
+	1702:  "deskshare",
+	1703:  "hb-engine",
+	1704:  "bcs-broker",
+	1705:  "slingshot",
+	1706:  "jetform",
+	1707:  "vdmplay",
+	1708:  "gat-lmd",
+	1709:  "centra",
+	1710:  "impera",
+	1711:  "pptconference",
+	1712:  "registrar",
+	1713:  "conferencetalk",
+	1714:  "sesi-lm",
+	1715:  "houdini-lm",
+	1716:  "xmsg",
+	1717:  "fj-hdnet",
+	1718:  "h323gatedisc",
+	1719:  "h323gatestat",
+	1720:  "h323hostcall",
+	1721:  "caicci",
+	1722:  "hks-lm",
+	1723:  "pptp",
+	1724:  "csbphonemaster",
+	1725:  "iden-ralp",
+	1726:  "iberiagames",
+	1727:  "winddx",
+	1728:  "telindus",
+	1729:  "citynl",
+	1730:  "roketz",
+	1731:  "msiccp",
+	1732:  "proxim",
+	1733:  "siipat",
+	1734:  "cambertx-lm",
+	1735:  "privatechat",
+	1736:  "street-stream",
+	1737:  "ultimad",
+	1738:  "gamegen1",
+	1739:  "webaccess",
+	1740:  "encore",
+	1741:  "cisco-net-mgmt",
+	1742:  "3Com-nsd",
+	1743:  "cinegrfx-lm",
+	1744:  "ncpm-ft",
+	1745:  "remote-winsock",
+	1746:  "ftrapid-1",
+	1747:  "ftrapid-2",
+	1748:  "oracle-em1",
+	1749:  "aspen-services",
+	1750:  "sslp",
+	1751:  "swiftnet",
+	1752:  "lofr-lm",
+	1753:  "predatar-comms",
+	1754:  "oracle-em2",
+	1755:  "ms-streaming",
+	1756:  "capfast-lmd",
+	1757:  "cnhrp",
+	1758:  "tftp-mcast",
+	1759:  "spss-lm",
+	1760:  "www-ldap-gw",
+	1761:  "cft-0",
+	1762:  "cft-1",
+	1763:  "cft-2",
+	1764:  "cft-3",
+	1765:  "cft-4",
+	1766:  "cft-5",
+	1767:  "cft-6",
+	1768:  "cft-7",
+	1769:  "bmc-net-adm",
+	1770:  "bmc-net-svc",
+	1771:  "vaultbase",
+	1772:  "essweb-gw",
+	1773:  "kmscontrol",
+	1774:  "global-dtserv",
+	1775:  "vdab",
+	1776:  "femis",
+	1777:  "powerguardian",
+	1778:  "prodigy-intrnet",
+	1779:  "pharmasoft",
+	1780:  "dpkeyserv",
+	1781:  "answersoft-lm",
+	1782:  "hp-hcip",
+	1784:  "finle-lm",
+	1785:  "windlm",
+	1786:  "funk-logger",
+	1787:  "funk-license",
+	1788:  "psmond",
+	1789:  "hello",
+	1790:  "nmsp",
+	1791:  "ea1",
+	1792:  "ibm-dt-2",
+	1793:  "rsc-robot",
+	1794:  "cera-bcm",
+	1795:  "dpi-proxy",
+	1796:  "vocaltec-admin",
+	1797:  "uma",
+	1798:  "etp",
+	1799:  "netrisk",
+	1800:  "ansys-lm",
+	1801:  "msmq",
+	1802:  "concomp1",
+	1803:  "hp-hcip-gwy",
+	1804:  "enl",
+	1805:  "enl-name",
+	1806:  "musiconline",
+	1807:  "fhsp",
+	1808:  "oracle-vp2",
+	1809:  "oracle-vp1",
+	1810:  "jerand-lm",
+	1811:  "scientia-sdb",
+	1812:  "radius",
+	1813:  "radius-acct",
+	1814:  "tdp-suite",
+	1815:  "mmpft",
+	1816:  "harp",
+	1817:  "rkb-oscs",
+	1818:  "etftp",
+	1819:  "plato-lm",
+	1820:  "mcagent",
+	1821:  "donnyworld",
+	1822:  "es-elmd",
+	1823:  "unisys-lm",
+	1824:  "metrics-pas",
+	1825:  "direcpc-video",
+	1826:  "ardt",
+	1827:  "asi",
+	1828:  "itm-mcell-u",
+	1829:  "optika-emedia",
+	1830:  "net8-cman",
+	1831:  "myrtle",
+	1832:  "tht-treasure",
+	1833:  "udpradio",
+	1834:  "ardusuni",
+	1835:  "ardusmul",
+	1836:  "ste-smsc",
+	1837:  "csoft1",
+	1838:  "talnet",
+	1839:  "netopia-vo1",
+	1840:  "netopia-vo2",
+	1841:  "netopia-vo3",
+	1842:  "netopia-vo4",
+	1843:  "netopia-vo5",
+	1844:  "direcpc-dll",
+	1845:  "altalink",
+	1846:  "tunstall-pnc",
+	1847:  "slp-notify",
+	1848:  "fjdocdist",
+	1849:  "alpha-sms",
+	1850:  "gsi",
+	1851:  "ctcd",
+	1852:  "virtual-time",
+	1853:  "vids-avtp",
+	1854:  "buddy-draw",
+	1855:  "fiorano-rtrsvc",
+	1856:  "fiorano-msgsvc",
+	1857:  "datacaptor",
+	1858:  "privateark",
+	1859:  "gammafetchsvr",
+	1860:  "sunscalar-svc",
+	1861:  "lecroy-vicp",
+	1862:  "mysql-cm-agent",
+	1863:  "msnp",
+	1864:  "paradym-31port",
+	1865:  "entp",
+	1866:  "swrmi",
+	1867:  "udrive",
+	1868:  "viziblebrowser",
+	1869:  "transact",
+	1870:  "sunscalar-dns",
+	1871:  "canocentral0",
+	1872:  "canocentral1",
+	1873:  "fjmpjps",
+	1874:  "fjswapsnp",
+	1875:  "westell-stats",
+	1876:  "ewcappsrv",
+	1877:  "hp-webqosdb",
+	1878:  "drmsmc",
+	1879:  "nettgain-nms",
+	1880:  "vsat-control",
+	1881:  "ibm-mqseries2",
+	1882:  "ecsqdmn",
+	1883:  "mqtt",
+	1884:  "idmaps",
+	1885:  "vrtstrapserver",
+	1886:  "leoip",
+	1887:  "filex-lport",
+	1888:  "ncconfig",
+	1889:  "unify-adapter",
+	1890:  "wilkenlistener",
+	1891:  "childkey-notif",
+	1892:  "childkey-ctrl",
+	1893:  "elad",
+	1894:  "o2server-port",
+	1896:  "b-novative-ls",
+	1897:  "metaagent",
+	1898:  "cymtec-port",
+	1899:  "mc2studios",
+	1900:  "ssdp",
+	1901:  "fjicl-tep-a",
+	1902:  "fjicl-tep-b",
+	1903:  "linkname",
+	1904:  "fjicl-tep-c",
+	1905:  "sugp",
+	1906:  "tpmd",
+	1907:  "intrastar",
+	1908:  "dawn",
+	1909:  "global-wlink",
+	1910:  "ultrabac",
+	1911:  "mtp",
+	1912:  "rhp-iibp",
+	1913:  "armadp",
+	1914:  "elm-momentum",
+	1915:  "facelink",
+	1916:  "persona",
+	1917:  "noagent",
+	1918:  "can-nds",
+	1919:  "can-dch",
+	1920:  "can-ferret",
+	1921:  "noadmin",
+	1922:  "tapestry",
+	1923:  "spice",
+	1924:  "xiip",
+	1925:  "discovery-port",
+	1926:  "egs",
+	1927:  "videte-cipc",
+	1928:  "emsd-port",
+	1929:  "bandwiz-system",
+	1930:  "driveappserver",
+	1931:  "amdsched",
+	1932:  "ctt-broker",
+	1933:  "xmapi",
+	1934:  "xaapi",
+	1935:  "macromedia-fcs",
+	1936:  "jetcmeserver",
+	1937:  "jwserver",
+	1938:  "jwclient",
+	1939:  "jvserver",
+	1940:  "jvclient",
+	1941:  "dic-aida",
+	1942:  "res",
+	1943:  "beeyond-media",
+	1944:  "close-combat",
+	1945:  "dialogic-elmd",
+	1946:  "tekpls",
+	1947:  "sentinelsrm",
+	1948:  "eye2eye",
+	1949:  "ismaeasdaqlive",
+	1950:  "ismaeasdaqtest",
+	1951:  "bcs-lmserver",
+	1952:  "mpnjsc",
+	1953:  "rapidbase",
+	1954:  "abr-api",
+	1955:  "abr-secure",
+	1956:  "vrtl-vmf-ds",
+	1957:  "unix-status",
+	1958:  "dxadmind",
+	1959:  "simp-all",
+	1960:  "nasmanager",
+	1961:  "bts-appserver",
+	1962:  "biap-mp",
+	1963:  "webmachine",
+	1964:  "solid-e-engine",
+	1965:  "tivoli-npm",
+	1966:  "slush",
+	1967:  "sns-quote",
+	1968:  "lipsinc",
+	1969:  "lipsinc1",
+	1970:  "netop-rc",
+	1971:  "netop-school",
+	1972:  "intersys-cache",
+	1973:  "dlsrap",
+	1974:  "drp",
+	1975:  "tcoflashagent",
+	1976:  "tcoregagent",
+	1977:  "tcoaddressbook",
+	1978:  "unisql",
+	1979:  "unisql-java",
+	1980:  "pearldoc-xact",
+	1981:  "p2pq",
+	1982:  "estamp",
+	1983:  "lhtp",
+	1984:  "bb",
+	1985:  "hsrp",
+	1986:  "licensedaemon",
+	1987:  "tr-rsrb-p1",
+	1988:  "tr-rsrb-p2",
+	1989:  "tr-rsrb-p3",
+	1990:  "stun-p1",
+	1991:  "stun-p2",
+	1992:  "stun-p3",
+	1993:  "snmp-tcp-port",
+	1994:  "stun-port",
+	1995:  "perf-port",
+	1996:  "tr-rsrb-port",
+	1997:  "gdp-port",
+	1998:  "x25-svc-port",
+	1999:  "tcp-id-port",
+	2000:  "cisco-sccp",
+	2001:  "dc",
+	2002:  "globe",
+	2003:  "brutus",
+	2004:  "mailbox",
+	2005:  "berknet",
+	2006:  "invokator",
+	2007:  "dectalk",
+	2008:  "conf",
+	2009:  "news",
+	2010:  "search",
+	2011:  "raid-cc",
+	2012:  "ttyinfo",
+	2013:  "raid-am",
+	2014:  "troff",
+	2015:  "cypress",
+	2016:  "bootserver",
+	2017:  "cypress-stat",
+	2018:  "terminaldb",
+	2019:  "whosockami",
+	2020:  "xinupageserver",
+	2021:  "servexec",
+	2022:  "down",
+	2023:  "xinuexpansion3",
+	2024:  "xinuexpansion4",
+	2025:  "ellpack",
+	2026:  "scrabble",
+	2027:  "shadowserver",
+	2028:  "submitserver",
+	2029:  "hsrpv6",
+	2030:  "device2",
+	2031:  "mobrien-chat",
+	2032:  "blackboard",
+	2033:  "glogger",
+	2034:  "scoremgr",
+	2035:  "imsldoc",
+	2036:  "e-dpnet",
+	2037:  "applus",
+	2038:  "objectmanager",
+	2039:  "prizma",
+	2040:  "lam",
+	2041:  "interbase",
+	2042:  "isis",
+	2043:  "isis-bcast",
+	2044:  "rimsl",
+	2045:  "cdfunc",
+	2046:  "sdfunc",
+	2047:  "dls",
+	2048:  "dls-monitor",
+	2049:  "shilp",
+	2050:  "av-emb-config",
+	2051:  "epnsdp",
+	2052:  "clearvisn",
+	2053:  "lot105-ds-upd",
+	2054:  "weblogin",
+	2055:  "iop",
+	2056:  "omnisky",
+	2057:  "rich-cp",
+	2058:  "newwavesearch",
+	2059:  "bmc-messaging",
+	2060:  "teleniumdaemon",
+	2061:  "netmount",
+	2062:  "icg-swp",
+	2063:  "icg-bridge",
+	2064:  "icg-iprelay",
+	2065:  "dlsrpn",
+	2066:  "aura",
+	2067:  "dlswpn",
+	2068:  "avauthsrvprtcl",
+	2069:  "event-port",
+	2070:  "ah-esp-encap",
+	2071:  "acp-port",
+	2072:  "msync",
+	2073:  "gxs-data-port",
+	2074:  "vrtl-vmf-sa",
+	2075:  "newlixengine",
+	2076:  "newlixconfig",
+	2077:  "tsrmagt",
+	2078:  "tpcsrvr",
+	2079:  "idware-router",
+	2080:  "autodesk-nlm",
+	2081:  "kme-trap-port",
+	2082:  "infowave",
+	2083:  "radsec",
+	2084:  "sunclustergeo",
+	2085:  "ada-cip",
+	2086:  "gnunet",
+	2087:  "eli",
+	2088:  "ip-blf",
+	2089:  "sep",
+	2090:  "lrp",
+	2091:  "prp",
+	2092:  "descent3",
+	2093:  "nbx-cc",
+	2094:  "nbx-au",
+	2095:  "nbx-ser",
+	2096:  "nbx-dir",
+	2097:  "jetformpreview",
+	2098:  "dialog-port",
+	2099:  "h2250-annex-g",
+	2100:  "amiganetfs",
+	2101:  "rtcm-sc104",
+	2102:  "zephyr-srv",
+	2103:  "zephyr-clt",
+	2104:  "zephyr-hm",
+	2105:  "minipay",
+	2106:  "mzap",
+	2107:  "bintec-admin",
+	2108:  "comcam",
+	2109:  "ergolight",
+	2110:  "umsp",
+	2111:  "dsatp",
+	2112:  "idonix-metanet",
+	2113:  "hsl-storm",
+	2114:  "newheights",
+	2115:  "kdm",
+	2116:  "ccowcmr",
+	2117:  "mentaclient",
+	2118:  "mentaserver",
+	2119:  "gsigatekeeper",
+	2120:  "qencp",
+	2121:  "scientia-ssdb",
+	2122:  "caupc-remote",
+	2123:  "gtp-control",
+	2124:  "elatelink",
+	2125:  "lockstep",
+	2126:  "pktcable-cops",
+	2127:  "index-pc-wb",
+	2128:  "net-steward",
+	2129:  "cs-live",
+	2130:  "xds",
+	2131:  "avantageb2b",
+	2132:  "solera-epmap",
+	2133:  "zymed-zpp",
+	2134:  "avenue",
+	2135:  "gris",
+	2136:  "appworxsrv",
+	2137:  "connect",
+	2138:  "unbind-cluster",
+	2139:  "ias-auth",
+	2140:  "ias-reg",
+	2141:  "ias-admind",
+	2142:  "tdmoip",
+	2143:  "lv-jc",
+	2144:  "lv-ffx",
+	2145:  "lv-pici",
+	2146:  "lv-not",
+	2147:  "lv-auth",
+	2148:  "veritas-ucl",
+	2149:  "acptsys",
+	2150:  "dynamic3d",
+	2151:  "docent",
+	2152:  "gtp-user",
+	2153:  "ctlptc",
+	2154:  "stdptc",
+	2155:  "brdptc",
+	2156:  "trp",
+	2157:  "xnds",
+	2158:  "touchnetplus",
+	2159:  "gdbremote",
+	2160:  "apc-2160",
+	2161:  "apc-2161",
+	2162:  "navisphere",
+	2163:  "navisphere-sec",
+	2164:  "ddns-v3",
+	2165:  "x-bone-api",
+	2166:  "iwserver",
+	2167:  "raw-serial",
+	2168:  "easy-soft-mux",
+	2169:  "brain",
+	2170:  "eyetv",
+	2171:  "msfw-storage",
+	2172:  "msfw-s-storage",
+	2173:  "msfw-replica",
+	2174:  "msfw-array",
+	2175:  "airsync",
+	2176:  "rapi",
+	2177:  "qwave",
+	2178:  "bitspeer",
+	2179:  "vmrdp",
+	2180:  "mc-gt-srv",
+	2181:  "eforward",
+	2182:  "cgn-stat",
+	2183:  "cgn-config",
+	2184:  "nvd",
+	2185:  "onbase-dds",
+	2186:  "gtaua",
+	2187:  "ssmc",
+	2188:  "radware-rpm",
+	2189:  "radware-rpm-s",
+	2190:  "tivoconnect",
+	2191:  "tvbus",
+	2192:  "asdis",
+	2193:  "drwcs",
+	2197:  "mnp-exchange",
+	2198:  "onehome-remote",
+	2199:  "onehome-help",
+	2200:  "ici",
+	2201:  "ats",
+	2202:  "imtc-map",
+	2203:  "b2-runtime",
+	2204:  "b2-license",
+	2205:  "jps",
+	2206:  "hpocbus",
+	2207:  "hpssd",
+	2208:  "hpiod",
+	2209:  "rimf-ps",
+	2210:  "noaaport",
+	2211:  "emwin",
+	2212:  "leecoposserver",
+	2213:  "kali",
+	2214:  "rpi",
+	2215:  "ipcore",
+	2216:  "vtu-comms",
+	2217:  "gotodevice",
+	2218:  "bounzza",
+	2219:  "netiq-ncap",
+	2220:  "netiq",
+	2221:  "ethernet-ip-s",
+	2222:  "EtherNet-IP-1",
+	2223:  "rockwell-csp2",
+	2224:  "efi-mg",
+	2225:  "rcip-itu",
+	2226:  "di-drm",
+	2227:  "di-msg",
+	2228:  "ehome-ms",
+	2229:  "datalens",
+	2230:  "queueadm",
+	2231:  "wimaxasncp",
+	2232:  "ivs-video",
+	2233:  "infocrypt",
+	2234:  "directplay",
+	2235:  "sercomm-wlink",
+	2236:  "nani",
+	2237:  "optech-port1-lm",
+	2238:  "aviva-sna",
+	2239:  "imagequery",
+	2240:  "recipe",
+	2241:  "ivsd",
+	2242:  "foliocorp",
+	2243:  "magicom",
+	2244:  "nmsserver",
+	2245:  "hao",
+	2246:  "pc-mta-addrmap",
+	2247:  "antidotemgrsvr",
+	2248:  "ums",
+	2249:  "rfmp",
+	2250:  "remote-collab",
+	2251:  "dif-port",
+	2252:  "njenet-ssl",
+	2253:  "dtv-chan-req",
+	2254:  "seispoc",
+	2255:  "vrtp",
+	2256:  "pcc-mfp",
+	2257:  "simple-tx-rx",
+	2258:  "rcts",
+	2260:  "apc-2260",
+	2261:  "comotionmaster",
+	2262:  "comotionback",
+	2263:  "ecwcfg",
+	2264:  "apx500api-1",
+	2265:  "apx500api-2",
+	2266:  "mfserver",
+	2267:  "ontobroker",
+	2268:  "amt",
+	2269:  "mikey",
+	2270:  "starschool",
+	2271:  "mmcals",
+	2272:  "mmcal",
+	2273:  "mysql-im",
+	2274:  "pcttunnell",
+	2275:  "ibridge-data",
+	2276:  "ibridge-mgmt",
+	2277:  "bluectrlproxy",
+	2278:  "s3db",
+	2279:  "xmquery",
+	2280:  "lnvpoller",
+	2281:  "lnvconsole",
+	2282:  "lnvalarm",
+	2283:  "lnvstatus",
+	2284:  "lnvmaps",
+	2285:  "lnvmailmon",
+	2286:  "nas-metering",
+	2287:  "dna",
+	2288:  "netml",
+	2289:  "dict-lookup",
+	2290:  "sonus-logging",
+	2291:  "eapsp",
+	2292:  "mib-streaming",
+	2293:  "npdbgmngr",
+	2294:  "konshus-lm",
+	2295:  "advant-lm",
+	2296:  "theta-lm",
+	2297:  "d2k-datamover1",
+	2298:  "d2k-datamover2",
+	2299:  "pc-telecommute",
+	2300:  "cvmmon",
+	2301:  "cpq-wbem",
+	2302:  "binderysupport",
+	2303:  "proxy-gateway",
+	2304:  "attachmate-uts",
+	2305:  "mt-scaleserver",
+	2306:  "tappi-boxnet",
+	2307:  "pehelp",
+	2308:  "sdhelp",
+	2309:  "sdserver",
+	2310:  "sdclient",
+	2311:  "messageservice",
+	2312:  "wanscaler",
+	2313:  "iapp",
+	2314:  "cr-websystems",
+	2315:  "precise-sft",
+	2316:  "sent-lm",
+	2317:  "attachmate-g32",
+	2318:  "cadencecontrol",
+	2319:  "infolibria",
+	2320:  "siebel-ns",
+	2321:  "rdlap",
+	2322:  "ofsd",
+	2323:  "3d-nfsd",
+	2324:  "cosmocall",
+	2325:  "ansysli",
+	2326:  "idcp",
+	2327:  "xingcsm",
+	2328:  "netrix-sftm",
+	2329:  "nvd",
+	2330:  "tscchat",
+	2331:  "agentview",
+	2332:  "rcc-host",
+	2333:  "snapp",
+	2334:  "ace-client",
+	2335:  "ace-proxy",
+	2336:  "appleugcontrol",
+	2337:  "ideesrv",
+	2338:  "norton-lambert",
+	2339:  "3com-webview",
+	2340:  "wrs-registry",
+	2341:  "xiostatus",
+	2342:  "manage-exec",
+	2343:  "nati-logos",
+	2344:  "fcmsys",
+	2345:  "dbm",
+	2346:  "redstorm-join",
+	2347:  "redstorm-find",
+	2348:  "redstorm-info",
+	2349:  "redstorm-diag",
+	2350:  "psbserver",
+	2351:  "psrserver",
+	2352:  "pslserver",
+	2353:  "pspserver",
+	2354:  "psprserver",
+	2355:  "psdbserver",
+	2356:  "gxtelmd",
+	2357:  "unihub-server",
+	2358:  "futrix",
+	2359:  "flukeserver",
+	2360:  "nexstorindltd",
+	2361:  "tl1",
+	2362:  "digiman",
+	2363:  "mediacntrlnfsd",
+	2364:  "oi-2000",
+	2365:  "dbref",
+	2366:  "qip-login",
+	2367:  "service-ctrl",
+	2368:  "opentable",
+	2370:  "l3-hbmon",
+	2371:  "hp-rda",
+	2372:  "lanmessenger",
+	2373:  "remographlm",
+	2374:  "hydra",
+	2375:  "docker",
+	2376:  "docker-s",
+	2377:  "swarm",
+	2379:  "etcd-client",
+	2380:  "etcd-server",
+	2381:  "compaq-https",
+	2382:  "ms-olap3",
+	2383:  "ms-olap4",
+	2384:  "sd-request",
+	2385:  "sd-data",
+	2386:  "virtualtape",
+	2387:  "vsamredirector",
+	2388:  "mynahautostart",
+	2389:  "ovsessionmgr",
+	2390:  "rsmtp",
+	2391:  "3com-net-mgmt",
+	2392:  "tacticalauth",
+	2393:  "ms-olap1",
+	2394:  "ms-olap2",
+	2395:  "lan900-remote",
+	2396:  "wusage",
+	2397:  "ncl",
+	2398:  "orbiter",
+	2399:  "fmpro-fdal",
+	2400:  "opequus-server",
+	2401:  "cvspserver",
+	2402:  "taskmaster2000",
+	2403:  "taskmaster2000",
+	2404:  "iec-104",
+	2405:  "trc-netpoll",
+	2406:  "jediserver",
+	2407:  "orion",
+	2408:  "railgun-webaccl",
+	2409:  "sns-protocol",
+	2410:  "vrts-registry",
+	2411:  "netwave-ap-mgmt",
+	2412:  "cdn",
+	2413:  "orion-rmi-reg",
+	2414:  "beeyond",
+	2415:  "codima-rtp",
+	2416:  "rmtserver",
+	2417:  "composit-server",
+	2418:  "cas",
+	2419:  "attachmate-s2s",
+	2420:  "dslremote-mgmt",
+	2421:  "g-talk",
+	2422:  "crmsbits",
+	2423:  "rnrp",
+	2424:  "kofax-svr",
+	2425:  "fjitsuappmgr",
+	2426:  "vcmp",
+	2427:  "mgcp-gateway",
+	2428:  "ott",
+	2429:  "ft-role",
+	2430:  "venus",
+	2431:  "venus-se",
+	2432:  "codasrv",
+	2433:  "codasrv-se",
+	2434:  "pxc-epmap",
+	2435:  "optilogic",
+	2436:  "topx",
+	2437:  "unicontrol",
+	2438:  "msp",
+	2439:  "sybasedbsynch",
+	2440:  "spearway",
+	2441:  "pvsw-inet",
+	2442:  "netangel",
+	2443:  "powerclientcsf",
+	2444:  "btpp2sectrans",
+	2445:  "dtn1",
+	2446:  "bues-service",
+	2447:  "ovwdb",
+	2448:  "hpppssvr",
+	2449:  "ratl",
+	2450:  "netadmin",
+	2451:  "netchat",
+	2452:  "snifferclient",
+	2453:  "madge-ltd",
+	2454:  "indx-dds",
+	2455:  "wago-io-system",
+	2456:  "altav-remmgt",
+	2457:  "rapido-ip",
+	2458:  "griffin",
+	2459:  "community",
+	2460:  "ms-theater",
+	2461:  "qadmifoper",
+	2462:  "qadmifevent",
+	2463:  "lsi-raid-mgmt",
+	2464:  "direcpc-si",
+	2465:  "lbm",
+	2466:  "lbf",
+	2467:  "high-criteria",
+	2468:  "qip-msgd",
+	2469:  "mti-tcs-comm",
+	2470:  "taskman-port",
+	2471:  "seaodbc",
+	2472:  "c3",
+	2473:  "aker-cdp",
+	2474:  "vitalanalysis",
+	2475:  "ace-server",
+	2476:  "ace-svr-prop",
+	2477:  "ssm-cvs",
+	2478:  "ssm-cssps",
+	2479:  "ssm-els",
+	2480:  "powerexchange",
+	2481:  "giop",
+	2482:  "giop-ssl",
+	2483:  "ttc",
+	2484:  "ttc-ssl",
+	2485:  "netobjects1",
+	2486:  "netobjects2",
+	2487:  "pns",
+	2488:  "moy-corp",
+	2489:  "tsilb",
+	2490:  "qip-qdhcp",
+	2491:  "conclave-cpp",
+	2492:  "groove",
+	2493:  "talarian-mqs",
+	2494:  "bmc-ar",
+	2495:  "fast-rem-serv",
+	2496:  "dirgis",
+	2497:  "quaddb",
+	2498:  "odn-castraq",
+	2499:  "unicontrol",
+	2500:  "rtsserv",
+	2501:  "rtsclient",
+	2502:  "kentrox-prot",
+	2503:  "nms-dpnss",
+	2504:  "wlbs",
+	2505:  "ppcontrol",
+	2506:  "jbroker",
+	2507:  "spock",
+	2508:  "jdatastore",
+	2509:  "fjmpss",
+	2510:  "fjappmgrbulk",
+	2511:  "metastorm",
+	2512:  "citrixima",
+	2513:  "citrixadmin",
+	2514:  "facsys-ntp",
+	2515:  "facsys-router",
+	2516:  "maincontrol",
+	2517:  "call-sig-trans",
+	2518:  "willy",
+	2519:  "globmsgsvc",
+	2520:  "pvsw",
+	2521:  "adaptecmgr",
+	2522:  "windb",
+	2523:  "qke-llc-v3",
+	2524:  "optiwave-lm",
+	2525:  "ms-v-worlds",
+	2526:  "ema-sent-lm",
+	2527:  "iqserver",
+	2528:  "ncr-ccl",
+	2529:  "utsftp",
+	2530:  "vrcommerce",
+	2531:  "ito-e-gui",
+	2532:  "ovtopmd",
+	2533:  "snifferserver",
+	2534:  "combox-web-acc",
+	2535:  "madcap",
+	2536:  "btpp2audctr1",
+	2537:  "upgrade",
+	2538:  "vnwk-prapi",
+	2539:  "vsiadmin",
+	2540:  "lonworks",
+	2541:  "lonworks2",
+	2542:  "udrawgraph",
+	2543:  "reftek",
+	2544:  "novell-zen",
+	2545:  "sis-emt",
+	2546:  "vytalvaultbrtp",
+	2547:  "vytalvaultvsmp",
+	2548:  "vytalvaultpipe",
+	2549:  "ipass",
+	2550:  "ads",
+	2551:  "isg-uda-server",
+	2552:  "call-logging",
+	2553:  "efidiningport",
+	2554:  "vcnet-link-v10",
+	2555:  "compaq-wcp",
+	2556:  "nicetec-nmsvc",
+	2557:  "nicetec-mgmt",
+	2558:  "pclemultimedia",
+	2559:  "lstp",
+	2560:  "labrat",
+	2561:  "mosaixcc",
+	2562:  "delibo",
+	2563:  "cti-redwood",
+	2564:  "hp-3000-telnet",
+	2565:  "coord-svr",
+	2566:  "pcs-pcw",
+	2567:  "clp",
+	2568:  "spamtrap",
+	2569:  "sonuscallsig",
+	2570:  "hs-port",
+	2571:  "cecsvc",
+	2572:  "ibp",
+	2573:  "trustestablish",
+	2574:  "blockade-bpsp",
+	2575:  "hl7",
+	2576:  "tclprodebugger",
+	2577:  "scipticslsrvr",
+	2578:  "rvs-isdn-dcp",
+	2579:  "mpfoncl",
+	2580:  "tributary",
+	2581:  "argis-te",
+	2582:  "argis-ds",
+	2583:  "mon",
+	2584:  "cyaserv",
+	2585:  "netx-server",
+	2586:  "netx-agent",
+	2587:  "masc",
+	2588:  "privilege",
+	2589:  "quartus-tcl",
+	2590:  "idotdist",
+	2591:  "maytagshuffle",
+	2592:  "netrek",
+	2593:  "mns-mail",
+	2594:  "dts",
+	2595:  "worldfusion1",
+	2596:  "worldfusion2",
+	2597:  "homesteadglory",
+	2598:  "citriximaclient",
+	2599:  "snapd",
+	2600:  "hpstgmgr",
+	2601:  "discp-client",
+	2602:  "discp-server",
+	2603:  "servicemeter",
+	2604:  "nsc-ccs",
+	2605:  "nsc-posa",
+	2606:  "netmon",
+	2607:  "connection",
+	2608:  "wag-service",
+	2609:  "system-monitor",
+	2610:  "versa-tek",
+	2611:  "lionhead",
+	2612:  "qpasa-agent",
+	2613:  "smntubootstrap",
+	2614:  "neveroffline",
+	2615:  "firepower",
+	2616:  "appswitch-emp",
+	2617:  "cmadmin",
+	2618:  "priority-e-com",
+	2619:  "bruce",
+	2620:  "lpsrecommender",
+	2621:  "miles-apart",
+	2622:  "metricadbc",
+	2623:  "lmdp",
+	2624:  "aria",
+	2625:  "blwnkl-port",
+	2626:  "gbjd816",
+	2627:  "moshebeeri",
+	2628:  "dict",
+	2629:  "sitaraserver",
+	2630:  "sitaramgmt",
+	2631:  "sitaradir",
+	2632:  "irdg-post",
+	2633:  "interintelli",
+	2634:  "pk-electronics",
+	2635:  "backburner",
+	2636:  "solve",
+	2637:  "imdocsvc",
+	2638:  "sybaseanywhere",
+	2639:  "aminet",
+	2640:  "ami-control",
+	2641:  "hdl-srv",
+	2642:  "tragic",
+	2643:  "gte-samp",
+	2644:  "travsoft-ipx-t",
+	2645:  "novell-ipx-cmd",
+	2646:  "and-lm",
+	2647:  "syncserver",
+	2648:  "upsnotifyprot",
+	2649:  "vpsipport",
+	2650:  "eristwoguns",
+	2651:  "ebinsite",
+	2652:  "interpathpanel",
+	2653:  "sonus",
+	2654:  "corel-vncadmin",
+	2655:  "unglue",
+	2656:  "kana",
+	2657:  "sns-dispatcher",
+	2658:  "sns-admin",
+	2659:  "sns-query",
+	2660:  "gcmonitor",
+	2661:  "olhost",
+	2662:  "bintec-capi",
+	2663:  "bintec-tapi",
+	2664:  "patrol-mq-gm",
+	2665:  "patrol-mq-nm",
+	2666:  "extensis",
+	2667:  "alarm-clock-s",
+	2668:  "alarm-clock-c",
+	2669:  "toad",
+	2670:  "tve-announce",
+	2671:  "newlixreg",
+	2672:  "nhserver",
+	2673:  "firstcall42",
+	2674:  "ewnn",
+	2675:  "ttc-etap",
+	2676:  "simslink",
+	2677:  "gadgetgate1way",
+	2678:  "gadgetgate2way",
+	2679:  "syncserverssl",
+	2680:  "pxc-sapxom",
+	2681:  "mpnjsomb",
+	2683:  "ncdloadbalance",
+	2684:  "mpnjsosv",
+	2685:  "mpnjsocl",
+	2686:  "mpnjsomg",
+	2687:  "pq-lic-mgmt",
+	2688:  "md-cg-http",
+	2689:  "fastlynx",
+	2690:  "hp-nnm-data",
+	2691:  "itinternet",
+	2692:  "admins-lms",
+	2694:  "pwrsevent",
+	2695:  "vspread",
+	2696:  "unifyadmin",
+	2697:  "oce-snmp-trap",
+	2698:  "mck-ivpip",
+	2699:  "csoft-plusclnt",
+	2700:  "tqdata",
+	2701:  "sms-rcinfo",
+	2702:  "sms-xfer",
+	2703:  "sms-chat",
+	2704:  "sms-remctrl",
+	2705:  "sds-admin",
+	2706:  "ncdmirroring",
+	2707:  "emcsymapiport",
+	2708:  "banyan-net",
+	2709:  "supermon",
+	2710:  "sso-service",
+	2711:  "sso-control",
+	2712:  "aocp",
+	2713:  "raventbs",
+	2714:  "raventdm",
+	2715:  "hpstgmgr2",
+	2716:  "inova-ip-disco",
+	2717:  "pn-requester",
+	2718:  "pn-requester2",
+	2719:  "scan-change",
+	2720:  "wkars",
+	2721:  "smart-diagnose",
+	2722:  "proactivesrvr",
+	2723:  "watchdog-nt",
+	2724:  "qotps",
+	2725:  "msolap-ptp2",
+	2726:  "tams",
+	2727:  "mgcp-callagent",
+	2728:  "sqdr",
+	2729:  "tcim-control",
+	2730:  "nec-raidplus",
+	2731:  "fyre-messanger",
+	2732:  "g5m",
+	2733:  "signet-ctf",
+	2734:  "ccs-software",
+	2735:  "netiq-mc",
+	2736:  "radwiz-nms-srv",
+	2737:  "srp-feedback",
+	2738:  "ndl-tcp-ois-gw",
+	2739:  "tn-timing",
+	2740:  "alarm",
+	2741:  "tsb",
+	2742:  "tsb2",
+	2743:  "murx",
+	2744:  "honyaku",
+	2745:  "urbisnet",
+	2746:  "cpudpencap",
+	2747:  "fjippol-swrly",
+	2748:  "fjippol-polsvr",
+	2749:  "fjippol-cnsl",
+	2750:  "fjippol-port1",
+	2751:  "fjippol-port2",
+	2752:  "rsisysaccess",
+	2753:  "de-spot",
+	2754:  "apollo-cc",
+	2755:  "expresspay",
+	2756:  "simplement-tie",
+	2757:  "cnrp",
+	2758:  "apollo-status",
+	2759:  "apollo-gms",
+	2760:  "sabams",
+	2761:  "dicom-iscl",
+	2762:  "dicom-tls",
+	2763:  "desktop-dna",
+	2764:  "data-insurance",
+	2765:  "qip-audup",
+	2766:  "compaq-scp",
+	2767:  "uadtc",
+	2768:  "uacs",
+	2769:  "exce",
+	2770:  "veronica",
+	2771:  "vergencecm",
+	2772:  "auris",
+	2773:  "rbakcup1",
+	2774:  "rbakcup2",
+	2775:  "smpp",
+	2776:  "ridgeway1",
+	2777:  "ridgeway2",
+	2778:  "gwen-sonya",
+	2779:  "lbc-sync",
+	2780:  "lbc-control",
+	2781:  "whosells",
+	2782:  "everydayrc",
+	2783:  "aises",
+	2784:  "www-dev",
+	2785:  "aic-np",
+	2786:  "aic-oncrpc",
+	2787:  "piccolo",
+	2788:  "fryeserv",
+	2789:  "media-agent",
+	2790:  "plgproxy",
+	2791:  "mtport-regist",
+	2792:  "f5-globalsite",
+	2793:  "initlsmsad",
+	2795:  "livestats",
+	2796:  "ac-tech",
+	2797:  "esp-encap",
+	2798:  "tmesis-upshot",
+	2799:  "icon-discover",
+	2800:  "acc-raid",
+	2801:  "igcp",
+	2802:  "veritas-tcp1",
+	2803:  "btprjctrl",
+	2804:  "dvr-esm",
+	2805:  "wta-wsp-s",
+	2806:  "cspuni",
+	2807:  "cspmulti",
+	2808:  "j-lan-p",
+	2809:  "corbaloc",
+	2810:  "netsteward",
+	2811:  "gsiftp",
+	2812:  "atmtcp",
+	2813:  "llm-pass",
+	2814:  "llm-csv",
+	2815:  "lbc-measure",
+	2816:  "lbc-watchdog",
+	2817:  "nmsigport",
+	2818:  "rmlnk",
+	2819:  "fc-faultnotify",
+	2820:  "univision",
+	2821:  "vrts-at-port",
+	2822:  "ka0wuc",
+	2823:  "cqg-netlan",
+	2824:  "cqg-netlan-1",
+	2826:  "slc-systemlog",
+	2827:  "slc-ctrlrloops",
+	2828:  "itm-lm",
+	2829:  "silkp1",
+	2830:  "silkp2",
+	2831:  "silkp3",
+	2832:  "silkp4",
+	2833:  "glishd",
+	2834:  "evtp",
+	2835:  "evtp-data",
+	2836:  "catalyst",
+	2837:  "repliweb",
+	2838:  "starbot",
+	2839:  "nmsigport",
+	2840:  "l3-exprt",
+	2841:  "l3-ranger",
+	2842:  "l3-hawk",
+	2843:  "pdnet",
+	2844:  "bpcp-poll",
+	2845:  "bpcp-trap",
+	2846:  "aimpp-hello",
+	2847:  "aimpp-port-req",
+	2848:  "amt-blc-port",
+	2849:  "fxp",
+	2850:  "metaconsole",
+	2851:  "webemshttp",
+	2852:  "bears-01",
+	2853:  "ispipes",
+	2854:  "infomover",
+	2855:  "msrp",
+	2856:  "cesdinv",
+	2857:  "simctlp",
+	2858:  "ecnp",
+	2859:  "activememory",
+	2860:  "dialpad-voice1",
+	2861:  "dialpad-voice2",
+	2862:  "ttg-protocol",
+	2863:  "sonardata",
+	2864:  "astromed-main",
+	2865:  "pit-vpn",
+	2866:  "iwlistener",
+	2867:  "esps-portal",
+	2868:  "npep-messaging",
+	2869:  "icslap",
+	2870:  "daishi",
+	2871:  "msi-selectplay",
+	2872:  "radix",
+	2874:  "dxmessagebase1",
+	2875:  "dxmessagebase2",
+	2876:  "sps-tunnel",
+	2877:  "bluelance",
+	2878:  "aap",
+	2879:  "ucentric-ds",
+	2880:  "synapse",
+	2881:  "ndsp",
+	2882:  "ndtp",
+	2883:  "ndnp",
+	2884:  "flashmsg",
+	2885:  "topflow",
+	2886:  "responselogic",
+	2887:  "aironetddp",
+	2888:  "spcsdlobby",
+	2889:  "rsom",
+	2890:  "cspclmulti",
+	2891:  "cinegrfx-elmd",
+	2892:  "snifferdata",
+	2893:  "vseconnector",
+	2894:  "abacus-remote",
+	2895:  "natuslink",
+	2896:  "ecovisiong6-1",
+	2897:  "citrix-rtmp",
+	2898:  "appliance-cfg",
+	2899:  "powergemplus",
+	2900:  "quicksuite",
+	2901:  "allstorcns",
+	2902:  "netaspi",
+	2903:  "suitcase",
+	2904:  "m2ua",
+	2905:  "m3ua",
+	2906:  "caller9",
+	2907:  "webmethods-b2b",
+	2908:  "mao",
+	2909:  "funk-dialout",
+	2910:  "tdaccess",
+	2911:  "blockade",
+	2912:  "epicon",
+	2913:  "boosterware",
+	2914:  "gamelobby",
+	2915:  "tksocket",
+	2916:  "elvin-server",
+	2917:  "elvin-client",
+	2918:  "kastenchasepad",
+	2919:  "roboer",
+	2920:  "roboeda",
+	2921:  "cesdcdman",
+	2922:  "cesdcdtrn",
+	2923:  "wta-wsp-wtp-s",
+	2924:  "precise-vip",
+	2926:  "mobile-file-dl",
+	2927:  "unimobilectrl",
+	2928:  "redstone-cpss",
+	2929:  "amx-webadmin",
+	2930:  "amx-weblinx",
+	2931:  "circle-x",
+	2932:  "incp",
+	2933:  "4-tieropmgw",
+	2934:  "4-tieropmcli",
+	2935:  "qtp",
+	2936:  "otpatch",
+	2937:  "pnaconsult-lm",
+	2938:  "sm-pas-1",
+	2939:  "sm-pas-2",
+	2940:  "sm-pas-3",
+	2941:  "sm-pas-4",
+	2942:  "sm-pas-5",
+	2943:  "ttnrepository",
+	2944:  "megaco-h248",
+	2945:  "h248-binary",
+	2946:  "fjsvmpor",
+	2947:  "gpsd",
+	2948:  "wap-push",
+	2949:  "wap-pushsecure",
+	2950:  "esip",
+	2951:  "ottp",
+	2952:  "mpfwsas",
+	2953:  "ovalarmsrv",
+	2954:  "ovalarmsrv-cmd",
+	2955:  "csnotify",
+	2956:  "ovrimosdbman",
+	2957:  "jmact5",
+	2958:  "jmact6",
+	2959:  "rmopagt",
+	2960:  "dfoxserver",
+	2961:  "boldsoft-lm",
+	2962:  "iph-policy-cli",
+	2963:  "iph-policy-adm",
+	2964:  "bullant-srap",
+	2965:  "bullant-rap",
+	2966:  "idp-infotrieve",
+	2967:  "ssc-agent",
+	2968:  "enpp",
+	2969:  "essp",
+	2970:  "index-net",
+	2971:  "netclip",
+	2972:  "pmsm-webrctl",
+	2973:  "svnetworks",
+	2974:  "signal",
+	2975:  "fjmpcm",
+	2976:  "cns-srv-port",
+	2977:  "ttc-etap-ns",
+	2978:  "ttc-etap-ds",
+	2979:  "h263-video",
+	2980:  "wimd",
+	2981:  "mylxamport",
+	2982:  "iwb-whiteboard",
+	2983:  "netplan",
+	2984:  "hpidsadmin",
+	2985:  "hpidsagent",
+	2986:  "stonefalls",
+	2987:  "identify",
+	2988:  "hippad",
+	2989:  "zarkov",
+	2990:  "boscap",
+	2991:  "wkstn-mon",
+	2992:  "avenyo",
+	2993:  "veritas-vis1",
+	2994:  "veritas-vis2",
+	2995:  "idrs",
+	2996:  "vsixml",
+	2997:  "rebol",
+	2998:  "realsecure",
+	2999:  "remoteware-un",
+	3000:  "hbci",
+	3001:  "origo-native",
+	3002:  "exlm-agent",
+	3003:  "cgms",
+	3004:  "csoftragent",
+	3005:  "geniuslm",
+	3006:  "ii-admin",
+	3007:  "lotusmtap",
+	3008:  "midnight-tech",
+	3009:  "pxc-ntfy",
+	3010:  "gw",
+	3011:  "trusted-web",
+	3012:  "twsdss",
+	3013:  "gilatskysurfer",
+	3014:  "broker-service",
+	3015:  "nati-dstp",
+	3016:  "notify-srvr",
+	3017:  "event-listener",
+	3018:  "srvc-registry",
+	3019:  "resource-mgr",
+	3020:  "cifs",
+	3021:  "agriserver",
+	3022:  "csregagent",
+	3023:  "magicnotes",
+	3024:  "nds-sso",
+	3025:  "arepa-raft",
+	3026:  "agri-gateway",
+	3027:  "LiebDevMgmt-C",
+	3028:  "LiebDevMgmt-DM",
+	3029:  "LiebDevMgmt-A",
+	3030:  "arepa-cas",
+	3031:  "eppc",
+	3032:  "redwood-chat",
+	3033:  "pdb",
+	3034:  "osmosis-aeea",
+	3035:  "fjsv-gssagt",
+	3036:  "hagel-dump",
+	3037:  "hp-san-mgmt",
+	3038:  "santak-ups",
+	3039:  "cogitate",
+	3040:  "tomato-springs",
+	3041:  "di-traceware",
+	3042:  "journee",
+	3043:  "brp",
+	3044:  "epp",
+	3045:  "responsenet",
+	3046:  "di-ase",
+	3047:  "hlserver",
+	3048:  "pctrader",
+	3049:  "nsws",
+	3050:  "gds-db",
+	3051:  "galaxy-server",
+	3052:  "apc-3052",
+	3053:  "dsom-server",
+	3054:  "amt-cnf-prot",
+	3055:  "policyserver",
+	3056:  "cdl-server",
+	3057:  "goahead-fldup",
+	3058:  "videobeans",
+	3059:  "qsoft",
+	3060:  "interserver",
+	3061:  "cautcpd",
+	3062:  "ncacn-ip-tcp",
+	3063:  "ncadg-ip-udp",
+	3064:  "rprt",
+	3065:  "slinterbase",
+	3066:  "netattachsdmp",
+	3067:  "fjhpjp",
+	3068:  "ls3bcast",
+	3069:  "ls3",
+	3070:  "mgxswitch",
+	3071:  "xplat-replicate",
+	3072:  "csd-monitor",
+	3073:  "vcrp",
+	3074:  "xbox",
+	3075:  "orbix-locator",
+	3076:  "orbix-config",
+	3077:  "orbix-loc-ssl",
+	3078:  "orbix-cfg-ssl",
+	3079:  "lv-frontpanel",
+	3080:  "stm-pproc",
+	3081:  "tl1-lv",
+	3082:  "tl1-raw",
+	3083:  "tl1-telnet",
+	3084:  "itm-mccs",
+	3085:  "pcihreq",
+	3086:  "jdl-dbkitchen",
+	3087:  "asoki-sma",
+	3088:  "xdtp",
+	3089:  "ptk-alink",
+	3090:  "stss",
+	3091:  "1ci-smcs",
+	3093:  "rapidmq-center",
+	3094:  "rapidmq-reg",
+	3095:  "panasas",
+	3096:  "ndl-aps",
+	3098:  "umm-port",
+	3099:  "chmd",
+	3100:  "opcon-xps",
+	3101:  "hp-pxpib",
+	3102:  "slslavemon",
+	3103:  "autocuesmi",
+	3104:  "autocuelog",
+	3105:  "cardbox",
+	3106:  "cardbox-http",
+	3107:  "business",
+	3108:  "geolocate",
+	3109:  "personnel",
+	3110:  "sim-control",
+	3111:  "wsynch",
+	3112:  "ksysguard",
+	3113:  "cs-auth-svr",
+	3114:  "ccmad",
+	3115:  "mctet-master",
+	3116:  "mctet-gateway",
+	3117:  "mctet-jserv",
+	3118:  "pkagent",
+	3119:  "d2000kernel",
+	3120:  "d2000webserver",
+	3121:  "pcmk-remote",
+	3122:  "vtr-emulator",
+	3123:  "edix",
+	3124:  "beacon-port",
+	3125:  "a13-an",
+	3127:  "ctx-bridge",
+	3128:  "ndl-aas",
+	3129:  "netport-id",
+	3130:  "icpv2",
+	3131:  "netbookmark",
+	3132:  "ms-rule-engine",
+	3133:  "prism-deploy",
+	3134:  "ecp",
+	3135:  "peerbook-port",
+	3136:  "grubd",
+	3137:  "rtnt-1",
+	3138:  "rtnt-2",
+	3139:  "incognitorv",
+	3140:  "ariliamulti",
+	3141:  "vmodem",
+	3142:  "rdc-wh-eos",
+	3143:  "seaview",
+	3144:  "tarantella",
+	3145:  "csi-lfap",
+	3146:  "bears-02",
+	3147:  "rfio",
+	3148:  "nm-game-admin",
+	3149:  "nm-game-server",
+	3150:  "nm-asses-admin",
+	3151:  "nm-assessor",
+	3152:  "feitianrockey",
+	3153:  "s8-client-port",
+	3154:  "ccmrmi",
+	3155:  "jpegmpeg",
+	3156:  "indura",
+	3157:  "e3consultants",
+	3158:  "stvp",
+	3159:  "navegaweb-port",
+	3160:  "tip-app-server",
+	3161:  "doc1lm",
+	3162:  "sflm",
+	3163:  "res-sap",
+	3164:  "imprs",
+	3165:  "newgenpay",
+	3166:  "sossecollector",
+	3167:  "nowcontact",
+	3168:  "poweronnud",
+	3169:  "serverview-as",
+	3170:  "serverview-asn",
+	3171:  "serverview-gf",
+	3172:  "serverview-rm",
+	3173:  "serverview-icc",
+	3174:  "armi-server",
+	3175:  "t1-e1-over-ip",
+	3176:  "ars-master",
+	3177:  "phonex-port",
+	3178:  "radclientport",
+	3179:  "h2gf-w-2m",
+	3180:  "mc-brk-srv",
+	3181:  "bmcpatrolagent",
+	3182:  "bmcpatrolrnvu",
+	3183:  "cops-tls",
+	3184:  "apogeex-port",
+	3185:  "smpppd",
+	3186:  "iiw-port",
+	3187:  "odi-port",
+	3188:  "brcm-comm-port",
+	3189:  "pcle-infex",
+	3190:  "csvr-proxy",
+	3191:  "csvr-sslproxy",
+	3192:  "firemonrcc",
+	3193:  "spandataport",
+	3194:  "magbind",
+	3195:  "ncu-1",
+	3196:  "ncu-2",
+	3197:  "embrace-dp-s",
+	3198:  "embrace-dp-c",
+	3199:  "dmod-workspace",
+	3200:  "tick-port",
+	3201:  "cpq-tasksmart",
+	3202:  "intraintra",
+	3203:  "netwatcher-mon",
+	3204:  "netwatcher-db",
+	3205:  "isns",
+	3206:  "ironmail",
+	3207:  "vx-auth-port",
+	3208:  "pfu-prcallback",
+	3209:  "netwkpathengine",
+	3210:  "flamenco-proxy",
+	3211:  "avsecuremgmt",
+	3212:  "surveyinst",
+	3213:  "neon24x7",
+	3214:  "jmq-daemon-1",
+	3215:  "jmq-daemon-2",
+	3216:  "ferrari-foam",
+	3217:  "unite",
+	3218:  "smartpackets",
+	3219:  "wms-messenger",
+	3220:  "xnm-ssl",
+	3221:  "xnm-clear-text",
+	3222:  "glbp",
+	3223:  "digivote",
+	3224:  "aes-discovery",
+	3225:  "fcip-port",
+	3226:  "isi-irp",
+	3227:  "dwnmshttp",
+	3228:  "dwmsgserver",
+	3229:  "global-cd-port",
+	3230:  "sftdst-port",
+	3231:  "vidigo",
+	3232:  "mdtp",
+	3233:  "whisker",
+	3234:  "alchemy",
+	3235:  "mdap-port",
+	3236:  "apparenet-ts",
+	3237:  "apparenet-tps",
+	3238:  "apparenet-as",
+	3239:  "apparenet-ui",
+	3240:  "triomotion",
+	3241:  "sysorb",
+	3242:  "sdp-id-port",
+	3243:  "timelot",
+	3244:  "onesaf",
+	3245:  "vieo-fe",
+	3246:  "dvt-system",
+	3247:  "dvt-data",
+	3248:  "procos-lm",
+	3249:  "ssp",
+	3250:  "hicp",
+	3251:  "sysscanner",
+	3252:  "dhe",
+	3253:  "pda-data",
+	3254:  "pda-sys",
+	3255:  "semaphore",
+	3256:  "cpqrpm-agent",
+	3257:  "cpqrpm-server",
+	3258:  "ivecon-port",
+	3259:  "epncdp2",
+	3260:  "iscsi-target",
+	3261:  "winshadow",
+	3262:  "necp",
+	3263:  "ecolor-imager",
+	3264:  "ccmail",
+	3265:  "altav-tunnel",
+	3266:  "ns-cfg-server",
+	3267:  "ibm-dial-out",
+	3268:  "msft-gc",
+	3269:  "msft-gc-ssl",
+	3270:  "verismart",
+	3271:  "csoft-prev",
+	3272:  "user-manager",
+	3273:  "sxmp",
+	3274:  "ordinox-server",
+	3275:  "samd",
+	3276:  "maxim-asics",
+	3277:  "awg-proxy",
+	3278:  "lkcmserver",
+	3279:  "admind",
+	3280:  "vs-server",
+	3281:  "sysopt",
+	3282:  "datusorb",
+	3283:  "Apple Remote Desktop (Net Assistant)",
+	3284:  "4talk",
+	3285:  "plato",
+	3286:  "e-net",
+	3287:  "directvdata",
+	3288:  "cops",
+	3289:  "enpc",
+	3290:  "caps-lm",
+	3291:  "sah-lm",
+	3292:  "cart-o-rama",
+	3293:  "fg-fps",
+	3294:  "fg-gip",
+	3295:  "dyniplookup",
+	3296:  "rib-slm",
+	3297:  "cytel-lm",
+	3298:  "deskview",
+	3299:  "pdrncs",
+	3300:  "ceph",
+	3302:  "mcs-fastmail",
+	3303:  "opsession-clnt",
+	3304:  "opsession-srvr",
+	3305:  "odette-ftp",
+	3306:  "mysql",
+	3307:  "opsession-prxy",
+	3308:  "tns-server",
+	3309:  "tns-adv",
+	3310:  "dyna-access",
+	3311:  "mcns-tel-ret",
+	3312:  "appman-server",
+	3313:  "uorb",
+	3314:  "uohost",
+	3315:  "cdid",
+	3316:  "aicc-cmi",
+	3317:  "vsaiport",
+	3318:  "ssrip",
+	3319:  "sdt-lmd",
+	3320:  "officelink2000",
+	3321:  "vnsstr",
+	3326:  "sftu",
+	3327:  "bbars",
+	3328:  "egptlm",
+	3329:  "hp-device-disc",
+	3330:  "mcs-calypsoicf",
+	3331:  "mcs-messaging",
+	3332:  "mcs-mailsvr",
+	3333:  "dec-notes",
+	3334:  "directv-web",
+	3335:  "directv-soft",
+	3336:  "directv-tick",
+	3337:  "directv-catlg",
+	3338:  "anet-b",
+	3339:  "anet-l",
+	3340:  "anet-m",
+	3341:  "anet-h",
+	3342:  "webtie",
+	3343:  "ms-cluster-net",
+	3344:  "bnt-manager",
+	3345:  "influence",
+	3346:  "trnsprntproxy",
+	3347:  "phoenix-rpc",
+	3348:  "pangolin-laser",
+	3349:  "chevinservices",
+	3350:  "findviatv",
+	3351:  "btrieve",
+	3352:  "ssql",
+	3353:  "fatpipe",
+	3354:  "suitjd",
+	3355:  "ordinox-dbase",
+	3356:  "upnotifyps",
+	3357:  "adtech-test",
+	3358:  "mpsysrmsvr",
+	3359:  "wg-netforce",
+	3360:  "kv-server",
+	3361:  "kv-agent",
+	3362:  "dj-ilm",
+	3363:  "nati-vi-server",
+	3364:  "creativeserver",
+	3365:  "contentserver",
+	3366:  "creativepartnr",
+	3372:  "tip2",
+	3373:  "lavenir-lm",
+	3374:  "cluster-disc",
+	3375:  "vsnm-agent",
+	3376:  "cdbroker",
+	3377:  "cogsys-lm",
+	3378:  "wsicopy",
+	3379:  "socorfs",
+	3380:  "sns-channels",
+	3381:  "geneous",
+	3382:  "fujitsu-neat",
+	3383:  "esp-lm",
+	3384:  "hp-clic",
+	3385:  "qnxnetman",
+	3386:  "gprs-data",
+	3387:  "backroomnet",
+	3388:  "cbserver",
+	3389:  "ms-wbt-server",
+	3390:  "dsc",
+	3391:  "savant",
+	3392:  "efi-lm",
+	3393:  "d2k-tapestry1",
+	3394:  "d2k-tapestry2",
+	3395:  "dyna-lm",
+	3396:  "printer-agent",
+	3397:  "cloanto-lm",
+	3398:  "mercantile",
+	3399:  "csms",
+	3400:  "csms2",
+	3401:  "filecast",
+	3402:  "fxaengine-net",
+	3405:  "nokia-ann-ch1",
+	3406:  "nokia-ann-ch2",
+	3407:  "ldap-admin",
+	3408:  "BESApi",
+	3409:  "networklens",
+	3410:  "networklenss",
+	3411:  "biolink-auth",
+	3412:  "xmlblaster",
+	3413:  "svnet",
+	3414:  "wip-port",
+	3415:  "bcinameservice",
+	3416:  "commandport",
+	3417:  "csvr",
+	3418:  "rnmap",
+	3419:  "softaudit",
+	3420:  "ifcp-port",
+	3421:  "bmap",
+	3422:  "rusb-sys-port",
+	3423:  "xtrm",
+	3424:  "xtrms",
+	3425:  "agps-port",
+	3426:  "arkivio",
+	3427:  "websphere-snmp",
+	3428:  "twcss",
+	3429:  "gcsp",
+	3430:  "ssdispatch",
+	3431:  "ndl-als",
+	3432:  "osdcp",
+	3433:  "opnet-smp",
+	3434:  "opencm",
+	3435:  "pacom",
+	3436:  "gc-config",
+	3437:  "autocueds",
+	3438:  "spiral-admin",
+	3439:  "hri-port",
+	3440:  "ans-console",
+	3441:  "connect-client",
+	3442:  "connect-server",
+	3443:  "ov-nnm-websrv",
+	3444:  "denali-server",
+	3445:  "monp",
+	3446:  "3comfaxrpc",
+	3447:  "directnet",
+	3448:  "dnc-port",
+	3449:  "hotu-chat",
+	3450:  "castorproxy",
+	3451:  "asam",
+	3452:  "sabp-signal",
+	3453:  "pscupd",
+	3454:  "mira",
+	3455:  "prsvp",
+	3456:  "vat",
+	3457:  "vat-control",
+	3458:  "d3winosfi",
+	3459:  "integral",
+	3460:  "edm-manager",
+	3461:  "edm-stager",
+	3462:  "edm-std-notify",
+	3463:  "edm-adm-notify",
+	3464:  "edm-mgr-sync",
+	3465:  "edm-mgr-cntrl",
+	3466:  "workflow",
+	3467:  "rcst",
+	3468:  "ttcmremotectrl",
+	3469:  "pluribus",
+	3470:  "jt400",
+	3471:  "jt400-ssl",
+	3472:  "jaugsremotec-1",
+	3473:  "jaugsremotec-2",
+	3474:  "ttntspauto",
+	3475:  "genisar-port",
+	3476:  "nppmp",
+	3477:  "ecomm",
+	3478:  "stun",
+	3479:  "twrpc",
+	3480:  "plethora",
+	3481:  "cleanerliverc",
+	3482:  "vulture",
+	3483:  "slim-devices",
+	3484:  "gbs-stp",
+	3485:  "celatalk",
+	3486:  "ifsf-hb-port",
+	3487:  "ltctcp",
+	3488:  "fs-rh-srv",
+	3489:  "dtp-dia",
+	3490:  "colubris",
+	3491:  "swr-port",
+	3492:  "tvdumtray-port",
+	3493:  "nut",
+	3494:  "ibm3494",
+	3495:  "seclayer-tcp",
+	3496:  "seclayer-tls",
+	3497:  "ipether232port",
+	3498:  "dashpas-port",
+	3499:  "sccip-media",
+	3500:  "rtmp-port",
+	3501:  "isoft-p2p",
+	3502:  "avinstalldisc",
+	3503:  "lsp-ping",
+	3504:  "ironstorm",
+	3505:  "ccmcomm",
+	3506:  "apc-3506",
+	3507:  "nesh-broker",
+	3508:  "interactionweb",
+	3509:  "vt-ssl",
+	3510:  "xss-port",
+	3511:  "webmail-2",
+	3512:  "aztec",
+	3513:  "arcpd",
+	3514:  "must-p2p",
+	3515:  "must-backplane",
+	3516:  "smartcard-port",
+	3517:  "802-11-iapp",
+	3518:  "artifact-msg",
+	3519:  "nvmsgd",
+	3520:  "galileolog",
+	3521:  "mc3ss",
+	3522:  "nssocketport",
+	3523:  "odeumservlink",
+	3524:  "ecmport",
+	3525:  "eisport",
+	3526:  "starquiz-port",
+	3527:  "beserver-msg-q",
+	3528:  "jboss-iiop",
+	3529:  "jboss-iiop-ssl",
+	3530:  "gf",
+	3531:  "joltid",
+	3532:  "raven-rmp",
+	3533:  "raven-rdp",
+	3534:  "urld-port",
+	3535:  "ms-la",
+	3536:  "snac",
+	3537:  "ni-visa-remote",
+	3538:  "ibm-diradm",
+	3539:  "ibm-diradm-ssl",
+	3540:  "pnrp-port",
+	3541:  "voispeed-port",
+	3542:  "hacl-monitor",
+	3543:  "qftest-lookup",
+	3544:  "teredo",
+	3545:  "camac",
+	3547:  "symantec-sim",
+	3548:  "interworld",
+	3549:  "tellumat-nms",
+	3550:  "ssmpp",
+	3551:  "apcupsd",
+	3552:  "taserver",
+	3553:  "rbr-discovery",
+	3554:  "questnotify",
+	3555:  "razor",
+	3556:  "sky-transport",
+	3557:  "personalos-001",
+	3558:  "mcp-port",
+	3559:  "cctv-port",
+	3560:  "iniserve-port",
+	3561:  "bmc-onekey",
+	3562:  "sdbproxy",
+	3563:  "watcomdebug",
+	3564:  "esimport",
+	3565:  "m2pa",
+	3566:  "quest-data-hub",
+	3567:  "dof-eps",
+	3568:  "dof-tunnel-sec",
+	3569:  "mbg-ctrl",
+	3570:  "mccwebsvr-port",
+	3571:  "megardsvr-port",
+	3572:  "megaregsvrport",
+	3573:  "tag-ups-1",
+	3574:  "dmaf-server",
+	3575:  "ccm-port",
+	3576:  "cmc-port",
+	3577:  "config-port",
+	3578:  "data-port",
+	3579:  "ttat3lb",
+	3580:  "nati-svrloc",
+	3581:  "kfxaclicensing",
+	3582:  "press",
+	3583:  "canex-watch",
+	3584:  "u-dbap",
+	3585:  "emprise-lls",
+	3586:  "emprise-lsc",
+	3587:  "p2pgroup",
+	3588:  "sentinel",
+	3589:  "isomair",
+	3590:  "wv-csp-sms",
+	3591:  "gtrack-server",
+	3592:  "gtrack-ne",
+	3593:  "bpmd",
+	3594:  "mediaspace",
+	3595:  "shareapp",
+	3596:  "iw-mmogame",
+	3597:  "a14",
+	3598:  "a15",
+	3599:  "quasar-server",
+	3600:  "trap-daemon",
+	3601:  "visinet-gui",
+	3602:  "infiniswitchcl",
+	3603:  "int-rcv-cntrl",
+	3604:  "bmc-jmx-port",
+	3605:  "comcam-io",
+	3606:  "splitlock",
+	3607:  "precise-i3",
+	3608:  "trendchip-dcp",
+	3609:  "cpdi-pidas-cm",
+	3610:  "echonet",
+	3611:  "six-degrees",
+	3612:  "hp-dataprotect",
+	3613:  "alaris-disc",
+	3614:  "sigma-port",
+	3615:  "start-network",
+	3616:  "cd3o-protocol",
+	3617:  "sharp-server",
+	3618:  "aairnet-1",
+	3619:  "aairnet-2",
+	3620:  "ep-pcp",
+	3621:  "ep-nsp",
+	3622:  "ff-lr-port",
+	3623:  "haipe-discover",
+	3624:  "dist-upgrade",
+	3625:  "volley",
+	3626:  "bvcdaemon-port",
+	3627:  "jamserverport",
+	3628:  "ept-machine",
+	3629:  "escvpnet",
+	3630:  "cs-remote-db",
+	3631:  "cs-services",
+	3632:  "distcc",
+	3633:  "wacp",
+	3634:  "hlibmgr",
+	3635:  "sdo",
+	3636:  "servistaitsm",
+	3637:  "scservp",
+	3638:  "ehp-backup",
+	3639:  "xap-ha",
+	3640:  "netplay-port1",
+	3641:  "netplay-port2",
+	3642:  "juxml-port",
+	3643:  "audiojuggler",
+	3644:  "ssowatch",
+	3645:  "cyc",
+	3646:  "xss-srv-port",
+	3647:  "splitlock-gw",
+	3648:  "fjcp",
+	3649:  "nmmp",
+	3650:  "prismiq-plugin",
+	3651:  "xrpc-registry",
+	3652:  "vxcrnbuport",
+	3653:  "tsp",
+	3654:  "vaprtm",
+	3655:  "abatemgr",
+	3656:  "abatjss",
+	3657:  "immedianet-bcn",
+	3658:  "ps-ams",
+	3659:  "apple-sasl",
+	3660:  "can-nds-ssl",
+	3661:  "can-ferret-ssl",
+	3662:  "pserver",
+	3663:  "dtp",
+	3664:  "ups-engine",
+	3665:  "ent-engine",
+	3666:  "eserver-pap",
+	3667:  "infoexch",
+	3668:  "dell-rm-port",
+	3669:  "casanswmgmt",
+	3670:  "smile",
+	3671:  "efcp",
+	3672:  "lispworks-orb",
+	3673:  "mediavault-gui",
+	3674:  "wininstall-ipc",
+	3675:  "calltrax",
+	3676:  "va-pacbase",
+	3677:  "roverlog",
+	3678:  "ipr-dglt",
+	3679:  "Escale (Newton Dock)",
+	3680:  "npds-tracker",
+	3681:  "bts-x73",
+	3682:  "cas-mapi",
+	3683:  "bmc-ea",
+	3684:  "faxstfx-port",
+	3685:  "dsx-agent",
+	3686:  "tnmpv2",
+	3687:  "simple-push",
+	3688:  "simple-push-s",
+	3689:  "daap",
+	3690:  "svn",
+	3691:  "magaya-network",
+	3692:  "intelsync",
+	3693:  "easl",
+	3695:  "bmc-data-coll",
+	3696:  "telnetcpcd",
+	3697:  "nw-license",
+	3698:  "sagectlpanel",
+	3699:  "kpn-icw",
+	3700:  "lrs-paging",
+	3701:  "netcelera",
+	3702:  "ws-discovery",
+	3703:  "adobeserver-3",
+	3704:  "adobeserver-4",
+	3705:  "adobeserver-5",
+	3706:  "rt-event",
+	3707:  "rt-event-s",
+	3708:  "sun-as-iiops",
+	3709:  "ca-idms",
+	3710:  "portgate-auth",
+	3711:  "edb-server2",
+	3712:  "sentinel-ent",
+	3713:  "tftps",
+	3714:  "delos-dms",
+	3715:  "anoto-rendezv",
+	3716:  "wv-csp-sms-cir",
+	3717:  "wv-csp-udp-cir",
+	3718:  "opus-services",
+	3719:  "itelserverport",
+	3720:  "ufastro-instr",
+	3721:  "xsync",
+	3722:  "xserveraid",
+	3723:  "sychrond",
+	3724:  "blizwow",
+	3725:  "na-er-tip",
+	3726:  "array-manager",
+	3727:  "e-mdu",
+	3728:  "e-woa",
+	3729:  "fksp-audit",
+	3730:  "client-ctrl",
+	3731:  "smap",
+	3732:  "m-wnn",
+	3733:  "multip-msg",
+	3734:  "synel-data",
+	3735:  "pwdis",
+	3736:  "rs-rmi",
+	3737:  "xpanel",
+	3738:  "versatalk",
+	3739:  "launchbird-lm",
+	3740:  "heartbeat",
+	3741:  "wysdma",
+	3742:  "cst-port",
+	3743:  "ipcs-command",
+	3744:  "sasg",
+	3745:  "gw-call-port",
+	3746:  "linktest",
+	3747:  "linktest-s",
+	3748:  "webdata",
+	3749:  "cimtrak",
+	3750:  "cbos-ip-port",
+	3751:  "gprs-cube",
+	3752:  "vipremoteagent",
+	3753:  "nattyserver",
+	3754:  "timestenbroker",
+	3755:  "sas-remote-hlp",
+	3756:  "canon-capt",
+	3757:  "grf-port",
+	3758:  "apw-registry",
+	3759:  "exapt-lmgr",
+	3760:  "adtempusclient",
+	3761:  "gsakmp",
+	3762:  "gbs-smp",
+	3763:  "xo-wave",
+	3764:  "mni-prot-rout",
+	3765:  "rtraceroute",
+	3766:  "sitewatch-s",
+	3767:  "listmgr-port",
+	3768:  "rblcheckd",
+	3769:  "haipe-otnk",
+	3770:  "cindycollab",
+	3771:  "paging-port",
+	3772:  "ctp",
+	3773:  "ctdhercules",
+	3774:  "zicom",
+	3775:  "ispmmgr",
+	3776:  "dvcprov-port",
+	3777:  "jibe-eb",
+	3778:  "c-h-it-port",
+	3779:  "cognima",
+	3780:  "nnp",
+	3781:  "abcvoice-port",
+	3782:  "iso-tp0s",
+	3783:  "bim-pem",
+	3784:  "bfd-control",
+	3785:  "bfd-echo",
+	3786:  "upstriggervsw",
+	3787:  "fintrx",
+	3788:  "isrp-port",
+	3789:  "remotedeploy",
+	3790:  "quickbooksrds",
+	3791:  "tvnetworkvideo",
+	3792:  "sitewatch",
+	3793:  "dcsoftware",
+	3794:  "jaus",
+	3795:  "myblast",
+	3796:  "spw-dialer",
+	3797:  "idps",
+	3798:  "minilock",
+	3799:  "radius-dynauth",
+	3800:  "pwgpsi",
+	3801:  "ibm-mgr",
+	3802:  "vhd",
+	3803:  "soniqsync",
+	3804:  "iqnet-port",
+	3805:  "tcpdataserver",
+	3806:  "wsmlb",
+	3807:  "spugna",
+	3808:  "sun-as-iiops-ca",
+	3809:  "apocd",
+	3810:  "wlanauth",
+	3811:  "amp",
+	3812:  "neto-wol-server",
+	3813:  "rap-ip",
+	3814:  "neto-dcs",
+	3815:  "lansurveyorxml",
+	3816:  "sunlps-http",
+	3817:  "tapeware",
+	3818:  "crinis-hb",
+	3819:  "epl-slp",
+	3820:  "scp",
+	3821:  "pmcp",
+	3822:  "acp-discovery",
+	3823:  "acp-conduit",
+	3824:  "acp-policy",
+	3825:  "ffserver",
+	3826:  "warmux",
+	3827:  "netmpi",
+	3828:  "neteh",
+	3829:  "neteh-ext",
+	3830:  "cernsysmgmtagt",
+	3831:  "dvapps",
+	3832:  "xxnetserver",
+	3833:  "aipn-auth",
+	3834:  "spectardata",
+	3835:  "spectardb",
+	3836:  "markem-dcp",
+	3837:  "mkm-discovery",
+	3838:  "sos",
+	3839:  "amx-rms",
+	3840:  "flirtmitmir",
+	3841:  "shiprush-db-svr",
+	3842:  "nhci",
+	3843:  "quest-agent",
+	3844:  "rnm",
+	3845:  "v-one-spp",
+	3846:  "an-pcp",
+	3847:  "msfw-control",
+	3848:  "item",
+	3849:  "spw-dnspreload",
+	3850:  "qtms-bootstrap",
+	3851:  "spectraport",
+	3852:  "sse-app-config",
+	3853:  "sscan",
+	3854:  "stryker-com",
+	3855:  "opentrac",
+	3856:  "informer",
+	3857:  "trap-port",
+	3858:  "trap-port-mom",
+	3859:  "nav-port",
+	3860:  "sasp",
+	3861:  "winshadow-hd",
+	3862:  "giga-pocket",
+	3863:  "asap-tcp",
+	3864:  "asap-tcp-tls",
+	3865:  "xpl",
+	3866:  "dzdaemon",
+	3867:  "dzoglserver",
+	3868:  "diameter",
+	3869:  "ovsam-mgmt",
+	3870:  "ovsam-d-agent",
+	3871:  "avocent-adsap",
+	3872:  "oem-agent",
+	3873:  "fagordnc",
+	3874:  "sixxsconfig",
+	3875:  "pnbscada",
+	3876:  "dl-agent",
+	3877:  "xmpcr-interface",
+	3878:  "fotogcad",
+	3879:  "appss-lm",
+	3880:  "igrs",
+	3881:  "idac",
+	3882:  "msdts1",
+	3883:  "vrpn",
+	3884:  "softrack-meter",
+	3885:  "topflow-ssl",
+	3886:  "nei-management",
+	3887:  "ciphire-data",
+	3888:  "ciphire-serv",
+	3889:  "dandv-tester",
+	3890:  "ndsconnect",
+	3891:  "rtc-pm-port",
+	3892:  "pcc-image-port",
+	3893:  "cgi-starapi",
+	3894:  "syam-agent",
+	3895:  "syam-smc",
+	3896:  "sdo-tls",
+	3897:  "sdo-ssh",
+	3898:  "senip",
+	3899:  "itv-control",
+	3900:  "udt-os",
+	3901:  "nimsh",
+	3902:  "nimaux",
+	3903:  "charsetmgr",
+	3904:  "omnilink-port",
+	3905:  "mupdate",
+	3906:  "topovista-data",
+	3907:  "imoguia-port",
+	3908:  "hppronetman",
+	3909:  "surfcontrolcpa",
+	3910:  "prnrequest",
+	3911:  "prnstatus",
+	3912:  "gbmt-stars",
+	3913:  "listcrt-port",
+	3914:  "listcrt-port-2",
+	3915:  "agcat",
+	3916:  "wysdmc",
+	3917:  "aftmux",
+	3918:  "pktcablemmcops",
+	3919:  "hyperip",
+	3920:  "exasoftport1",
+	3921:  "herodotus-net",
+	3922:  "sor-update",
+	3923:  "symb-sb-port",
+	3924:  "mpl-gprs-port",
+	3925:  "zmp",
+	3926:  "winport",
+	3927:  "natdataservice",
+	3928:  "netboot-pxe",
+	3929:  "smauth-port",
+	3930:  "syam-webserver",
+	3931:  "msr-plugin-port",
+	3932:  "dyn-site",
+	3933:  "plbserve-port",
+	3934:  "sunfm-port",
+	3935:  "sdp-portmapper",
+	3936:  "mailprox",
+	3937:  "dvbservdsc",
+	3938:  "dbcontrol-agent",
+	3939:  "aamp",
+	3940:  "xecp-node",
+	3941:  "homeportal-web",
+	3942:  "srdp",
+	3943:  "tig",
+	3944:  "sops",
+	3945:  "emcads",
+	3946:  "backupedge",
+	3947:  "ccp",
+	3948:  "apdap",
+	3949:  "drip",
+	3950:  "namemunge",
+	3951:  "pwgippfax",
+	3952:  "i3-sessionmgr",
+	3953:  "xmlink-connect",
+	3954:  "adrep",
+	3955:  "p2pcommunity",
+	3956:  "gvcp",
+	3957:  "mqe-broker",
+	3958:  "mqe-agent",
+	3959:  "treehopper",
+	3960:  "bess",
+	3961:  "proaxess",
+	3962:  "sbi-agent",
+	3963:  "thrp",
+	3964:  "sasggprs",
+	3965:  "ati-ip-to-ncpe",
+	3966:  "bflckmgr",
+	3967:  "ppsms",
+	3968:  "ianywhere-dbns",
+	3969:  "landmarks",
+	3970:  "lanrevagent",
+	3971:  "lanrevserver",
+	3972:  "iconp",
+	3973:  "progistics",
+	3974:  "citysearch",
+	3975:  "airshot",
+	3976:  "opswagent",
+	3977:  "opswmanager",
+	3978:  "secure-cfg-svr",
+	3979:  "smwan",
+	3980:  "acms",
+	3981:  "starfish",
+	3982:  "eis",
+	3983:  "eisp",
+	3984:  "mapper-nodemgr",
+	3985:  "mapper-mapethd",
+	3986:  "mapper-ws-ethd",
+	3987:  "centerline",
+	3988:  "dcs-config",
+	3989:  "bv-queryengine",
+	3990:  "bv-is",
+	3991:  "bv-smcsrv",
+	3992:  "bv-ds",
+	3993:  "bv-agent",
+	3995:  "iss-mgmt-ssl",
+	3996:  "abcsoftware",
+	3997:  "agentsease-db",
+	3998:  "dnx",
+	3999:  "nvcnet",
+	4000:  "terabase",
+	4001:  "newoak",
+	4002:  "pxc-spvr-ft",
+	4003:  "pxc-splr-ft",
+	4004:  "pxc-roid",
+	4005:  "pxc-pin",
+	4006:  "pxc-spvr",
+	4007:  "pxc-splr",
+	4008:  "netcheque",
+	4009:  "chimera-hwm",
+	4010:  "samsung-unidex",
+	4011:  "altserviceboot",
+	4012:  "pda-gate",
+	4013:  "acl-manager",
+	4014:  "taiclock",
+	4015:  "talarian-mcast1",
+	4016:  "talarian-mcast2",
+	4017:  "talarian-mcast3",
+	4018:  "talarian-mcast4",
+	4019:  "talarian-mcast5",
+	4020:  "trap",
+	4021:  "nexus-portal",
+	4022:  "dnox",
+	4023:  "esnm-zoning",
+	4024:  "tnp1-port",
+	4025:  "partimage",
+	4026:  "as-debug",
+	4027:  "bxp",
+	4028:  "dtserver-port",
+	4029:  "ip-qsig",
+	4030:  "jdmn-port",
+	4031:  "suucp",
+	4032:  "vrts-auth-port",
+	4033:  "sanavigator",
+	4034:  "ubxd",
+	4035:  "wap-push-http",
+	4036:  "wap-push-https",
+	4037:  "ravehd",
+	4038:  "fazzt-ptp",
+	4039:  "fazzt-admin",
+	4040:  "yo-main",
+	4041:  "houston",
+	4042:  "ldxp",
+	4043:  "nirp",
+	4044:  "ltp",
+	4045:  "npp",
+	4046:  "acp-proto",
+	4047:  "ctp-state",
+	4049:  "wafs",
+	4050:  "cisco-wafs",
+	4051:  "cppdp",
+	4052:  "interact",
+	4053:  "ccu-comm-1",
+	4054:  "ccu-comm-2",
+	4055:  "ccu-comm-3",
+	4056:  "lms",
+	4057:  "wfm",
+	4058:  "kingfisher",
+	4059:  "dlms-cosem",
+	4060:  "dsmeter-iatc",
+	4061:  "ice-location",
+	4062:  "ice-slocation",
+	4063:  "ice-router",
+	4064:  "ice-srouter",
+	4065:  "avanti-cdp",
+	4066:  "pmas",
+	4067:  "idp",
+	4068:  "ipfltbcst",
+	4069:  "minger",
+	4070:  "tripe",
+	4071:  "aibkup",
+	4072:  "zieto-sock",
+	4073:  "iRAPP",
+	4074:  "cequint-cityid",
+	4075:  "perimlan",
+	4076:  "seraph",
+	4078:  "cssp",
+	4079:  "santools",
+	4080:  "lorica-in",
+	4081:  "lorica-in-sec",
+	4082:  "lorica-out",
+	4083:  "lorica-out-sec",
+	4085:  "ezmessagesrv",
+	4087:  "applusservice",
+	4088:  "npsp",
+	4089:  "opencore",
+	4090:  "omasgport",
+	4091:  "ewinstaller",
+	4092:  "ewdgs",
+	4093:  "pvxpluscs",
+	4094:  "sysrqd",
+	4095:  "xtgui",
+	4096:  "bre",
+	4097:  "patrolview",
+	4098:  "drmsfsd",
+	4099:  "dpcp",
+	4100:  "igo-incognito",
+	4101:  "brlp-0",
+	4102:  "brlp-1",
+	4103:  "brlp-2",
+	4104:  "brlp-3",
+	4105:  "shofar",
+	4106:  "synchronite",
+	4107:  "j-ac",
+	4108:  "accel",
+	4109:  "izm",
+	4110:  "g2tag",
+	4111:  "xgrid",
+	4112:  "apple-vpns-rp",
+	4113:  "aipn-reg",
+	4114:  "jomamqmonitor",
+	4115:  "cds",
+	4116:  "smartcard-tls",
+	4117:  "hillrserv",
+	4118:  "netscript",
+	4119:  "assuria-slm",
+	4120:  "minirem",
+	4121:  "e-builder",
+	4122:  "fprams",
+	4123:  "z-wave",
+	4124:  "tigv2",
+	4125:  "opsview-envoy",
+	4126:  "ddrepl",
+	4127:  "unikeypro",
+	4128:  "nufw",
+	4129:  "nuauth",
+	4130:  "fronet",
+	4131:  "stars",
+	4132:  "nuts-dem",
+	4133:  "nuts-bootp",
+	4134:  "nifty-hmi",
+	4135:  "cl-db-attach",
+	4136:  "cl-db-request",
+	4137:  "cl-db-remote",
+	4138:  "nettest",
+	4139:  "thrtx",
+	4140:  "cedros-fds",
+	4141:  "oirtgsvc",
+	4142:  "oidocsvc",
+	4143:  "oidsr",
+	4145:  "vvr-control",
+	4146:  "tgcconnect",
+	4147:  "vrxpservman",
+	4148:  "hhb-handheld",
+	4149:  "agslb",
+	4150:  "PowerAlert-nsa",
+	4151:  "menandmice-noh",
+	4152:  "idig-mux",
+	4153:  "mbl-battd",
+	4154:  "atlinks",
+	4155:  "bzr",
+	4156:  "stat-results",
+	4157:  "stat-scanner",
+	4158:  "stat-cc",
+	4159:  "nss",
+	4160:  "jini-discovery",
+	4161:  "omscontact",
+	4162:  "omstopology",
+	4163:  "silverpeakpeer",
+	4164:  "silverpeakcomm",
+	4165:  "altcp",
+	4166:  "joost",
+	4167:  "ddgn",
+	4168:  "pslicser",
+	4169:  "iadt",
+	4170:  "d-cinema-csp",
+	4171:  "ml-svnet",
+	4172:  "pcoip",
+	4174:  "smcluster",
+	4175:  "bccp",
+	4176:  "tl-ipcproxy",
+	4177:  "wello",
+	4178:  "storman",
+	4179:  "MaxumSP",
+	4180:  "httpx",
+	4181:  "macbak",
+	4182:  "pcptcpservice",
+	4183:  "cyborgnet",
+	4184:  "universe-suite",
+	4185:  "wcpp",
+	4186:  "boxbackupstore",
+	4187:  "csc-proxy",
+	4188:  "vatata",
+	4189:  "pcep",
+	4190:  "sieve",
+	4192:  "azeti",
+	4193:  "pvxplusio",
+	4197:  "hctl",
+	4199:  "eims-admin",
+	4300:  "corelccam",
+	4301:  "d-data",
+	4302:  "d-data-control",
+	4303:  "srcp",
+	4304:  "owserver",
+	4305:  "batman",
+	4306:  "pinghgl",
+	4307:  "trueconf",
+	4308:  "compx-lockview",
+	4309:  "dserver",
+	4310:  "mirrtex",
+	4311:  "p6ssmc",
+	4312:  "pscl-mgt",
+	4313:  "perrla",
+	4314:  "choiceview-agt",
+	4316:  "choiceview-clt",
+	4320:  "fdt-rcatp",
+	4321:  "rwhois",
+	4322:  "trim-event",
+	4323:  "trim-ice",
+	4325:  "geognosisman",
+	4326:  "geognosis",
+	4327:  "jaxer-web",
+	4328:  "jaxer-manager",
+	4329:  "publiqare-sync",
+	4330:  "dey-sapi",
+	4331:  "ktickets-rest",
+	4333:  "ahsp",
+	4334:  "netconf-ch-ssh",
+	4335:  "netconf-ch-tls",
+	4336:  "restconf-ch-tls",
+	4340:  "gaia",
+	4341:  "lisp-data",
+	4342:  "lisp-cons",
+	4343:  "unicall",
+	4344:  "vinainstall",
+	4345:  "m4-network-as",
+	4346:  "elanlm",
+	4347:  "lansurveyor",
+	4348:  "itose",
+	4349:  "fsportmap",
+	4350:  "net-device",
+	4351:  "plcy-net-svcs",
+	4352:  "pjlink",
+	4353:  "f5-iquery",
+	4354:  "qsnet-trans",
+	4355:  "qsnet-workst",
+	4356:  "qsnet-assist",
+	4357:  "qsnet-cond",
+	4358:  "qsnet-nucl",
+	4359:  "omabcastltkm",
+	4360:  "matrix-vnet",
+	4368:  "wxbrief",
+	4369:  "epmd",
+	4370:  "elpro-tunnel",
+	4371:  "l2c-control",
+	4372:  "l2c-data",
+	4373:  "remctl",
+	4374:  "psi-ptt",
+	4375:  "tolteces",
+	4376:  "bip",
+	4377:  "cp-spxsvr",
+	4378:  "cp-spxdpy",
+	4379:  "ctdb",
+	4389:  "xandros-cms",
+	4390:  "wiegand",
+	4391:  "apwi-imserver",
+	4392:  "apwi-rxserver",
+	4393:  "apwi-rxspooler",
+	4395:  "omnivisionesx",
+	4396:  "fly",
+	4400:  "ds-srv",
+	4401:  "ds-srvr",
+	4402:  "ds-clnt",
+	4403:  "ds-user",
+	4404:  "ds-admin",
+	4405:  "ds-mail",
+	4406:  "ds-slp",
+	4407:  "nacagent",
+	4408:  "slscc",
+	4409:  "netcabinet-com",
+	4410:  "itwo-server",
+	4411:  "found",
+	4413:  "avi-nms",
+	4414:  "updog",
+	4415:  "brcd-vr-req",
+	4416:  "pjj-player",
+	4417:  "workflowdir",
+	4419:  "cbp",
+	4420:  "nvm-express",
+	4421:  "scaleft",
+	4422:  "tsepisp",
+	4423:  "thingkit",
+	4425:  "netrockey6",
+	4426:  "beacon-port-2",
+	4427:  "drizzle",
+	4428:  "omviserver",
+	4429:  "omviagent",
+	4430:  "rsqlserver",
+	4431:  "wspipe",
+	4432:  "l-acoustics",
+	4433:  "vop",
+	4442:  "saris",
+	4443:  "pharos",
+	4444:  "krb524",
+	4445:  "upnotifyp",
+	4446:  "n1-fwp",
+	4447:  "n1-rmgmt",
+	4448:  "asc-slmd",
+	4449:  "privatewire",
+	4450:  "camp",
+	4451:  "ctisystemmsg",
+	4452:  "ctiprogramload",
+	4453:  "nssalertmgr",
+	4454:  "nssagentmgr",
+	4455:  "prchat-user",
+	4456:  "prchat-server",
+	4457:  "prRegister",
+	4458:  "mcp",
+	4484:  "hpssmgmt",
+	4485:  "assyst-dr",
+	4486:  "icms",
+	4487:  "prex-tcp",
+	4488:  "awacs-ice",
+	4500:  "ipsec-nat-t",
+	4535:  "ehs",
+	4536:  "ehs-ssl",
+	4537:  "wssauthsvc",
+	4538:  "swx-gate",
+	4545:  "worldscores",
+	4546:  "sf-lm",
+	4547:  "lanner-lm",
+	4548:  "synchromesh",
+	4549:  "aegate",
+	4550:  "gds-adppiw-db",
+	4551:  "ieee-mih",
+	4552:  "menandmice-mon",
+	4553:  "icshostsvc",
+	4554:  "msfrs",
+	4555:  "rsip",
+	4556:  "dtn-bundle",
+	4559:  "hylafax",
+	4563:  "amahi-anywhere",
+	4566:  "kwtc",
+	4567:  "tram",
+	4568:  "bmc-reporting",
+	4569:  "iax",
+	4570:  "deploymentmap",
+	4573:  "cardifftec-back",
+	4590:  "rid",
+	4591:  "l3t-at-an",
+	4593:  "ipt-anri-anri",
+	4594:  "ias-session",
+	4595:  "ias-paging",
+	4596:  "ias-neighbor",
+	4597:  "a21-an-1xbs",
+	4598:  "a16-an-an",
+	4599:  "a17-an-an",
+	4600:  "piranha1",
+	4601:  "piranha2",
+	4602:  "mtsserver",
+	4603:  "menandmice-upg",
+	4604:  "irp",
+	4605:  "sixchat",
+	4658:  "playsta2-app",
+	4659:  "playsta2-lob",
+	4660:  "smaclmgr",
+	4661:  "kar2ouche",
+	4662:  "oms",
+	4663:  "noteit",
+	4664:  "ems",
+	4665:  "contclientms",
+	4666:  "eportcomm",
+	4667:  "mmacomm",
+	4668:  "mmaeds",
+	4669:  "eportcommdata",
+	4670:  "light",
+	4671:  "acter",
+	4672:  "rfa",
+	4673:  "cxws",
+	4674:  "appiq-mgmt",
+	4675:  "dhct-status",
+	4676:  "dhct-alerts",
+	4677:  "bcs",
+	4678:  "traversal",
+	4679:  "mgesupervision",
+	4680:  "mgemanagement",
+	4681:  "parliant",
+	4682:  "finisar",
+	4683:  "spike",
+	4684:  "rfid-rp1",
+	4685:  "autopac",
+	4686:  "msp-os",
+	4687:  "nst",
+	4688:  "mobile-p2p",
+	4689:  "altovacentral",
+	4690:  "prelude",
+	4691:  "mtn",
+	4692:  "conspiracy",
+	4700:  "netxms-agent",
+	4701:  "netxms-mgmt",
+	4702:  "netxms-sync",
+	4703:  "npqes-test",
+	4704:  "assuria-ins",
+	4711:  "trinity-dist",
+	4725:  "truckstar",
+	4727:  "fcis",
+	4728:  "capmux",
+	4730:  "gearman",
+	4731:  "remcap",
+	4733:  "resorcs",
+	4737:  "ipdr-sp",
+	4738:  "solera-lpn",
+	4739:  "ipfix",
+	4740:  "ipfixs",
+	4741:  "lumimgrd",
+	4742:  "sicct",
+	4743:  "openhpid",
+	4744:  "ifsp",
+	4745:  "fmp",
+	4749:  "profilemac",
+	4750:  "ssad",
+	4751:  "spocp",
+	4752:  "snap",
+	4753:  "simon",
+	4756:  "RDCenter",
+	4774:  "converge",
+	4784:  "bfd-multi-ctl",
+	4786:  "smart-install",
+	4787:  "sia-ctrl-plane",
+	4788:  "xmcp",
+	4800:  "iims",
+	4801:  "iwec",
+	4802:  "ilss",
+	4803:  "notateit",
+	4827:  "htcp",
+	4837:  "varadero-0",
+	4838:  "varadero-1",
+	4839:  "varadero-2",
+	4840:  "opcua-tcp",
+	4841:  "quosa",
+	4842:  "gw-asv",
+	4843:  "opcua-tls",
+	4844:  "gw-log",
+	4845:  "wcr-remlib",
+	4846:  "contamac-icm",
+	4847:  "wfc",
+	4848:  "appserv-http",
+	4849:  "appserv-https",
+	4850:  "sun-as-nodeagt",
+	4851:  "derby-repli",
+	4867:  "unify-debug",
+	4868:  "phrelay",
+	4869:  "phrelaydbg",
+	4870:  "cc-tracking",
+	4871:  "wired",
+	4876:  "tritium-can",
+	4877:  "lmcs",
+	4879:  "wsdl-event",
+	4880:  "hislip",
+	4883:  "wmlserver",
+	4884:  "hivestor",
+	4885:  "abbs",
+	4894:  "lyskom",
+	4899:  "radmin-port",
+	4900:  "hfcs",
+	4901:  "flr-agent",
+	4902:  "magiccontrol",
+	4912:  "lutap",
+	4913:  "lutcp",
+	4914:  "bones",
+	4915:  "frcs",
+	4940:  "eq-office-4940",
+	4941:  "eq-office-4941",
+	4942:  "eq-office-4942",
+	4949:  "munin",
+	4950:  "sybasesrvmon",
+	4951:  "pwgwims",
+	4952:  "sagxtsds",
+	4953:  "dbsyncarbiter",
+	4969:  "ccss-qmm",
+	4970:  "ccss-qsm",
+	4971:  "burp",
+	4984:  "webyast",
+	4985:  "gerhcs",
+	4986:  "mrip",
+	4987:  "smar-se-port1",
+	4988:  "smar-se-port2",
+	4989:  "parallel",
+	4990:  "busycal",
+	4991:  "vrt",
+	4999:  "hfcs-manager",
+	5000:  "commplex-main",
+	5001:  "commplex-link",
+	5002:  "rfe",
+	5003:  "fmpro-internal",
+	5004:  "avt-profile-1",
+	5005:  "avt-profile-2",
+	5006:  "wsm-server",
+	5007:  "wsm-server-ssl",
+	5008:  "synapsis-edge",
+	5009:  "winfs",
+	5010:  "telelpathstart",
+	5011:  "telelpathattack",
+	5012:  "nsp",
+	5013:  "fmpro-v6",
+	5015:  "fmwp",
+	5020:  "zenginkyo-1",
+	5021:  "zenginkyo-2",
+	5022:  "mice",
+	5023:  "htuilsrv",
+	5024:  "scpi-telnet",
+	5025:  "scpi-raw",
+	5026:  "strexec-d",
+	5027:  "strexec-s",
+	5028:  "qvr",
+	5029:  "infobright",
+	5030:  "surfpass",
+	5032:  "signacert-agent",
+	5033:  "jtnetd-server",
+	5034:  "jtnetd-status",
+	5042:  "asnaacceler8db",
+	5043:  "swxadmin",
+	5044:  "lxi-evntsvc",
+	5045:  "osp",
+	5048:  "texai",
+	5049:  "ivocalize",
+	5050:  "mmcc",
+	5051:  "ita-agent",
+	5052:  "ita-manager",
+	5053:  "rlm",
+	5054:  "rlm-admin",
+	5055:  "unot",
+	5056:  "intecom-ps1",
+	5057:  "intecom-ps2",
+	5059:  "sds",
+	5060:  "sip",
+	5061:  "sips",
+	5062:  "na-localise",
+	5063:  "csrpc",
+	5064:  "ca-1",
+	5065:  "ca-2",
+	5066:  "stanag-5066",
+	5067:  "authentx",
+	5068:  "bitforestsrv",
+	5069:  "i-net-2000-npr",
+	5070:  "vtsas",
+	5071:  "powerschool",
+	5072:  "ayiya",
+	5073:  "tag-pm",
+	5074:  "alesquery",
+	5075:  "pvaccess",
+	5080:  "onscreen",
+	5081:  "sdl-ets",
+	5082:  "qcp",
+	5083:  "qfp",
+	5084:  "llrp",
+	5085:  "encrypted-llrp",
+	5086:  "aprigo-cs",
+	5087:  "biotic",
+	5093:  "sentinel-lm",
+	5094:  "hart-ip",
+	5099:  "sentlm-srv2srv",
+	5100:  "socalia",
+	5101:  "talarian-tcp",
+	5102:  "oms-nonsecure",
+	5103:  "actifio-c2c",
+	5106:  "actifioudsagent",
+	5107:  "actifioreplic",
+	5111:  "taep-as-svc",
+	5112:  "pm-cmdsvr",
+	5114:  "ev-services",
+	5115:  "autobuild",
+	5117:  "gradecam",
+	5120:  "barracuda-bbs",
+	5133:  "nbt-pc",
+	5134:  "ppactivation",
+	5135:  "erp-scale",
+	5137:  "ctsd",
+	5145:  "rmonitor-secure",
+	5146:  "social-alarm",
+	5150:  "atmp",
+	5151:  "esri-sde",
+	5152:  "sde-discovery",
+	5153:  "toruxserver",
+	5154:  "bzflag",
+	5155:  "asctrl-agent",
+	5156:  "rugameonline",
+	5157:  "mediat",
+	5161:  "snmpssh",
+	5162:  "snmpssh-trap",
+	5163:  "sbackup",
+	5164:  "vpa",
+	5165:  "ife-icorp",
+	5166:  "winpcs",
+	5167:  "scte104",
+	5168:  "scte30",
+	5172:  "pcoip-mgmt",
+	5190:  "aol",
+	5191:  "aol-1",
+	5192:  "aol-2",
+	5193:  "aol-3",
+	5194:  "cpscomm",
+	5195:  "ampl-lic",
+	5196:  "ampl-tableproxy",
+	5197:  "tunstall-lwp",
+	5200:  "targus-getdata",
+	5201:  "targus-getdata1",
+	5202:  "targus-getdata2",
+	5203:  "targus-getdata3",
+	5209:  "nomad",
+	5215:  "noteza",
+	5221:  "3exmp",
+	5222:  "xmpp-client",
+	5223:  "hpvirtgrp",
+	5224:  "hpvirtctrl",
+	5225:  "hp-server",
+	5226:  "hp-status",
+	5227:  "perfd",
+	5228:  "hpvroom",
+	5229:  "jaxflow",
+	5230:  "jaxflow-data",
+	5231:  "crusecontrol",
+	5232:  "csedaemon",
+	5233:  "enfs",
+	5234:  "eenet",
+	5235:  "galaxy-network",
+	5236:  "padl2sim",
+	5237:  "mnet-discovery",
+	5245:  "downtools",
+	5248:  "caacws",
+	5249:  "caaclang2",
+	5250:  "soagateway",
+	5251:  "caevms",
+	5252:  "movaz-ssc",
+	5253:  "kpdp",
+	5254:  "logcabin",
+	5264:  "3com-njack-1",
+	5265:  "3com-njack-2",
+	5269:  "xmpp-server",
+	5270:  "cartographerxmp",
+	5271:  "cuelink",
+	5272:  "pk",
+	5280:  "xmpp-bosh",
+	5281:  "undo-lm",
+	5282:  "transmit-port",
+	5298:  "presence",
+	5299:  "nlg-data",
+	5300:  "hacl-hb",
+	5301:  "hacl-gs",
+	5302:  "hacl-cfg",
+	5303:  "hacl-probe",
+	5304:  "hacl-local",
+	5305:  "hacl-test",
+	5306:  "sun-mc-grp",
+	5307:  "sco-aip",
+	5308:  "cfengine",
+	5309:  "jprinter",
+	5310:  "outlaws",
+	5312:  "permabit-cs",
+	5313:  "rrdp",
+	5314:  "opalis-rbt-ipc",
+	5315:  "hacl-poll",
+	5316:  "hpbladems",
+	5317:  "hpdevms",
+	5318:  "pkix-cmc",
+	5320:  "bsfserver-zn",
+	5321:  "bsfsvr-zn-ssl",
+	5343:  "kfserver",
+	5344:  "xkotodrcp",
+	5349:  "stuns",
+	5352:  "dns-llq",
+	5353:  "mdns",
+	5354:  "mdnsresponder",
+	5355:  "llmnr",
+	5356:  "ms-smlbiz",
+	5357:  "wsdapi",
+	5358:  "wsdapi-s",
+	5359:  "ms-alerter",
+	5360:  "ms-sideshow",
+	5361:  "ms-s-sideshow",
+	5362:  "serverwsd2",
+	5363:  "net-projection",
+	5397:  "stresstester",
+	5398:  "elektron-admin",
+	5399:  "securitychase",
+	5400:  "excerpt",
+	5401:  "excerpts",
+	5402:  "mftp",
+	5403:  "hpoms-ci-lstn",
+	5404:  "hpoms-dps-lstn",
+	5405:  "netsupport",
+	5406:  "systemics-sox",
+	5407:  "foresyte-clear",
+	5408:  "foresyte-sec",
+	5409:  "salient-dtasrv",
+	5410:  "salient-usrmgr",
+	5411:  "actnet",
+	5412:  "continuus",
+	5413:  "wwiotalk",
+	5414:  "statusd",
+	5415:  "ns-server",
+	5416:  "sns-gateway",
+	5417:  "sns-agent",
+	5418:  "mcntp",
+	5419:  "dj-ice",
+	5420:  "cylink-c",
+	5421:  "netsupport2",
+	5422:  "salient-mux",
+	5423:  "virtualuser",
+	5424:  "beyond-remote",
+	5425:  "br-channel",
+	5426:  "devbasic",
+	5427:  "sco-peer-tta",
+	5428:  "telaconsole",
+	5429:  "base",
+	5430:  "radec-corp",
+	5431:  "park-agent",
+	5432:  "postgresql",
+	5433:  "pyrrho",
+	5434:  "sgi-arrayd",
+	5435:  "sceanics",
+	5443:  "spss",
+	5445:  "smbdirect",
+	5450:  "tiepie",
+	5453:  "surebox",
+	5454:  "apc-5454",
+	5455:  "apc-5455",
+	5456:  "apc-5456",
+	5461:  "silkmeter",
+	5462:  "ttl-publisher",
+	5463:  "ttlpriceproxy",
+	5464:  "quailnet",
+	5465:  "netops-broker",
+	5470:  "apsolab-col",
+	5471:  "apsolab-cols",
+	5472:  "apsolab-tag",
+	5473:  "apsolab-tags",
+	5475:  "apsolab-data",
+	5500:  "fcp-addr-srvr1",
+	5501:  "fcp-addr-srvr2",
+	5502:  "fcp-srvr-inst1",
+	5503:  "fcp-srvr-inst2",
+	5504:  "fcp-cics-gw1",
+	5505:  "checkoutdb",
+	5506:  "amc",
+	5507:  "psl-management",
+	5550:  "cbus",
+	5553:  "sgi-eventmond",
+	5554:  "sgi-esphttp",
+	5555:  "personal-agent",
+	5556:  "freeciv",
+	5557:  "farenet",
+	5565:  "hpe-dp-bura",
+	5566:  "westec-connect",
+	5567:  "dof-dps-mc-sec",
+	5568:  "sdt",
+	5569:  "rdmnet-ctrl",
+	5573:  "sdmmp",
+	5574:  "lsi-bobcat",
+	5575:  "ora-oap",
+	5579:  "fdtracks",
+	5580:  "tmosms0",
+	5581:  "tmosms1",
+	5582:  "fac-restore",
+	5583:  "tmo-icon-sync",
+	5584:  "bis-web",
+	5585:  "bis-sync",
+	5586:  "att-mt-sms",
+	5597:  "ininmessaging",
+	5598:  "mctfeed",
+	5599:  "esinstall",
+	5600:  "esmmanager",
+	5601:  "esmagent",
+	5602:  "a1-msc",
+	5603:  "a1-bs",
+	5604:  "a3-sdunode",
+	5605:  "a4-sdunode",
+	5618:  "efr",
+	5627:  "ninaf",
+	5628:  "htrust",
+	5629:  "symantec-sfdb",
+	5630:  "precise-comm",
+	5631:  "pcanywheredata",
+	5632:  "pcanywherestat",
+	5633:  "beorl",
+	5634:  "xprtld",
+	5635:  "sfmsso",
+	5636:  "sfm-db-server",
+	5637:  "cssc",
+	5638:  "flcrs",
+	5639:  "ics",
+	5646:  "vfmobile",
+	5666:  "nrpe",
+	5670:  "filemq",
+	5671:  "amqps",
+	5672:  "amqp",
+	5673:  "jms",
+	5674:  "hyperscsi-port",
+	5675:  "v5ua",
+	5676:  "raadmin",
+	5677:  "questdb2-lnchr",
+	5678:  "rrac",
+	5679:  "dccm",
+	5680:  "auriga-router",
+	5681:  "ncxcp",
+	5688:  "ggz",
+	5689:  "qmvideo",
+	5693:  "rbsystem",
+	5696:  "kmip",
+	5700:  "supportassist",
+	5705:  "storageos",
+	5713:  "proshareaudio",
+	5714:  "prosharevideo",
+	5715:  "prosharedata",
+	5716:  "prosharerequest",
+	5717:  "prosharenotify",
+	5718:  "dpm",
+	5719:  "dpm-agent",
+	5720:  "ms-licensing",
+	5721:  "dtpt",
+	5722:  "msdfsr",
+	5723:  "omhs",
+	5724:  "omsdk",
+	5725:  "ms-ilm",
+	5726:  "ms-ilm-sts",
+	5727:  "asgenf",
+	5728:  "io-dist-data",
+	5729:  "openmail",
+	5730:  "unieng",
+	5741:  "ida-discover1",
+	5742:  "ida-discover2",
+	5743:  "watchdoc-pod",
+	5744:  "watchdoc",
+	5745:  "fcopy-server",
+	5746:  "fcopys-server",
+	5747:  "tunatic",
+	5748:  "tunalyzer",
+	5750:  "rscd",
+	5755:  "openmailg",
+	5757:  "x500ms",
+	5766:  "openmailns",
+	5767:  "s-openmail",
+	5768:  "openmailpxy",
+	5769:  "spramsca",
+	5770:  "spramsd",
+	5771:  "netagent",
+	5777:  "dali-port",
+	5780:  "vts-rpc",
+	5781:  "3par-evts",
+	5782:  "3par-mgmt",
+	5783:  "3par-mgmt-ssl",
+	5785:  "3par-rcopy",
+	5793:  "xtreamx",
+	5813:  "icmpd",
+	5814:  "spt-automation",
+	5841:  "shiprush-d-ch",
+	5842:  "reversion",
+	5859:  "wherehoo",
+	5863:  "ppsuitemsg",
+	5868:  "diameters",
+	5883:  "jute",
+	5900:  "rfb",
+	5910:  "cm",
+	5911:  "cpdlc",
+	5912:  "fis",
+	5913:  "ads-c",
+	5963:  "indy",
+	5968:  "mppolicy-v5",
+	5969:  "mppolicy-mgr",
+	5984:  "couchdb",
+	5985:  "wsman",
+	5986:  "wsmans",
+	5987:  "wbem-rmi",
+	5988:  "wbem-http",
+	5989:  "wbem-https",
+	5990:  "wbem-exp-https",
+	5991:  "nuxsl",
+	5992:  "consul-insight",
+	5993:  "cim-rs",
+	5999:  "cvsup",
+	6064:  "ndl-ahp-svc",
+	6065:  "winpharaoh",
+	6066:  "ewctsp",
+	6068:  "gsmp-ancp",
+	6069:  "trip",
+	6070:  "messageasap",
+	6071:  "ssdtp",
+	6072:  "diagnose-proc",
+	6073:  "directplay8",
+	6074:  "max",
+	6075:  "dpm-acm",
+	6076:  "msft-dpm-cert",
+	6077:  "iconstructsrv",
+	6084:  "reload-config",
+	6085:  "konspire2b",
+	6086:  "pdtp",
+	6087:  "ldss",
+	6088:  "doglms",
+	6099:  "raxa-mgmt",
+	6100:  "synchronet-db",
+	6101:  "synchronet-rtc",
+	6102:  "synchronet-upd",
+	6103:  "rets",
+	6104:  "dbdb",
+	6105:  "primaserver",
+	6106:  "mpsserver",
+	6107:  "etc-control",
+	6108:  "sercomm-scadmin",
+	6109:  "globecast-id",
+	6110:  "softcm",
+	6111:  "spc",
+	6112:  "dtspcd",
+	6113:  "dayliteserver",
+	6114:  "wrspice",
+	6115:  "xic",
+	6116:  "xtlserv",
+	6117:  "daylitetouch",
+	6121:  "spdy",
+	6122:  "bex-webadmin",
+	6123:  "backup-express",
+	6124:  "pnbs",
+	6130:  "damewaremobgtwy",
+	6133:  "nbt-wol",
+	6140:  "pulsonixnls",
+	6141:  "meta-corp",
+	6142:  "aspentec-lm",
+	6143:  "watershed-lm",
+	6144:  "statsci1-lm",
+	6145:  "statsci2-lm",
+	6146:  "lonewolf-lm",
+	6147:  "montage-lm",
+	6148:  "ricardo-lm",
+	6149:  "tal-pod",
+	6159:  "efb-aci",
+	6160:  "ecmp",
+	6161:  "patrol-ism",
+	6162:  "patrol-coll",
+	6163:  "pscribe",
+	6200:  "lm-x",
+	6209:  "qmtps",
+	6222:  "radmind",
+	6241:  "jeol-nsdtp-1",
+	6242:  "jeol-nsdtp-2",
+	6243:  "jeol-nsdtp-3",
+	6244:  "jeol-nsdtp-4",
+	6251:  "tl1-raw-ssl",
+	6252:  "tl1-ssh",
+	6253:  "crip",
+	6267:  "gld",
+	6268:  "grid",
+	6269:  "grid-alt",
+	6300:  "bmc-grx",
+	6301:  "bmc-ctd-ldap",
+	6306:  "ufmp",
+	6315:  "scup",
+	6316:  "abb-escp",
+	6317:  "nav-data-cmd",
+	6320:  "repsvc",
+	6321:  "emp-server1",
+	6322:  "emp-server2",
+	6324:  "hrd-ncs",
+	6325:  "dt-mgmtsvc",
+	6326:  "dt-vra",
+	6343:  "sflow",
+	6344:  "streletz",
+	6346:  "gnutella-svc",
+	6347:  "gnutella-rtr",
+	6350:  "adap",
+	6355:  "pmcs",
+	6360:  "metaedit-mu",
+	6370:  "metaedit-se",
+	6379:  "redis",
+	6382:  "metatude-mds",
+	6389:  "clariion-evr01",
+	6390:  "metaedit-ws",
+	6417:  "faxcomservice",
+	6418:  "syserverremote",
+	6419:  "svdrp",
+	6420:  "nim-vdrshell",
+	6421:  "nim-wan",
+	6432:  "pgbouncer",
+	6442:  "tarp",
+	6443:  "sun-sr-https",
+	6444:  "sge-qmaster",
+	6445:  "sge-execd",
+	6446:  "mysql-proxy",
+	6455:  "skip-cert-recv",
+	6456:  "skip-cert-send",
+	6464:  "ieee11073-20701",
+	6471:  "lvision-lm",
+	6480:  "sun-sr-http",
+	6481:  "servicetags",
+	6482:  "ldoms-mgmt",
+	6483:  "SunVTS-RMI",
+	6484:  "sun-sr-jms",
+	6485:  "sun-sr-iiop",
+	6486:  "sun-sr-iiops",
+	6487:  "sun-sr-iiop-aut",
+	6488:  "sun-sr-jmx",
+	6489:  "sun-sr-admin",
+	6500:  "boks",
+	6501:  "boks-servc",
+	6502:  "boks-servm",
+	6503:  "boks-clntd",
+	6505:  "badm-priv",
+	6506:  "badm-pub",
+	6507:  "bdir-priv",
+	6508:  "bdir-pub",
+	6509:  "mgcs-mfp-port",
+	6510:  "mcer-port",
+	6513:  "netconf-tls",
+	6514:  "syslog-tls",
+	6515:  "elipse-rec",
+	6543:  "lds-distrib",
+	6544:  "lds-dump",
+	6547:  "apc-6547",
+	6548:  "apc-6548",
+	6549:  "apc-6549",
+	6550:  "fg-sysupdate",
+	6551:  "sum",
+	6558:  "xdsxdm",
+	6566:  "sane-port",
+	6568:  "canit-store",
+	6579:  "affiliate",
+	6580:  "parsec-master",
+	6581:  "parsec-peer",
+	6582:  "parsec-game",
+	6583:  "joaJewelSuite",
+	6600:  "mshvlm",
+	6601:  "mstmg-sstp",
+	6602:  "wsscomfrmwk",
+	6619:  "odette-ftps",
+	6620:  "kftp-data",
+	6621:  "kftp",
+	6622:  "mcftp",
+	6623:  "ktelnet",
+	6624:  "datascaler-db",
+	6625:  "datascaler-ctl",
+	6626:  "wago-service",
+	6627:  "nexgen",
+	6628:  "afesc-mc",
+	6629:  "nexgen-aux",
+	6632:  "mxodbc-connect",
+	6640:  "ovsdb",
+	6653:  "openflow",
+	6655:  "pcs-sf-ui-man",
+	6656:  "emgmsg",
+	6670:  "vocaltec-gold",
+	6671:  "p4p-portal",
+	6672:  "vision-server",
+	6673:  "vision-elmd",
+	6678:  "vfbp",
+	6679:  "osaut",
+	6687:  "clever-ctrace",
+	6688:  "clever-tcpip",
+	6689:  "tsa",
+	6690:  "cleverdetect",
+	6697:  "ircs-u",
+	6701:  "kti-icad-srvr",
+	6702:  "e-design-net",
+	6703:  "e-design-web",
+	6714:  "ibprotocol",
+	6715:  "fibotrader-com",
+	6716:  "princity-agent",
+	6767:  "bmc-perf-agent",
+	6768:  "bmc-perf-mgrd",
+	6769:  "adi-gxp-srvprt",
+	6770:  "plysrv-http",
+	6771:  "plysrv-https",
+	6777:  "ntz-tracker",
+	6778:  "ntz-p2p-storage",
+	6785:  "dgpf-exchg",
+	6786:  "smc-jmx",
+	6787:  "smc-admin",
+	6788:  "smc-http",
+	6789:  "radg",
+	6790:  "hnmp",
+	6791:  "hnm",
+	6801:  "acnet",
+	6817:  "pentbox-sim",
+	6831:  "ambit-lm",
+	6841:  "netmo-default",
+	6842:  "netmo-http",
+	6850:  "iccrushmore",
+	6868:  "acctopus-cc",
+	6888:  "muse",
+	6900:  "rtimeviewer",
+	6901:  "jetstream",
+	6935:  "ethoscan",
+	6936:  "xsmsvc",
+	6946:  "bioserver",
+	6951:  "otlp",
+	6961:  "jmact3",
+	6962:  "jmevt2",
+	6963:  "swismgr1",
+	6964:  "swismgr2",
+	6965:  "swistrap",
+	6966:  "swispol",
+	6969:  "acmsoda",
+	6970:  "conductor",
+	6997:  "MobilitySrv",
+	6998:  "iatp-highpri",
+	6999:  "iatp-normalpri",
+	7000:  "afs3-fileserver",
+	7001:  "afs3-callback",
+	7002:  "afs3-prserver",
+	7003:  "afs3-vlserver",
+	7004:  "afs3-kaserver",
+	7005:  "afs3-volser",
+	7006:  "afs3-errors",
+	7007:  "afs3-bos",
+	7008:  "afs3-update",
+	7009:  "afs3-rmtsys",
+	7010:  "ups-onlinet",
+	7011:  "talon-disc",
+	7012:  "talon-engine",
+	7013:  "microtalon-dis",
+	7014:  "microtalon-com",
+	7015:  "talon-webserver",
+	7016:  "spg",
+	7017:  "grasp",
+	7018:  "fisa-svc",
+	7019:  "doceri-ctl",
+	7020:  "dpserve",
+	7021:  "dpserveadmin",
+	7022:  "ctdp",
+	7023:  "ct2nmcs",
+	7024:  "vmsvc",
+	7025:  "vmsvc-2",
+	7030:  "op-probe",
+	7031:  "iposplanet",
+	7070:  "arcp",
+	7071:  "iwg1",
+	7073:  "martalk",
+	7080:  "empowerid",
+	7099:  "lazy-ptop",
+	7100:  "font-service",
+	7101:  "elcn",
+	7117:  "rothaga",
+	7121:  "virprot-lm",
+	7128:  "scenidm",
+	7129:  "scenccs",
+	7161:  "cabsm-comm",
+	7162:  "caistoragemgr",
+	7163:  "cacsambroker",
+	7164:  "fsr",
+	7165:  "doc-server",
+	7166:  "aruba-server",
+	7167:  "casrmagent",
+	7168:  "cnckadserver",
+	7169:  "ccag-pib",
+	7170:  "nsrp",
+	7171:  "drm-production",
+	7172:  "metalbend",
+	7173:  "zsecure",
+	7174:  "clutild",
+	7200:  "fodms",
+	7201:  "dlip",
+	7202:  "pon-ictp",
+	7215:  "PS-Server",
+	7216:  "PS-Capture-Pro",
+	7227:  "ramp",
+	7228:  "citrixupp",
+	7229:  "citrixuppg",
+	7236:  "display",
+	7237:  "pads",
+	7244:  "frc-hicp",
+	7262:  "cnap",
+	7272:  "watchme-7272",
+	7273:  "oma-rlp",
+	7274:  "oma-rlp-s",
+	7275:  "oma-ulp",
+	7276:  "oma-ilp",
+	7277:  "oma-ilp-s",
+	7278:  "oma-dcdocbs",
+	7279:  "ctxlic",
+	7280:  "itactionserver1",
+	7281:  "itactionserver2",
+	7282:  "mzca-action",
+	7283:  "genstat",
+	7365:  "lcm-server",
+	7391:  "mindfilesys",
+	7392:  "mrssrendezvous",
+	7393:  "nfoldman",
+	7394:  "fse",
+	7395:  "winqedit",
+	7397:  "hexarc",
+	7400:  "rtps-discovery",
+	7401:  "rtps-dd-ut",
+	7402:  "rtps-dd-mt",
+	7410:  "ionixnetmon",
+	7411:  "daqstream",
+	7421:  "mtportmon",
+	7426:  "pmdmgr",
+	7427:  "oveadmgr",
+	7428:  "ovladmgr",
+	7429:  "opi-sock",
+	7430:  "xmpv7",
+	7431:  "pmd",
+	7437:  "faximum",
+	7443:  "oracleas-https",
+	7471:  "sttunnel",
+	7473:  "rise",
+	7474:  "neo4j",
+	7478:  "openit",
+	7491:  "telops-lmd",
+	7500:  "silhouette",
+	7501:  "ovbus",
+	7508:  "adcp",
+	7509:  "acplt",
+	7510:  "ovhpas",
+	7511:  "pafec-lm",
+	7542:  "saratoga",
+	7543:  "atul",
+	7544:  "nta-ds",
+	7545:  "nta-us",
+	7546:  "cfs",
+	7547:  "cwmp",
+	7548:  "tidp",
+	7549:  "nls-tl",
+	7551:  "controlone-con",
+	7560:  "sncp",
+	7563:  "cfw",
+	7566:  "vsi-omega",
+	7569:  "dell-eql-asm",
+	7570:  "aries-kfinder",
+	7574:  "coherence",
+	7588:  "sun-lm",
+	7606:  "mipi-debug",
+	7624:  "indi",
+	7626:  "simco",
+	7627:  "soap-http",
+	7628:  "zen-pawn",
+	7629:  "xdas",
+	7630:  "hawk",
+	7631:  "tesla-sys-msg",
+	7633:  "pmdfmgt",
+	7648:  "cuseeme",
+	7672:  "imqstomp",
+	7673:  "imqstomps",
+	7674:  "imqtunnels",
+	7675:  "imqtunnel",
+	7676:  "imqbrokerd",
+	7677:  "sun-user-https",
+	7680:  "pando-pub",
+	7683:  "dmt",
+	7687:  "bolt",
+	7689:  "collaber",
+	7697:  "klio",
+	7700:  "em7-secom",
+	7707:  "sync-em7",
+	7708:  "scinet",
+	7720:  "medimageportal",
+	7724:  "nsdeepfreezectl",
+	7725:  "nitrogen",
+	7726:  "freezexservice",
+	7727:  "trident-data",
+	7728:  "osvr",
+	7734:  "smip",
+	7738:  "aiagent",
+	7741:  "scriptview",
+	7742:  "msss",
+	7743:  "sstp-1",
+	7744:  "raqmon-pdu",
+	7747:  "prgp",
+	7775:  "inetfs",
+	7777:  "cbt",
+	7778:  "interwise",
+	7779:  "vstat",
+	7781:  "accu-lmgr",
+	7786:  "minivend",
+	7787:  "popup-reminders",
+	7789:  "office-tools",
+	7794:  "q3ade",
+	7797:  "pnet-conn",
+	7798:  "pnet-enc",
+	7799:  "altbsdp",
+	7800:  "asr",
+	7801:  "ssp-client",
+	7810:  "rbt-wanopt",
+	7845:  "apc-7845",
+	7846:  "apc-7846",
+	7847:  "csoauth",
+	7869:  "mobileanalyzer",
+	7870:  "rbt-smc",
+	7871:  "mdm",
+	7878:  "owms",
+	7880:  "pss",
+	7887:  "ubroker",
+	7900:  "mevent",
+	7901:  "tnos-sp",
+	7902:  "tnos-dp",
+	7903:  "tnos-dps",
+	7913:  "qo-secure",
+	7932:  "t2-drm",
+	7933:  "t2-brm",
+	7962:  "generalsync",
+	7967:  "supercell",
+	7979:  "micromuse-ncps",
+	7980:  "quest-vista",
+	7981:  "sossd-collect",
+	7982:  "sossd-agent",
+	7997:  "pushns",
+	7999:  "irdmi2",
+	8000:  "irdmi",
+	8001:  "vcom-tunnel",
+	8002:  "teradataordbms",
+	8003:  "mcreport",
+	8005:  "mxi",
+	8006:  "wpl-analytics",
+	8007:  "warppipe",
+	8008:  "http-alt",
+	8019:  "qbdb",
+	8020:  "intu-ec-svcdisc",
+	8021:  "intu-ec-client",
+	8022:  "oa-system",
+	8025:  "ca-audit-da",
+	8026:  "ca-audit-ds",
+	8032:  "pro-ed",
+	8033:  "mindprint",
+	8034:  "vantronix-mgmt",
+	8040:  "ampify",
+	8041:  "enguity-xccetp",
+	8042:  "fs-agent",
+	8043:  "fs-server",
+	8044:  "fs-mgmt",
+	8051:  "rocrail",
+	8052:  "senomix01",
+	8053:  "senomix02",
+	8054:  "senomix03",
+	8055:  "senomix04",
+	8056:  "senomix05",
+	8057:  "senomix06",
+	8058:  "senomix07",
+	8059:  "senomix08",
+	8066:  "toad-bi-appsrvr",
+	8067:  "infi-async",
+	8070:  "ucs-isc",
+	8074:  "gadugadu",
+	8077:  "mles",
+	8080:  "http-alt",
+	8081:  "sunproxyadmin",
+	8082:  "us-cli",
+	8083:  "us-srv",
+	8086:  "d-s-n",
+	8087:  "simplifymedia",
+	8088:  "radan-http",
+	8090:  "opsmessaging",
+	8091:  "jamlink",
+	8097:  "sac",
+	8100:  "xprint-server",
+	8101:  "ldoms-migr",
+	8102:  "kz-migr",
+	8115:  "mtl8000-matrix",
+	8116:  "cp-cluster",
+	8117:  "purityrpc",
+	8118:  "privoxy",
+	8121:  "apollo-data",
+	8122:  "apollo-admin",
+	8128:  "paycash-online",
+	8129:  "paycash-wbp",
+	8130:  "indigo-vrmi",
+	8131:  "indigo-vbcp",
+	8132:  "dbabble",
+	8140:  "puppet",
+	8148:  "isdd",
+	8153:  "quantastor",
+	8160:  "patrol",
+	8161:  "patrol-snmp",
+	8162:  "lpar2rrd",
+	8181:  "intermapper",
+	8182:  "vmware-fdm",
+	8183:  "proremote",
+	8184:  "itach",
+	8190:  "gcp-rphy",
+	8191:  "limnerpressure",
+	8192:  "spytechphone",
+	8194:  "blp1",
+	8195:  "blp2",
+	8199:  "vvr-data",
+	8200:  "trivnet1",
+	8201:  "trivnet2",
+	8204:  "lm-perfworks",
+	8205:  "lm-instmgr",
+	8206:  "lm-dta",
+	8207:  "lm-sserver",
+	8208:  "lm-webwatcher",
+	8230:  "rexecj",
+	8243:  "synapse-nhttps",
+	8270:  "robot-remote",
+	8276:  "pando-sec",
+	8280:  "synapse-nhttp",
+	8282:  "libelle",
+	8292:  "blp3",
+	8293:  "hiperscan-id",
+	8294:  "blp4",
+	8300:  "tmi",
+	8301:  "amberon",
+	8313:  "hub-open-net",
+	8320:  "tnp-discover",
+	8321:  "tnp",
+	8322:  "garmin-marine",
+	8351:  "server-find",
+	8376:  "cruise-enum",
+	8377:  "cruise-swroute",
+	8378:  "cruise-config",
+	8379:  "cruise-diags",
+	8380:  "cruise-update",
+	8383:  "m2mservices",
+	8400:  "cvd",
+	8401:  "sabarsd",
+	8402:  "abarsd",
+	8403:  "admind",
+	8404:  "svcloud",
+	8405:  "svbackup",
+	8415:  "dlpx-sp",
+	8416:  "espeech",
+	8417:  "espeech-rtp",
+	8423:  "aritts",
+	8442:  "cybro-a-bus",
+	8443:  "pcsync-https",
+	8444:  "pcsync-http",
+	8445:  "copy",
+	8450:  "npmp",
+	8457:  "nexentamv",
+	8470:  "cisco-avp",
+	8471:  "pim-port",
+	8472:  "otv",
+	8473:  "vp2p",
+	8474:  "noteshare",
+	8500:  "fmtp",
+	8501:  "cmtp-mgt",
+	8502:  "ftnmtp",
+	8554:  "rtsp-alt",
+	8555:  "d-fence",
+	8567:  "dof-tunnel",
+	8600:  "asterix",
+	8610:  "canon-mfnp",
+	8611:  "canon-bjnp1",
+	8612:  "canon-bjnp2",
+	8613:  "canon-bjnp3",
+	8614:  "canon-bjnp4",
+	8615:  "imink",
+	8665:  "monetra",
+	8666:  "monetra-admin",
+	8675:  "msi-cps-rm",
+	8686:  "sun-as-jmxrmi",
+	8688:  "openremote-ctrl",
+	8699:  "vnyx",
+	8711:  "nvc",
+	8733:  "ibus",
+	8750:  "dey-keyneg",
+	8763:  "mc-appserver",
+	8764:  "openqueue",
+	8765:  "ultraseek-http",
+	8766:  "amcs",
+	8770:  "dpap",
+	8778:  "uec",
+	8786:  "msgclnt",
+	8787:  "msgsrvr",
+	8793:  "acd-pm",
+	8800:  "sunwebadmin",
+	8804:  "truecm",
+	8873:  "dxspider",
+	8880:  "cddbp-alt",
+	8881:  "galaxy4d",
+	8883:  "secure-mqtt",
+	8888:  "ddi-tcp-1",
+	8889:  "ddi-tcp-2",
+	8890:  "ddi-tcp-3",
+	8891:  "ddi-tcp-4",
+	8892:  "ddi-tcp-5",
+	8893:  "ddi-tcp-6",
+	8894:  "ddi-tcp-7",
+	8899:  "ospf-lite",
+	8900:  "jmb-cds1",
+	8901:  "jmb-cds2",
+	8910:  "manyone-http",
+	8911:  "manyone-xml",
+	8912:  "wcbackup",
+	8913:  "dragonfly",
+	8937:  "twds",
+	8953:  "ub-dns-control",
+	8954:  "cumulus-admin",
+	8980:  "nod-provider",
+	8989:  "sunwebadmins",
+	8990:  "http-wmap",
+	8991:  "https-wmap",
+	8997:  "oracle-ms-ens",
+	8998:  "canto-roboflow",
+	8999:  "bctp",
+	9000:  "cslistener",
+	9001:  "etlservicemgr",
+	9002:  "dynamid",
+	9005:  "golem",
+	9008:  "ogs-server",
+	9009:  "pichat",
+	9010:  "sdr",
+	9020:  "tambora",
+	9021:  "panagolin-ident",
+	9022:  "paragent",
+	9023:  "swa-1",
+	9024:  "swa-2",
+	9025:  "swa-3",
+	9026:  "swa-4",
+	9050:  "versiera",
+	9051:  "fio-cmgmt",
+	9060:  "CardWeb-IO",
+	9080:  "glrpc",
+	9083:  "emc-pp-mgmtsvc",
+	9084:  "aurora",
+	9085:  "ibm-rsyscon",
+	9086:  "net2display",
+	9087:  "classic",
+	9088:  "sqlexec",
+	9089:  "sqlexec-ssl",
+	9090:  "websm",
+	9091:  "xmltec-xmlmail",
+	9092:  "XmlIpcRegSvc",
+	9093:  "copycat",
+	9100:  "hp-pdl-datastr",
+	9101:  "bacula-dir",
+	9102:  "bacula-fd",
+	9103:  "bacula-sd",
+	9104:  "peerwire",
+	9105:  "xadmin",
+	9106:  "astergate",
+	9107:  "astergatefax",
+	9119:  "mxit",
+	9122:  "grcmp",
+	9123:  "grcp",
+	9131:  "dddp",
+	9160:  "apani1",
+	9161:  "apani2",
+	9162:  "apani3",
+	9163:  "apani4",
+	9164:  "apani5",
+	9191:  "sun-as-jpda",
+	9200:  "wap-wsp",
+	9201:  "wap-wsp-wtp",
+	9202:  "wap-wsp-s",
+	9203:  "wap-wsp-wtp-s",
+	9204:  "wap-vcard",
+	9205:  "wap-vcal",
+	9206:  "wap-vcard-s",
+	9207:  "wap-vcal-s",
+	9208:  "rjcdb-vcards",
+	9209:  "almobile-system",
+	9210:  "oma-mlp",
+	9211:  "oma-mlp-s",
+	9212:  "serverviewdbms",
+	9213:  "serverstart",
+	9214:  "ipdcesgbs",
+	9215:  "insis",
+	9216:  "acme",
+	9217:  "fsc-port",
+	9222:  "teamcoherence",
+	9255:  "mon",
+	9278:  "pegasus",
+	9279:  "pegasus-ctl",
+	9280:  "pgps",
+	9281:  "swtp-port1",
+	9282:  "swtp-port2",
+	9283:  "callwaveiam",
+	9284:  "visd",
+	9285:  "n2h2server",
+	9287:  "cumulus",
+	9292:  "armtechdaemon",
+	9293:  "storview",
+	9294:  "armcenterhttp",
+	9295:  "armcenterhttps",
+	9300:  "vrace",
+	9306:  "sphinxql",
+	9312:  "sphinxapi",
+	9318:  "secure-ts",
+	9321:  "guibase",
+	9343:  "mpidcmgr",
+	9344:  "mphlpdmc",
+	9345:  "rancher",
+	9346:  "ctechlicensing",
+	9374:  "fjdmimgr",
+	9380:  "boxp",
+	9387:  "d2dconfig",
+	9388:  "d2ddatatrans",
+	9389:  "adws",
+	9390:  "otp",
+	9396:  "fjinvmgr",
+	9397:  "mpidcagt",
+	9400:  "sec-t4net-srv",
+	9401:  "sec-t4net-clt",
+	9402:  "sec-pc2fax-srv",
+	9418:  "git",
+	9443:  "tungsten-https",
+	9444:  "wso2esb-console",
+	9445:  "mindarray-ca",
+	9450:  "sntlkeyssrvr",
+	9500:  "ismserver",
+	9535:  "mngsuite",
+	9536:  "laes-bf",
+	9555:  "trispen-sra",
+	9592:  "ldgateway",
+	9593:  "cba8",
+	9594:  "msgsys",
+	9595:  "pds",
+	9596:  "mercury-disc",
+	9597:  "pd-admin",
+	9598:  "vscp",
+	9599:  "robix",
+	9600:  "micromuse-ncpw",
+	9612:  "streamcomm-ds",
+	9614:  "iadt-tls",
+	9616:  "erunbook-agent",
+	9617:  "erunbook-server",
+	9618:  "condor",
+	9628:  "odbcpathway",
+	9629:  "uniport",
+	9630:  "peoctlr",
+	9631:  "peocoll",
+	9640:  "pqsflows",
+	9666:  "zoomcp",
+	9667:  "xmms2",
+	9668:  "tec5-sdctp",
+	9694:  "client-wakeup",
+	9695:  "ccnx",
+	9700:  "board-roar",
+	9747:  "l5nas-parchan",
+	9750:  "board-voip",
+	9753:  "rasadv",
+	9762:  "tungsten-http",
+	9800:  "davsrc",
+	9801:  "sstp-2",
+	9802:  "davsrcs",
+	9875:  "sapv1",
+	9876:  "sd",
+	9888:  "cyborg-systems",
+	9889:  "gt-proxy",
+	9898:  "monkeycom",
+	9900:  "iua",
+	9909:  "domaintime",
+	9911:  "sype-transport",
+	9925:  "xybrid-cloud",
+	9950:  "apc-9950",
+	9951:  "apc-9951",
+	9952:  "apc-9952",
+	9953:  "acis",
+	9954:  "hinp",
+	9955:  "alljoyn-stm",
+	9966:  "odnsp",
+	9978:  "xybrid-rt",
+	9979:  "visweather",
+	9981:  "pumpkindb",
+	9987:  "dsm-scm-target",
+	9988:  "nsesrvr",
+	9990:  "osm-appsrvr",
+	9991:  "osm-oev",
+	9992:  "palace-1",
+	9993:  "palace-2",
+	9994:  "palace-3",
+	9995:  "palace-4",
+	9996:  "palace-5",
+	9997:  "palace-6",
+	9998:  "distinct32",
+	9999:  "distinct",
+	10000: "ndmp",
+	10001: "scp-config",
+	10002: "documentum",
+	10003: "documentum-s",
+	10004: "emcrmirccd",
+	10005: "emcrmird",
+	10006: "netapp-sync",
+	10007: "mvs-capacity",
+	10008: "octopus",
+	10009: "swdtp-sv",
+	10010: "rxapi",
+	10020: "abb-hw",
+	10050: "zabbix-agent",
+	10051: "zabbix-trapper",
+	10055: "qptlmd",
+	10080: "amanda",
+	10081: "famdc",
+	10100: "itap-ddtp",
+	10101: "ezmeeting-2",
+	10102: "ezproxy-2",
+	10103: "ezrelay",
+	10104: "swdtp",
+	10107: "bctp-server",
+	10110: "nmea-0183",
+	10113: "netiq-endpoint",
+	10114: "netiq-qcheck",
+	10115: "netiq-endpt",
+	10116: "netiq-voipa",
+	10117: "iqrm",
+	10125: "cimple",
+	10128: "bmc-perf-sd",
+	10129: "bmc-gms",
+	10160: "qb-db-server",
+	10161: "snmptls",
+	10162: "snmptls-trap",
+	10200: "trisoap",
+	10201: "rsms",
+	10252: "apollo-relay",
+	10260: "axis-wimp-port",
+	10261: "tile-ml",
+	10288: "blocks",
+	10321: "cosir",
+	10540: "MOS-lower",
+	10541: "MOS-upper",
+	10542: "MOS-aux",
+	10543: "MOS-soap",
+	10544: "MOS-soap-opt",
+	10548: "serverdocs",
+	10631: "printopia",
+	10800: "gap",
+	10805: "lpdg",
+	10809: "nbd",
+	10860: "helix",
+	10880: "bveapi",
+	10933: "octopustentacle",
+	10990: "rmiaux",
+	11000: "irisa",
+	11001: "metasys",
+	11095: "weave",
+	11103: "origo-sync",
+	11104: "netapp-icmgmt",
+	11105: "netapp-icdata",
+	11106: "sgi-lk",
+	11109: "sgi-dmfmgr",
+	11110: "sgi-soap",
+	11111: "vce",
+	11112: "dicom",
+	11161: "suncacao-snmp",
+	11162: "suncacao-jmxmp",
+	11163: "suncacao-rmi",
+	11164: "suncacao-csa",
+	11165: "suncacao-websvc",
+	11172: "oemcacao-jmxmp",
+	11173: "t5-straton",
+	11174: "oemcacao-rmi",
+	11175: "oemcacao-websvc",
+	11201: "smsqp",
+	11202: "dcsl-backup",
+	11208: "wifree",
+	11211: "memcache",
+	11319: "imip",
+	11320: "imip-channels",
+	11321: "arena-server",
+	11367: "atm-uhas",
+	11371: "hkp",
+	11489: "asgcypresstcps",
+	11600: "tempest-port",
+	11623: "emc-xsw-dconfig",
+	11720: "h323callsigalt",
+	11723: "emc-xsw-dcache",
+	11751: "intrepid-ssl",
+	11796: "lanschool",
+	11876: "xoraya",
+	11967: "sysinfo-sp",
+	12000: "entextxid",
+	12001: "entextnetwk",
+	12002: "entexthigh",
+	12003: "entextmed",
+	12004: "entextlow",
+	12005: "dbisamserver1",
+	12006: "dbisamserver2",
+	12007: "accuracer",
+	12008: "accuracer-dbms",
+	12010: "edbsrvr",
+	12012: "vipera",
+	12013: "vipera-ssl",
+	12109: "rets-ssl",
+	12121: "nupaper-ss",
+	12168: "cawas",
+	12172: "hivep",
+	12300: "linogridengine",
+	12302: "rads",
+	12321: "warehouse-sss",
+	12322: "warehouse",
+	12345: "italk",
+	12753: "tsaf",
+	12865: "netperf",
+	13160: "i-zipqd",
+	13216: "bcslogc",
+	13217: "rs-pias",
+	13218: "emc-vcas-tcp",
+	13223: "powwow-client",
+	13224: "powwow-server",
+	13400: "doip-data",
+	13720: "bprd",
+	13721: "bpdbm",
+	13722: "bpjava-msvc",
+	13724: "vnetd",
+	13782: "bpcd",
+	13783: "vopied",
+	13785: "nbdb",
+	13786: "nomdb",
+	13818: "dsmcc-config",
+	13819: "dsmcc-session",
+	13820: "dsmcc-passthru",
+	13821: "dsmcc-download",
+	13822: "dsmcc-ccp",
+	13823: "bmdss",
+	13894: "ucontrol",
+	13929: "dta-systems",
+	13930: "medevolve",
+	14000: "scotty-ft",
+	14001: "sua",
+	14033: "sage-best-com1",
+	14034: "sage-best-com2",
+	14141: "vcs-app",
+	14142: "icpp",
+	14143: "icpps",
+	14145: "gcm-app",
+	14149: "vrts-tdd",
+	14150: "vcscmd",
+	14154: "vad",
+	14250: "cps",
+	14414: "ca-web-update",
+	14500: "xpra",
+	14936: "hde-lcesrvr-1",
+	14937: "hde-lcesrvr-2",
+	15000: "hydap",
+	15002: "onep-tls",
+	15345: "xpilot",
+	15363: "3link",
+	15555: "cisco-snat",
+	15660: "bex-xr",
+	15740: "ptp",
+	15999: "programmar",
+	16000: "fmsas",
+	16001: "fmsascon",
+	16002: "gsms",
+	16020: "jwpc",
+	16021: "jwpc-bin",
+	16161: "sun-sea-port",
+	16162: "solaris-audit",
+	16309: "etb4j",
+	16310: "pduncs",
+	16311: "pdefmns",
+	16360: "netserialext1",
+	16361: "netserialext2",
+	16367: "netserialext3",
+	16368: "netserialext4",
+	16384: "connected",
+	16385: "rdgs",
+	16619: "xoms",
+	16665: "axon-tunnel",
+	16789: "cadsisvr",
+	16900: "newbay-snc-mc",
+	16950: "sgcip",
+	16991: "intel-rci-mp",
+	16992: "amt-soap-http",
+	16993: "amt-soap-https",
+	16994: "amt-redir-tcp",
+	16995: "amt-redir-tls",
+	17007: "isode-dua",
+	17184: "vestasdlp",
+	17185: "soundsvirtual",
+	17219: "chipper",
+	17220: "avtp",
+	17221: "avdecc",
+	17223: "isa100-gci",
+	17225: "trdp-md",
+	17234: "integrius-stp",
+	17235: "ssh-mgmt",
+	17500: "db-lsp",
+	17555: "ailith",
+	17729: "ea",
+	17754: "zep",
+	17755: "zigbee-ip",
+	17756: "zigbee-ips",
+	17777: "sw-orion",
+	18000: "biimenu",
+	18104: "radpdf",
+	18136: "racf",
+	18181: "opsec-cvp",
+	18182: "opsec-ufp",
+	18183: "opsec-sam",
+	18184: "opsec-lea",
+	18185: "opsec-omi",
+	18186: "ohsc",
+	18187: "opsec-ela",
+	18241: "checkpoint-rtm",
+	18242: "iclid",
+	18243: "clusterxl",
+	18262: "gv-pf",
+	18463: "ac-cluster",
+	18634: "rds-ib",
+	18635: "rds-ip",
+	18668: "vdmmesh",
+	18769: "ique",
+	18881: "infotos",
+	18888: "apc-necmp",
+	19000: "igrid",
+	19007: "scintilla",
+	19020: "j-link",
+	19191: "opsec-uaa",
+	19194: "ua-secureagent",
+	19220: "cora",
+	19283: "keysrvr",
+	19315: "keyshadow",
+	19398: "mtrgtrans",
+	19410: "hp-sco",
+	19411: "hp-sca",
+	19412: "hp-sessmon",
+	19539: "fxuptp",
+	19540: "sxuptp",
+	19541: "jcp",
+	19998: "iec-104-sec",
+	19999: "dnp-sec",
+	20000: "dnp",
+	20001: "microsan",
+	20002: "commtact-http",
+	20003: "commtact-https",
+	20005: "openwebnet",
+	20013: "ss-idi",
+	20014: "opendeploy",
+	20034: "nburn-id",
+	20046: "tmophl7mts",
+	20048: "mountd",
+	20049: "nfsrdma",
+	20057: "avesterra",
+	20167: "tolfab",
+	20202: "ipdtp-port",
+	20222: "ipulse-ics",
+	20480: "emwavemsg",
+	20670: "track",
+	20999: "athand-mmp",
+	21000: "irtrans",
+	21010: "notezilla-lan",
+	21221: "aigairserver",
+	21553: "rdm-tfs",
+	21554: "dfserver",
+	21590: "vofr-gateway",
+	21800: "tvpm",
+	21845: "webphone",
+	21846: "netspeak-is",
+	21847: "netspeak-cs",
+	21848: "netspeak-acd",
+	21849: "netspeak-cps",
+	22000: "snapenetio",
+	22001: "optocontrol",
+	22002: "optohost002",
+	22003: "optohost003",
+	22004: "optohost004",
+	22005: "optohost004",
+	22125: "dcap",
+	22128: "gsidcap",
+	22222: "easyengine",
+	22273: "wnn6",
+	22305: "cis",
+	22335: "shrewd-control",
+	22343: "cis-secure",
+	22347: "wibukey",
+	22350: "codemeter",
+	22351: "codemeter-cmwan",
+	22537: "caldsoft-backup",
+	22555: "vocaltec-wconf",
+	22763: "talikaserver",
+	22800: "aws-brf",
+	22951: "brf-gw",
+	23000: "inovaport1",
+	23001: "inovaport2",
+	23002: "inovaport3",
+	23003: "inovaport4",
+	23004: "inovaport5",
+	23005: "inovaport6",
+	23053: "gntp",
+	23294: "5afe-dir",
+	23333: "elxmgmt",
+	23400: "novar-dbase",
+	23401: "novar-alarm",
+	23402: "novar-global",
+	23456: "aequus",
+	23457: "aequus-alt",
+	23546: "areaguard-neo",
+	24000: "med-ltp",
+	24001: "med-fsp-rx",
+	24002: "med-fsp-tx",
+	24003: "med-supp",
+	24004: "med-ovw",
+	24005: "med-ci",
+	24006: "med-net-svc",
+	24242: "filesphere",
+	24249: "vista-4gl",
+	24321: "ild",
+	24386: "intel-rci",
+	24465: "tonidods",
+	24554: "binkp",
+	24577: "bilobit",
+	24666: "sdtvwcam",
+	24676: "canditv",
+	24677: "flashfiler",
+	24678: "proactivate",
+	24680: "tcc-http",
+	24754: "cslg",
+	24922: "find",
+	25000: "icl-twobase1",
+	25001: "icl-twobase2",
+	25002: "icl-twobase3",
+	25003: "icl-twobase4",
+	25004: "icl-twobase5",
+	25005: "icl-twobase6",
+	25006: "icl-twobase7",
+	25007: "icl-twobase8",
+	25008: "icl-twobase9",
+	25009: "icl-twobase10",
+	25576: "sauterdongle",
+	25604: "idtp",
+	25793: "vocaltec-hos",
+	25900: "tasp-net",
+	25901: "niobserver",
+	25902: "nilinkanalyst",
+	25903: "niprobe",
+	26000: "quake",
+	26133: "scscp",
+	26208: "wnn6-ds",
+	26257: "cockroach",
+	26260: "ezproxy",
+	26261: "ezmeeting",
+	26262: "k3software-svr",
+	26263: "k3software-cli",
+	26486: "exoline-tcp",
+	26487: "exoconfig",
+	26489: "exonet",
+	27345: "imagepump",
+	27442: "jesmsjc",
+	27504: "kopek-httphead",
+	27782: "ars-vista",
+	27876: "astrolink",
+	27999: "tw-auth-key",
+	28000: "nxlmd",
+	28001: "pqsp",
+	28200: "voxelstorm",
+	28240: "siemensgsm",
+	28589: "bosswave",
+	29167: "otmp",
+	29999: "bingbang",
+	30000: "ndmps",
+	30001: "pago-services1",
+	30002: "pago-services2",
+	30003: "amicon-fpsu-ra",
+	30100: "rwp",
+	30260: "kingdomsonline",
+	30400: "gs-realtime",
+	30999: "ovobs",
+	31016: "ka-sddp",
+	31020: "autotrac-acp",
+	31400: "pace-licensed",
+	31416: "xqosd",
+	31457: "tetrinet",
+	31620: "lm-mon",
+	31685: "dsx-monitor",
+	31765: "gamesmith-port",
+	31948: "iceedcp-tx",
+	31949: "iceedcp-rx",
+	32034: "iracinghelper",
+	32249: "t1distproc60",
+	32400: "plex",
+	32483: "apm-link",
+	32635: "sec-ntb-clnt",
+	32636: "DMExpress",
+	32767: "filenet-powsrm",
+	32768: "filenet-tms",
+	32769: "filenet-rpc",
+	32770: "filenet-nch",
+	32771: "filenet-rmi",
+	32772: "filenet-pa",
+	32773: "filenet-cm",
+	32774: "filenet-re",
+	32775: "filenet-pch",
+	32776: "filenet-peior",
+	32777: "filenet-obrok",
+	32801: "mlsn",
+	32811: "retp",
+	32896: "idmgratm",
+	33060: "mysqlx",
+	33123: "aurora-balaena",
+	33331: "diamondport",
+	33333: "dgi-serv",
+	33334: "speedtrace",
+	33434: "traceroute",
+	33656: "snip-slave",
+	34249: "turbonote-2",
+	34378: "p-net-local",
+	34379: "p-net-remote",
+	34567: "dhanalakshmi",
+	34962: "profinet-rt",
+	34963: "profinet-rtm",
+	34964: "profinet-cm",
+	34980: "ethercat",
+	35000: "heathview",
+	35001: "rt-viewer",
+	35002: "rt-sound",
+	35003: "rt-devicemapper",
+	35004: "rt-classmanager",
+	35005: "rt-labtracker",
+	35006: "rt-helper",
+	35100: "axio-disc",
+	35354: "kitim",
+	35355: "altova-lm",
+	35356: "guttersnex",
+	35357: "openstack-id",
+	36001: "allpeers",
+	36524: "febooti-aw",
+	36602: "observium-agent",
+	36700: "mapx",
+	36865: "kastenxpipe",
+	37475: "neckar",
+	37483: "gdrive-sync",
+	37601: "eftp",
+	37654: "unisys-eportal",
+	38000: "ivs-database",
+	38001: "ivs-insertion",
+	38002: "cresco-control",
+	38201: "galaxy7-data",
+	38202: "fairview",
+	38203: "agpolicy",
+	38800: "sruth",
+	38865: "secrmmsafecopya",
+	39681: "turbonote-1",
+	40000: "safetynetp",
+	40404: "sptx",
+	40841: "cscp",
+	40842: "csccredir",
+	40843: "csccfirewall",
+	41111: "fs-qos",
+	41121: "tentacle",
+	41230: "z-wave-s",
+	41794: "crestron-cip",
+	41795: "crestron-ctp",
+	41796: "crestron-cips",
+	41797: "crestron-ctps",
+	42508: "candp",
+	42509: "candrp",
+	42510: "caerpc",
+	43000: "recvr-rc",
+	43188: "reachout",
+	43189: "ndm-agent-port",
+	43190: "ip-provision",
+	43191: "noit-transport",
+	43210: "shaperai",
+	43439: "eq3-update",
+	43440: "ew-mgmt",
+	43441: "ciscocsdb",
+	44123: "z-wave-tunnel",
+	44321: "pmcd",
+	44322: "pmcdproxy",
+	44323: "pmwebapi",
+	44444: "cognex-dataman",
+	44553: "rbr-debug",
+	44818: "EtherNet-IP-2",
+	44900: "m3da",
+	45000: "asmp",
+	45001: "asmps",
+	45002: "rs-status",
+	45045: "synctest",
+	45054: "invision-ag",
+	45514: "cloudcheck",
+	45678: "eba",
+	45824: "dai-shell",
+	45825: "qdb2service",
+	45966: "ssr-servermgr",
+	46336: "inedo",
+	46998: "spremotetablet",
+	46999: "mediabox",
+	47000: "mbus",
+	47001: "winrm",
+	47557: "dbbrowse",
+	47624: "directplaysrvr",
+	47806: "ap",
+	47808: "bacnet",
+	48000: "nimcontroller",
+	48001: "nimspooler",
+	48002: "nimhub",
+	48003: "nimgtw",
+	48004: "nimbusdb",
+	48005: "nimbusdbctrl",
+	48049: "3gpp-cbsp",
+	48050: "weandsf",
+	48128: "isnetserv",
+	48129: "blp5",
+	48556: "com-bardac-dw",
+	48619: "iqobject",
+	48653: "robotraconteur",
+	49000: "matahari",
+	49001: "nusrp",
+}
+var udpPortNames = map[UDPPort]string{
+	1:     "tcpmux",
+	2:     "compressnet",
+	3:     "compressnet",
+	5:     "rje",
+	7:     "echo",
+	9:     "discard",
+	11:    "systat",
+	13:    "daytime",
+	17:    "qotd",
+	18:    "msp",
+	19:    "chargen",
+	20:    "ftp-data",
+	21:    "ftp",
+	22:    "ssh",
+	23:    "telnet",
+	25:    "smtp",
+	27:    "nsw-fe",
+	29:    "msg-icp",
+	31:    "msg-auth",
+	33:    "dsp",
+	37:    "time",
+	38:    "rap",
+	39:    "rlp",
+	41:    "graphics",
+	42:    "name",
+	43:    "nicname",
+	44:    "mpm-flags",
+	45:    "mpm",
+	46:    "mpm-snd",
+	48:    "auditd",
+	49:    "tacacs",
+	50:    "re-mail-ck",
+	52:    "xns-time",
+	53:    "domain",
+	54:    "xns-ch",
+	55:    "isi-gl",
+	56:    "xns-auth",
+	58:    "xns-mail",
+	62:    "acas",
+	63:    "whoispp",
+	64:    "covia",
+	65:    "tacacs-ds",
+	66:    "sql-net",
+	67:    "bootps",
+	68:    "bootpc",
+	69:    "tftp",
+	70:    "gopher",
+	71:    "netrjs-1",
+	72:    "netrjs-2",
+	73:    "netrjs-3",
+	74:    "netrjs-4",
+	76:    "deos",
+	78:    "vettcp",
+	79:    "finger",
+	80:    "http",
+	82:    "xfer",
+	83:    "mit-ml-dev",
+	84:    "ctf",
+	85:    "mit-ml-dev",
+	86:    "mfcobol",
+	88:    "kerberos",
+	89:    "su-mit-tg",
+	90:    "dnsix",
+	91:    "mit-dov",
+	92:    "npp",
+	93:    "dcp",
+	94:    "objcall",
+	95:    "supdup",
+	96:    "dixie",
+	97:    "swift-rvf",
+	98:    "tacnews",
+	99:    "metagram",
+	101:   "hostname",
+	102:   "iso-tsap",
+	103:   "gppitnp",
+	104:   "acr-nema",
+	105:   "cso",
+	106:   "3com-tsmux",
+	107:   "rtelnet",
+	108:   "snagas",
+	109:   "pop2",
+	110:   "pop3",
+	111:   "sunrpc",
+	112:   "mcidas",
+	113:   "auth",
+	115:   "sftp",
+	116:   "ansanotify",
+	117:   "uucp-path",
+	118:   "sqlserv",
+	119:   "nntp",
+	120:   "cfdptkt",
+	121:   "erpc",
+	122:   "smakynet",
+	123:   "ntp",
+	124:   "ansatrader",
+	125:   "locus-map",
+	126:   "nxedit",
+	127:   "locus-con",
+	128:   "gss-xlicen",
+	129:   "pwdgen",
+	130:   "cisco-fna",
+	131:   "cisco-tna",
+	132:   "cisco-sys",
+	133:   "statsrv",
+	134:   "ingres-net",
+	135:   "epmap",
+	136:   "profile",
+	137:   "netbios-ns",
+	138:   "netbios-dgm",
+	139:   "netbios-ssn",
+	140:   "emfis-data",
+	141:   "emfis-cntl",
+	142:   "bl-idm",
+	143:   "imap",
+	144:   "uma",
+	145:   "uaac",
+	146:   "iso-tp0",
+	147:   "iso-ip",
+	148:   "jargon",
+	149:   "aed-512",
+	150:   "sql-net",
+	151:   "hems",
+	152:   "bftp",
+	153:   "sgmp",
+	154:   "netsc-prod",
+	155:   "netsc-dev",
+	156:   "sqlsrv",
+	157:   "knet-cmp",
+	158:   "pcmail-srv",
+	159:   "nss-routing",
+	160:   "sgmp-traps",
+	161:   "snmp",
+	162:   "snmptrap",
+	163:   "cmip-man",
+	164:   "cmip-agent",
+	165:   "xns-courier",
+	166:   "s-net",
+	167:   "namp",
+	168:   "rsvd",
+	169:   "send",
+	170:   "print-srv",
+	171:   "multiplex",
+	172:   "cl-1",
+	173:   "xyplex-mux",
+	174:   "mailq",
+	175:   "vmnet",
+	176:   "genrad-mux",
+	177:   "xdmcp",
+	178:   "nextstep",
+	179:   "bgp",
+	180:   "ris",
+	181:   "unify",
+	182:   "audit",
+	183:   "ocbinder",
+	184:   "ocserver",
+	185:   "remote-kis",
+	186:   "kis",
+	187:   "aci",
+	188:   "mumps",
+	189:   "qft",
+	190:   "gacp",
+	191:   "prospero",
+	192:   "osu-nms",
+	193:   "srmp",
+	194:   "irc",
+	195:   "dn6-nlm-aud",
+	196:   "dn6-smm-red",
+	197:   "dls",
+	198:   "dls-mon",
+	199:   "smux",
+	200:   "src",
+	201:   "at-rtmp",
+	202:   "at-nbp",
+	203:   "at-3",
+	204:   "at-echo",
+	205:   "at-5",
+	206:   "at-zis",
+	207:   "at-7",
+	208:   "at-8",
+	209:   "qmtp",
+	210:   "z39-50",
+	211:   "914c-g",
+	212:   "anet",
+	213:   "ipx",
+	214:   "vmpwscs",
+	215:   "softpc",
+	216:   "CAIlic",
+	217:   "dbase",
+	218:   "mpp",
+	219:   "uarps",
+	220:   "imap3",
+	221:   "fln-spx",
+	222:   "rsh-spx",
+	223:   "cdc",
+	224:   "masqdialer",
+	242:   "direct",
+	243:   "sur-meas",
+	244:   "inbusiness",
+	245:   "link",
+	246:   "dsp3270",
+	247:   "subntbcst-tftp",
+	248:   "bhfhs",
+	256:   "rap",
+	257:   "set",
+	259:   "esro-gen",
+	260:   "openport",
+	261:   "nsiiops",
+	262:   "arcisdms",
+	263:   "hdap",
+	264:   "bgmp",
+	265:   "x-bone-ctl",
+	266:   "sst",
+	267:   "td-service",
+	268:   "td-replica",
+	269:   "manet",
+	270:   "gist",
+	280:   "http-mgmt",
+	281:   "personal-link",
+	282:   "cableport-ax",
+	283:   "rescap",
+	284:   "corerjd",
+	286:   "fxp",
+	287:   "k-block",
+	308:   "novastorbakcup",
+	309:   "entrusttime",
+	310:   "bhmds",
+	311:   "asip-webadmin",
+	312:   "vslmp",
+	313:   "magenta-logic",
+	314:   "opalis-robot",
+	315:   "dpsi",
+	316:   "decauth",
+	317:   "zannet",
+	318:   "pkix-timestamp",
+	319:   "ptp-event",
+	320:   "ptp-general",
+	321:   "pip",
+	322:   "rtsps",
+	333:   "texar",
+	344:   "pdap",
+	345:   "pawserv",
+	346:   "zserv",
+	347:   "fatserv",
+	348:   "csi-sgwp",
+	349:   "mftp",
+	350:   "matip-type-a",
+	351:   "matip-type-b",
+	352:   "dtag-ste-sb",
+	353:   "ndsauth",
+	354:   "bh611",
+	355:   "datex-asn",
+	356:   "cloanto-net-1",
+	357:   "bhevent",
+	358:   "shrinkwrap",
+	359:   "nsrmp",
+	360:   "scoi2odialog",
+	361:   "semantix",
+	362:   "srssend",
+	363:   "rsvp-tunnel",
+	364:   "aurora-cmgr",
+	365:   "dtk",
+	366:   "odmr",
+	367:   "mortgageware",
+	368:   "qbikgdp",
+	369:   "rpc2portmap",
+	370:   "codaauth2",
+	371:   "clearcase",
+	372:   "ulistproc",
+	373:   "legent-1",
+	374:   "legent-2",
+	375:   "hassle",
+	376:   "nip",
+	377:   "tnETOS",
+	378:   "dsETOS",
+	379:   "is99c",
+	380:   "is99s",
+	381:   "hp-collector",
+	382:   "hp-managed-node",
+	383:   "hp-alarm-mgr",
+	384:   "arns",
+	385:   "ibm-app",
+	386:   "asa",
+	387:   "aurp",
+	388:   "unidata-ldm",
+	389:   "ldap",
+	390:   "uis",
+	391:   "synotics-relay",
+	392:   "synotics-broker",
+	393:   "meta5",
+	394:   "embl-ndt",
+	395:   "netcp",
+	396:   "netware-ip",
+	397:   "mptn",
+	398:   "kryptolan",
+	399:   "iso-tsap-c2",
+	400:   "osb-sd",
+	401:   "ups",
+	402:   "genie",
+	403:   "decap",
+	404:   "nced",
+	405:   "ncld",
+	406:   "imsp",
+	407:   "timbuktu",
+	408:   "prm-sm",
+	409:   "prm-nm",
+	410:   "decladebug",
+	411:   "rmt",
+	412:   "synoptics-trap",
+	413:   "smsp",
+	414:   "infoseek",
+	415:   "bnet",
+	416:   "silverplatter",
+	417:   "onmux",
+	418:   "hyper-g",
+	419:   "ariel1",
+	420:   "smpte",
+	421:   "ariel2",
+	422:   "ariel3",
+	423:   "opc-job-start",
+	424:   "opc-job-track",
+	425:   "icad-el",
+	426:   "smartsdp",
+	427:   "svrloc",
+	428:   "ocs-cmu",
+	429:   "ocs-amu",
+	430:   "utmpsd",
+	431:   "utmpcd",
+	432:   "iasd",
+	433:   "nnsp",
+	434:   "mobileip-agent",
+	435:   "mobilip-mn",
+	436:   "dna-cml",
+	437:   "comscm",
+	438:   "dsfgw",
+	439:   "dasp",
+	440:   "sgcp",
+	441:   "decvms-sysmgt",
+	442:   "cvc-hostd",
+	443:   "https",
+	444:   "snpp",
+	445:   "microsoft-ds",
+	446:   "ddm-rdb",
+	447:   "ddm-dfm",
+	448:   "ddm-ssl",
+	449:   "as-servermap",
+	450:   "tserver",
+	451:   "sfs-smp-net",
+	452:   "sfs-config",
+	453:   "creativeserver",
+	454:   "contentserver",
+	455:   "creativepartnr",
+	456:   "macon-udp",
+	457:   "scohelp",
+	458:   "appleqtc",
+	459:   "ampr-rcmd",
+	460:   "skronk",
+	461:   "datasurfsrv",
+	462:   "datasurfsrvsec",
+	463:   "alpes",
+	464:   "kpasswd",
+	465:   "igmpv3lite",
+	466:   "digital-vrc",
+	467:   "mylex-mapd",
+	468:   "photuris",
+	469:   "rcp",
+	470:   "scx-proxy",
+	471:   "mondex",
+	472:   "ljk-login",
+	473:   "hybrid-pop",
+	474:   "tn-tl-w2",
+	475:   "tcpnethaspsrv",
+	476:   "tn-tl-fd1",
+	477:   "ss7ns",
+	478:   "spsc",
+	479:   "iafserver",
+	480:   "iafdbase",
+	481:   "ph",
+	482:   "bgs-nsi",
+	483:   "ulpnet",
+	484:   "integra-sme",
+	485:   "powerburst",
+	486:   "avian",
+	487:   "saft",
+	488:   "gss-http",
+	489:   "nest-protocol",
+	490:   "micom-pfs",
+	491:   "go-login",
+	492:   "ticf-1",
+	493:   "ticf-2",
+	494:   "pov-ray",
+	495:   "intecourier",
+	496:   "pim-rp-disc",
+	497:   "retrospect",
+	498:   "siam",
+	499:   "iso-ill",
+	500:   "isakmp",
+	501:   "stmf",
+	502:   "mbap",
+	503:   "intrinsa",
+	504:   "citadel",
+	505:   "mailbox-lm",
+	506:   "ohimsrv",
+	507:   "crs",
+	508:   "xvttp",
+	509:   "snare",
+	510:   "fcp",
+	511:   "passgo",
+	512:   "comsat",
+	513:   "who",
+	514:   "syslog",
+	515:   "printer",
+	516:   "videotex",
+	517:   "talk",
+	518:   "ntalk",
+	519:   "utime",
+	520:   "router",
+	521:   "ripng",
+	522:   "ulp",
+	523:   "ibm-db2",
+	524:   "ncp",
+	525:   "timed",
+	526:   "tempo",
+	527:   "stx",
+	528:   "custix",
+	529:   "irc-serv",
+	530:   "courier",
+	531:   "conference",
+	532:   "netnews",
+	533:   "netwall",
+	534:   "windream",
+	535:   "iiop",
+	536:   "opalis-rdv",
+	537:   "nmsp",
+	538:   "gdomap",
+	539:   "apertus-ldp",
+	540:   "uucp",
+	541:   "uucp-rlogin",
+	542:   "commerce",
+	543:   "klogin",
+	544:   "kshell",
+	545:   "appleqtcsrvr",
+	546:   "dhcpv6-client",
+	547:   "dhcpv6-server",
+	548:   "afpovertcp",
+	549:   "idfp",
+	550:   "new-rwho",
+	551:   "cybercash",
+	552:   "devshr-nts",
+	553:   "pirp",
+	554:   "rtsp",
+	555:   "dsf",
+	556:   "remotefs",
+	557:   "openvms-sysipc",
+	558:   "sdnskmp",
+	559:   "teedtap",
+	560:   "rmonitor",
+	561:   "monitor",
+	562:   "chshell",
+	563:   "nntps",
+	564:   "9pfs",
+	565:   "whoami",
+	566:   "streettalk",
+	567:   "banyan-rpc",
+	568:   "ms-shuttle",
+	569:   "ms-rome",
+	570:   "meter",
+	571:   "meter",
+	572:   "sonar",
+	573:   "banyan-vip",
+	574:   "ftp-agent",
+	575:   "vemmi",
+	576:   "ipcd",
+	577:   "vnas",
+	578:   "ipdd",
+	579:   "decbsrv",
+	580:   "sntp-heartbeat",
+	581:   "bdp",
+	582:   "scc-security",
+	583:   "philips-vc",
+	584:   "keyserver",
+	586:   "password-chg",
+	587:   "submission",
+	588:   "cal",
+	589:   "eyelink",
+	590:   "tns-cml",
+	591:   "http-alt",
+	592:   "eudora-set",
+	593:   "http-rpc-epmap",
+	594:   "tpip",
+	595:   "cab-protocol",
+	596:   "smsd",
+	597:   "ptcnameservice",
+	598:   "sco-websrvrmg3",
+	599:   "acp",
+	600:   "ipcserver",
+	601:   "syslog-conn",
+	602:   "xmlrpc-beep",
+	603:   "idxp",
+	604:   "tunnel",
+	605:   "soap-beep",
+	606:   "urm",
+	607:   "nqs",
+	608:   "sift-uft",
+	609:   "npmp-trap",
+	610:   "npmp-local",
+	611:   "npmp-gui",
+	612:   "hmmp-ind",
+	613:   "hmmp-op",
+	614:   "sshell",
+	615:   "sco-inetmgr",
+	616:   "sco-sysmgr",
+	617:   "sco-dtmgr",
+	618:   "dei-icda",
+	619:   "compaq-evm",
+	620:   "sco-websrvrmgr",
+	621:   "escp-ip",
+	622:   "collaborator",
+	623:   "asf-rmcp",
+	624:   "cryptoadmin",
+	625:   "dec-dlm",
+	626:   "asia",
+	627:   "passgo-tivoli",
+	628:   "qmqp",
+	629:   "3com-amp3",
+	630:   "rda",
+	631:   "ipp",
+	632:   "bmpp",
+	633:   "servstat",
+	634:   "ginad",
+	635:   "rlzdbase",
+	636:   "ldaps",
+	637:   "lanserver",
+	638:   "mcns-sec",
+	639:   "msdp",
+	640:   "entrust-sps",
+	641:   "repcmd",
+	642:   "esro-emsdp",
+	643:   "sanity",
+	644:   "dwr",
+	645:   "pssc",
+	646:   "ldp",
+	647:   "dhcp-failover",
+	648:   "rrp",
+	649:   "cadview-3d",
+	650:   "obex",
+	651:   "ieee-mms",
+	652:   "hello-port",
+	653:   "repscmd",
+	654:   "aodv",
+	655:   "tinc",
+	656:   "spmp",
+	657:   "rmc",
+	658:   "tenfold",
+	660:   "mac-srvr-admin",
+	661:   "hap",
+	662:   "pftp",
+	663:   "purenoise",
+	664:   "asf-secure-rmcp",
+	665:   "sun-dr",
+	666:   "mdqs",
+	667:   "disclose",
+	668:   "mecomm",
+	669:   "meregister",
+	670:   "vacdsm-sws",
+	671:   "vacdsm-app",
+	672:   "vpps-qua",
+	673:   "cimplex",
+	674:   "acap",
+	675:   "dctp",
+	676:   "vpps-via",
+	677:   "vpp",
+	678:   "ggf-ncp",
+	679:   "mrm",
+	680:   "entrust-aaas",
+	681:   "entrust-aams",
+	682:   "xfr",
+	683:   "corba-iiop",
+	684:   "corba-iiop-ssl",
+	685:   "mdc-portmapper",
+	686:   "hcp-wismar",
+	687:   "asipregistry",
+	688:   "realm-rusd",
+	689:   "nmap",
+	690:   "vatp",
+	691:   "msexch-routing",
+	692:   "hyperwave-isp",
+	693:   "connendp",
+	694:   "ha-cluster",
+	695:   "ieee-mms-ssl",
+	696:   "rushd",
+	697:   "uuidgen",
+	698:   "olsr",
+	699:   "accessnetwork",
+	700:   "epp",
+	701:   "lmp",
+	702:   "iris-beep",
+	704:   "elcsd",
+	705:   "agentx",
+	706:   "silc",
+	707:   "borland-dsj",
+	709:   "entrust-kmsh",
+	710:   "entrust-ash",
+	711:   "cisco-tdp",
+	712:   "tbrpf",
+	713:   "iris-xpc",
+	714:   "iris-xpcs",
+	715:   "iris-lwz",
+	716:   "pana",
+	729:   "netviewdm1",
+	730:   "netviewdm2",
+	731:   "netviewdm3",
+	741:   "netgw",
+	742:   "netrcs",
+	744:   "flexlm",
+	747:   "fujitsu-dev",
+	748:   "ris-cm",
+	749:   "kerberos-adm",
+	750:   "loadav",
+	751:   "pump",
+	752:   "qrh",
+	753:   "rrh",
+	754:   "tell",
+	758:   "nlogin",
+	759:   "con",
+	760:   "ns",
+	761:   "rxe",
+	762:   "quotad",
+	763:   "cycleserv",
+	764:   "omserv",
+	765:   "webster",
+	767:   "phonebook",
+	769:   "vid",
+	770:   "cadlock",
+	771:   "rtip",
+	772:   "cycleserv2",
+	773:   "notify",
+	774:   "acmaint-dbd",
+	775:   "acmaint-transd",
+	776:   "wpages",
+	777:   "multiling-http",
+	780:   "wpgs",
+	800:   "mdbs-daemon",
+	801:   "device",
+	802:   "mbap-s",
+	810:   "fcp-udp",
+	828:   "itm-mcell-s",
+	829:   "pkix-3-ca-ra",
+	830:   "netconf-ssh",
+	831:   "netconf-beep",
+	832:   "netconfsoaphttp",
+	833:   "netconfsoapbeep",
+	847:   "dhcp-failover2",
+	848:   "gdoi",
+	853:   "domain-s",
+	854:   "dlep",
+	860:   "iscsi",
+	861:   "owamp-control",
+	862:   "twamp-control",
+	873:   "rsync",
+	886:   "iclcnet-locate",
+	887:   "iclcnet-svinfo",
+	888:   "accessbuilder",
+	900:   "omginitialrefs",
+	901:   "smpnameres",
+	902:   "ideafarm-door",
+	903:   "ideafarm-panic",
+	910:   "kink",
+	911:   "xact-backup",
+	912:   "apex-mesh",
+	913:   "apex-edge",
+	989:   "ftps-data",
+	990:   "ftps",
+	991:   "nas",
+	992:   "telnets",
+	993:   "imaps",
+	995:   "pop3s",
+	996:   "vsinet",
+	997:   "maitrd",
+	998:   "puparp",
+	999:   "applix",
+	1000:  "cadlock2",
+	1010:  "surf",
+	1021:  "exp1",
+	1022:  "exp2",
+	1025:  "blackjack",
+	1026:  "cap",
+	1027:  "6a44",
+	1029:  "solid-mux",
+	1033:  "netinfo-local",
+	1034:  "activesync",
+	1035:  "mxxrlogin",
+	1036:  "nsstp",
+	1037:  "ams",
+	1038:  "mtqp",
+	1039:  "sbl",
+	1040:  "netarx",
+	1041:  "danf-ak2",
+	1042:  "afrog",
+	1043:  "boinc-client",
+	1044:  "dcutility",
+	1045:  "fpitp",
+	1046:  "wfremotertm",
+	1047:  "neod1",
+	1048:  "neod2",
+	1049:  "td-postman",
+	1050:  "cma",
+	1051:  "optima-vnet",
+	1052:  "ddt",
+	1053:  "remote-as",
+	1054:  "brvread",
+	1055:  "ansyslmd",
+	1056:  "vfo",
+	1057:  "startron",
+	1058:  "nim",
+	1059:  "nimreg",
+	1060:  "polestar",
+	1061:  "kiosk",
+	1062:  "veracity",
+	1063:  "kyoceranetdev",
+	1064:  "jstel",
+	1065:  "syscomlan",
+	1066:  "fpo-fns",
+	1067:  "instl-boots",
+	1068:  "instl-bootc",
+	1069:  "cognex-insight",
+	1070:  "gmrupdateserv",
+	1071:  "bsquare-voip",
+	1072:  "cardax",
+	1073:  "bridgecontrol",
+	1074:  "warmspotMgmt",
+	1075:  "rdrmshc",
+	1076:  "dab-sti-c",
+	1077:  "imgames",
+	1078:  "avocent-proxy",
+	1079:  "asprovatalk",
+	1080:  "socks",
+	1081:  "pvuniwien",
+	1082:  "amt-esd-prot",
+	1083:  "ansoft-lm-1",
+	1084:  "ansoft-lm-2",
+	1085:  "webobjects",
+	1086:  "cplscrambler-lg",
+	1087:  "cplscrambler-in",
+	1088:  "cplscrambler-al",
+	1089:  "ff-annunc",
+	1090:  "ff-fms",
+	1091:  "ff-sm",
+	1092:  "obrpd",
+	1093:  "proofd",
+	1094:  "rootd",
+	1095:  "nicelink",
+	1096:  "cnrprotocol",
+	1097:  "sunclustermgr",
+	1098:  "rmiactivation",
+	1099:  "rmiregistry",
+	1100:  "mctp",
+	1101:  "pt2-discover",
+	1102:  "adobeserver-1",
+	1103:  "adobeserver-2",
+	1104:  "xrl",
+	1105:  "ftranhc",
+	1106:  "isoipsigport-1",
+	1107:  "isoipsigport-2",
+	1108:  "ratio-adp",
+	1110:  "nfsd-keepalive",
+	1111:  "lmsocialserver",
+	1112:  "icp",
+	1113:  "ltp-deepspace",
+	1114:  "mini-sql",
+	1115:  "ardus-trns",
+	1116:  "ardus-cntl",
+	1117:  "ardus-mtrns",
+	1118:  "sacred",
+	1119:  "bnetgame",
+	1120:  "bnetfile",
+	1121:  "rmpp",
+	1122:  "availant-mgr",
+	1123:  "murray",
+	1124:  "hpvmmcontrol",
+	1125:  "hpvmmagent",
+	1126:  "hpvmmdata",
+	1127:  "kwdb-commn",
+	1128:  "saphostctrl",
+	1129:  "saphostctrls",
+	1130:  "casp",
+	1131:  "caspssl",
+	1132:  "kvm-via-ip",
+	1133:  "dfn",
+	1134:  "aplx",
+	1135:  "omnivision",
+	1136:  "hhb-gateway",
+	1137:  "trim",
+	1138:  "encrypted-admin",
+	1139:  "evm",
+	1140:  "autonoc",
+	1141:  "mxomss",
+	1142:  "edtools",
+	1143:  "imyx",
+	1144:  "fuscript",
+	1145:  "x9-icue",
+	1146:  "audit-transfer",
+	1147:  "capioverlan",
+	1148:  "elfiq-repl",
+	1149:  "bvtsonar",
+	1150:  "blaze",
+	1151:  "unizensus",
+	1152:  "winpoplanmess",
+	1153:  "c1222-acse",
+	1154:  "resacommunity",
+	1155:  "nfa",
+	1156:  "iascontrol-oms",
+	1157:  "iascontrol",
+	1158:  "dbcontrol-oms",
+	1159:  "oracle-oms",
+	1160:  "olsv",
+	1161:  "health-polling",
+	1162:  "health-trap",
+	1163:  "sddp",
+	1164:  "qsm-proxy",
+	1165:  "qsm-gui",
+	1166:  "qsm-remote",
+	1167:  "cisco-ipsla",
+	1168:  "vchat",
+	1169:  "tripwire",
+	1170:  "atc-lm",
+	1171:  "atc-appserver",
+	1172:  "dnap",
+	1173:  "d-cinema-rrp",
+	1174:  "fnet-remote-ui",
+	1175:  "dossier",
+	1176:  "indigo-server",
+	1177:  "dkmessenger",
+	1178:  "sgi-storman",
+	1179:  "b2n",
+	1180:  "mc-client",
+	1181:  "3comnetman",
+	1182:  "accelenet-data",
+	1183:  "llsurfup-http",
+	1184:  "llsurfup-https",
+	1185:  "catchpole",
+	1186:  "mysql-cluster",
+	1187:  "alias",
+	1188:  "hp-webadmin",
+	1189:  "unet",
+	1190:  "commlinx-avl",
+	1191:  "gpfs",
+	1192:  "caids-sensor",
+	1193:  "fiveacross",
+	1194:  "openvpn",
+	1195:  "rsf-1",
+	1196:  "netmagic",
+	1197:  "carrius-rshell",
+	1198:  "cajo-discovery",
+	1199:  "dmidi",
+	1200:  "scol",
+	1201:  "nucleus-sand",
+	1202:  "caiccipc",
+	1203:  "ssslic-mgr",
+	1204:  "ssslog-mgr",
+	1205:  "accord-mgc",
+	1206:  "anthony-data",
+	1207:  "metasage",
+	1208:  "seagull-ais",
+	1209:  "ipcd3",
+	1210:  "eoss",
+	1211:  "groove-dpp",
+	1212:  "lupa",
+	1213:  "mpc-lifenet",
+	1214:  "kazaa",
+	1215:  "scanstat-1",
+	1216:  "etebac5",
+	1217:  "hpss-ndapi",
+	1218:  "aeroflight-ads",
+	1219:  "aeroflight-ret",
+	1220:  "qt-serveradmin",
+	1221:  "sweetware-apps",
+	1222:  "nerv",
+	1223:  "tgp",
+	1224:  "vpnz",
+	1225:  "slinkysearch",
+	1226:  "stgxfws",
+	1227:  "dns2go",
+	1228:  "florence",
+	1229:  "zented",
+	1230:  "periscope",
+	1231:  "menandmice-lpm",
+	1232:  "first-defense",
+	1233:  "univ-appserver",
+	1234:  "search-agent",
+	1235:  "mosaicsyssvc1",
+	1236:  "bvcontrol",
+	1237:  "tsdos390",
+	1238:  "hacl-qs",
+	1239:  "nmsd",
+	1240:  "instantia",
+	1241:  "nessus",
+	1242:  "nmasoverip",
+	1243:  "serialgateway",
+	1244:  "isbconference1",
+	1245:  "isbconference2",
+	1246:  "payrouter",
+	1247:  "visionpyramid",
+	1248:  "hermes",
+	1249:  "mesavistaco",
+	1250:  "swldy-sias",
+	1251:  "servergraph",
+	1252:  "bspne-pcc",
+	1253:  "q55-pcc",
+	1254:  "de-noc",
+	1255:  "de-cache-query",
+	1256:  "de-server",
+	1257:  "shockwave2",
+	1258:  "opennl",
+	1259:  "opennl-voice",
+	1260:  "ibm-ssd",
+	1261:  "mpshrsv",
+	1262:  "qnts-orb",
+	1263:  "dka",
+	1264:  "prat",
+	1265:  "dssiapi",
+	1266:  "dellpwrappks",
+	1267:  "epc",
+	1268:  "propel-msgsys",
+	1269:  "watilapp",
+	1270:  "opsmgr",
+	1271:  "excw",
+	1272:  "cspmlockmgr",
+	1273:  "emc-gateway",
+	1274:  "t1distproc",
+	1275:  "ivcollector",
+	1277:  "miva-mqs",
+	1278:  "dellwebadmin-1",
+	1279:  "dellwebadmin-2",
+	1280:  "pictrography",
+	1281:  "healthd",
+	1282:  "emperion",
+	1283:  "productinfo",
+	1284:  "iee-qfx",
+	1285:  "neoiface",
+	1286:  "netuitive",
+	1287:  "routematch",
+	1288:  "navbuddy",
+	1289:  "jwalkserver",
+	1290:  "winjaserver",
+	1291:  "seagulllms",
+	1292:  "dsdn",
+	1293:  "pkt-krb-ipsec",
+	1294:  "cmmdriver",
+	1295:  "ehtp",
+	1296:  "dproxy",
+	1297:  "sdproxy",
+	1298:  "lpcp",
+	1299:  "hp-sci",
+	1300:  "h323hostcallsc",
+	1301:  "ci3-software-1",
+	1302:  "ci3-software-2",
+	1303:  "sftsrv",
+	1304:  "boomerang",
+	1305:  "pe-mike",
+	1306:  "re-conn-proto",
+	1307:  "pacmand",
+	1308:  "odsi",
+	1309:  "jtag-server",
+	1310:  "husky",
+	1311:  "rxmon",
+	1312:  "sti-envision",
+	1313:  "bmc-patroldb",
+	1314:  "pdps",
+	1315:  "els",
+	1316:  "exbit-escp",
+	1317:  "vrts-ipcserver",
+	1318:  "krb5gatekeeper",
+	1319:  "amx-icsp",
+	1320:  "amx-axbnet",
+	1321:  "pip",
+	1322:  "novation",
+	1323:  "brcd",
+	1324:  "delta-mcp",
+	1325:  "dx-instrument",
+	1326:  "wimsic",
+	1327:  "ultrex",
+	1328:  "ewall",
+	1329:  "netdb-export",
+	1330:  "streetperfect",
+	1331:  "intersan",
+	1332:  "pcia-rxp-b",
+	1333:  "passwrd-policy",
+	1334:  "writesrv",
+	1335:  "digital-notary",
+	1336:  "ischat",
+	1337:  "menandmice-dns",
+	1338:  "wmc-log-svc",
+	1339:  "kjtsiteserver",
+	1340:  "naap",
+	1341:  "qubes",
+	1342:  "esbroker",
+	1343:  "re101",
+	1344:  "icap",
+	1345:  "vpjp",
+	1346:  "alta-ana-lm",
+	1347:  "bbn-mmc",
+	1348:  "bbn-mmx",
+	1349:  "sbook",
+	1350:  "editbench",
+	1351:  "equationbuilder",
+	1352:  "lotusnote",
+	1353:  "relief",
+	1354:  "XSIP-network",
+	1355:  "intuitive-edge",
+	1356:  "cuillamartin",
+	1357:  "pegboard",
+	1358:  "connlcli",
+	1359:  "ftsrv",
+	1360:  "mimer",
+	1361:  "linx",
+	1362:  "timeflies",
+	1363:  "ndm-requester",
+	1364:  "ndm-server",
+	1365:  "adapt-sna",
+	1366:  "netware-csp",
+	1367:  "dcs",
+	1368:  "screencast",
+	1369:  "gv-us",
+	1370:  "us-gv",
+	1371:  "fc-cli",
+	1372:  "fc-ser",
+	1373:  "chromagrafx",
+	1374:  "molly",
+	1375:  "bytex",
+	1376:  "ibm-pps",
+	1377:  "cichlid",
+	1378:  "elan",
+	1379:  "dbreporter",
+	1380:  "telesis-licman",
+	1381:  "apple-licman",
+	1382:  "udt-os",
+	1383:  "gwha",
+	1384:  "os-licman",
+	1385:  "atex-elmd",
+	1386:  "checksum",
+	1387:  "cadsi-lm",
+	1388:  "objective-dbc",
+	1389:  "iclpv-dm",
+	1390:  "iclpv-sc",
+	1391:  "iclpv-sas",
+	1392:  "iclpv-pm",
+	1393:  "iclpv-nls",
+	1394:  "iclpv-nlc",
+	1395:  "iclpv-wsm",
+	1396:  "dvl-activemail",
+	1397:  "audio-activmail",
+	1398:  "video-activmail",
+	1399:  "cadkey-licman",
+	1400:  "cadkey-tablet",
+	1401:  "goldleaf-licman",
+	1402:  "prm-sm-np",
+	1403:  "prm-nm-np",
+	1404:  "igi-lm",
+	1405:  "ibm-res",
+	1406:  "netlabs-lm",
+	1408:  "sophia-lm",
+	1409:  "here-lm",
+	1410:  "hiq",
+	1411:  "af",
+	1412:  "innosys",
+	1413:  "innosys-acl",
+	1414:  "ibm-mqseries",
+	1415:  "dbstar",
+	1416:  "novell-lu6-2",
+	1417:  "timbuktu-srv1",
+	1418:  "timbuktu-srv2",
+	1419:  "timbuktu-srv3",
+	1420:  "timbuktu-srv4",
+	1421:  "gandalf-lm",
+	1422:  "autodesk-lm",
+	1423:  "essbase",
+	1424:  "hybrid",
+	1425:  "zion-lm",
+	1426:  "sais",
+	1427:  "mloadd",
+	1428:  "informatik-lm",
+	1429:  "nms",
+	1430:  "tpdu",
+	1431:  "rgtp",
+	1432:  "blueberry-lm",
+	1433:  "ms-sql-s",
+	1434:  "ms-sql-m",
+	1435:  "ibm-cics",
+	1436:  "saism",
+	1437:  "tabula",
+	1438:  "eicon-server",
+	1439:  "eicon-x25",
+	1440:  "eicon-slp",
+	1441:  "cadis-1",
+	1442:  "cadis-2",
+	1443:  "ies-lm",
+	1444:  "marcam-lm",
+	1445:  "proxima-lm",
+	1446:  "ora-lm",
+	1447:  "apri-lm",
+	1448:  "oc-lm",
+	1449:  "peport",
+	1450:  "dwf",
+	1451:  "infoman",
+	1452:  "gtegsc-lm",
+	1453:  "genie-lm",
+	1454:  "interhdl-elmd",
+	1455:  "esl-lm",
+	1456:  "dca",
+	1457:  "valisys-lm",
+	1458:  "nrcabq-lm",
+	1459:  "proshare1",
+	1460:  "proshare2",
+	1461:  "ibm-wrless-lan",
+	1462:  "world-lm",
+	1463:  "nucleus",
+	1464:  "msl-lmd",
+	1465:  "pipes",
+	1466:  "oceansoft-lm",
+	1467:  "csdmbase",
+	1468:  "csdm",
+	1469:  "aal-lm",
+	1470:  "uaiact",
+	1471:  "csdmbase",
+	1472:  "csdm",
+	1473:  "openmath",
+	1474:  "telefinder",
+	1475:  "taligent-lm",
+	1476:  "clvm-cfg",
+	1477:  "ms-sna-server",
+	1478:  "ms-sna-base",
+	1479:  "dberegister",
+	1480:  "pacerforum",
+	1481:  "airs",
+	1482:  "miteksys-lm",
+	1483:  "afs",
+	1484:  "confluent",
+	1485:  "lansource",
+	1486:  "nms-topo-serv",
+	1487:  "localinfosrvr",
+	1488:  "docstor",
+	1489:  "dmdocbroker",
+	1490:  "insitu-conf",
+	1492:  "stone-design-1",
+	1493:  "netmap-lm",
+	1494:  "ica",
+	1495:  "cvc",
+	1496:  "liberty-lm",
+	1497:  "rfx-lm",
+	1498:  "sybase-sqlany",
+	1499:  "fhc",
+	1500:  "vlsi-lm",
+	1501:  "saiscm",
+	1502:  "shivadiscovery",
+	1503:  "imtc-mcs",
+	1504:  "evb-elm",
+	1505:  "funkproxy",
+	1506:  "utcd",
+	1507:  "symplex",
+	1508:  "diagmond",
+	1509:  "robcad-lm",
+	1510:  "mvx-lm",
+	1511:  "3l-l1",
+	1512:  "wins",
+	1513:  "fujitsu-dtc",
+	1514:  "fujitsu-dtcns",
+	1515:  "ifor-protocol",
+	1516:  "vpad",
+	1517:  "vpac",
+	1518:  "vpvd",
+	1519:  "vpvc",
+	1520:  "atm-zip-office",
+	1521:  "ncube-lm",
+	1522:  "ricardo-lm",
+	1523:  "cichild-lm",
+	1524:  "ingreslock",
+	1525:  "orasrv",
+	1526:  "pdap-np",
+	1527:  "tlisrv",
+	1528:  "ngr-t",
+	1529:  "coauthor",
+	1530:  "rap-service",
+	1531:  "rap-listen",
+	1532:  "miroconnect",
+	1533:  "virtual-places",
+	1534:  "micromuse-lm",
+	1535:  "ampr-info",
+	1536:  "ampr-inter",
+	1537:  "sdsc-lm",
+	1538:  "3ds-lm",
+	1539:  "intellistor-lm",
+	1540:  "rds",
+	1541:  "rds2",
+	1542:  "gridgen-elmd",
+	1543:  "simba-cs",
+	1544:  "aspeclmd",
+	1545:  "vistium-share",
+	1546:  "abbaccuray",
+	1547:  "laplink",
+	1548:  "axon-lm",
+	1549:  "shivasound",
+	1550:  "3m-image-lm",
+	1551:  "hecmtl-db",
+	1552:  "pciarray",
+	1553:  "sna-cs",
+	1554:  "caci-lm",
+	1555:  "livelan",
+	1556:  "veritas-pbx",
+	1557:  "arbortext-lm",
+	1558:  "xingmpeg",
+	1559:  "web2host",
+	1560:  "asci-val",
+	1561:  "facilityview",
+	1562:  "pconnectmgr",
+	1563:  "cadabra-lm",
+	1564:  "pay-per-view",
+	1565:  "winddlb",
+	1566:  "corelvideo",
+	1567:  "jlicelmd",
+	1568:  "tsspmap",
+	1569:  "ets",
+	1570:  "orbixd",
+	1571:  "rdb-dbs-disp",
+	1572:  "chip-lm",
+	1573:  "itscomm-ns",
+	1574:  "mvel-lm",
+	1575:  "oraclenames",
+	1576:  "moldflow-lm",
+	1577:  "hypercube-lm",
+	1578:  "jacobus-lm",
+	1579:  "ioc-sea-lm",
+	1580:  "tn-tl-r2",
+	1581:  "mil-2045-47001",
+	1582:  "msims",
+	1583:  "simbaexpress",
+	1584:  "tn-tl-fd2",
+	1585:  "intv",
+	1586:  "ibm-abtact",
+	1587:  "pra-elmd",
+	1588:  "triquest-lm",
+	1589:  "vqp",
+	1590:  "gemini-lm",
+	1591:  "ncpm-pm",
+	1592:  "commonspace",
+	1593:  "mainsoft-lm",
+	1594:  "sixtrak",
+	1595:  "radio",
+	1596:  "radio-bc",
+	1597:  "orbplus-iiop",
+	1598:  "picknfs",
+	1599:  "simbaservices",
+	1600:  "issd",
+	1601:  "aas",
+	1602:  "inspect",
+	1603:  "picodbc",
+	1604:  "icabrowser",
+	1605:  "slp",
+	1606:  "slm-api",
+	1607:  "stt",
+	1608:  "smart-lm",
+	1609:  "isysg-lm",
+	1610:  "taurus-wh",
+	1611:  "ill",
+	1612:  "netbill-trans",
+	1613:  "netbill-keyrep",
+	1614:  "netbill-cred",
+	1615:  "netbill-auth",
+	1616:  "netbill-prod",
+	1617:  "nimrod-agent",
+	1618:  "skytelnet",
+	1619:  "xs-openstorage",
+	1620:  "faxportwinport",
+	1621:  "softdataphone",
+	1622:  "ontime",
+	1623:  "jaleosnd",
+	1624:  "udp-sr-port",
+	1625:  "svs-omagent",
+	1626:  "shockwave",
+	1627:  "t128-gateway",
+	1628:  "lontalk-norm",
+	1629:  "lontalk-urgnt",
+	1630:  "oraclenet8cman",
+	1631:  "visitview",
+	1632:  "pammratc",
+	1633:  "pammrpc",
+	1634:  "loaprobe",
+	1635:  "edb-server1",
+	1636:  "isdc",
+	1637:  "islc",
+	1638:  "ismc",
+	1639:  "cert-initiator",
+	1640:  "cert-responder",
+	1641:  "invision",
+	1642:  "isis-am",
+	1643:  "isis-ambc",
+	1644:  "saiseh",
+	1645:  "sightline",
+	1646:  "sa-msg-port",
+	1647:  "rsap",
+	1648:  "concurrent-lm",
+	1649:  "kermit",
+	1650:  "nkd",
+	1651:  "shiva-confsrvr",
+	1652:  "xnmp",
+	1653:  "alphatech-lm",
+	1654:  "stargatealerts",
+	1655:  "dec-mbadmin",
+	1656:  "dec-mbadmin-h",
+	1657:  "fujitsu-mmpdc",
+	1658:  "sixnetudr",
+	1659:  "sg-lm",
+	1660:  "skip-mc-gikreq",
+	1661:  "netview-aix-1",
+	1662:  "netview-aix-2",
+	1663:  "netview-aix-3",
+	1664:  "netview-aix-4",
+	1665:  "netview-aix-5",
+	1666:  "netview-aix-6",
+	1667:  "netview-aix-7",
+	1668:  "netview-aix-8",
+	1669:  "netview-aix-9",
+	1670:  "netview-aix-10",
+	1671:  "netview-aix-11",
+	1672:  "netview-aix-12",
+	1673:  "proshare-mc-1",
+	1674:  "proshare-mc-2",
+	1675:  "pdp",
+	1676:  "netcomm2",
+	1677:  "groupwise",
+	1678:  "prolink",
+	1679:  "darcorp-lm",
+	1680:  "microcom-sbp",
+	1681:  "sd-elmd",
+	1682:  "lanyon-lantern",
+	1683:  "ncpm-hip",
+	1684:  "snaresecure",
+	1685:  "n2nremote",
+	1686:  "cvmon",
+	1687:  "nsjtp-ctrl",
+	1688:  "nsjtp-data",
+	1689:  "firefox",
+	1690:  "ng-umds",
+	1691:  "empire-empuma",
+	1692:  "sstsys-lm",
+	1693:  "rrirtr",
+	1694:  "rrimwm",
+	1695:  "rrilwm",
+	1696:  "rrifmm",
+	1697:  "rrisat",
+	1698:  "rsvp-encap-1",
+	1699:  "rsvp-encap-2",
+	1700:  "mps-raft",
+	1701:  "l2f",
+	1702:  "deskshare",
+	1703:  "hb-engine",
+	1704:  "bcs-broker",
+	1705:  "slingshot",
+	1706:  "jetform",
+	1707:  "vdmplay",
+	1708:  "gat-lmd",
+	1709:  "centra",
+	1710:  "impera",
+	1711:  "pptconference",
+	1712:  "registrar",
+	1713:  "conferencetalk",
+	1714:  "sesi-lm",
+	1715:  "houdini-lm",
+	1716:  "xmsg",
+	1717:  "fj-hdnet",
+	1718:  "h323gatedisc",
+	1719:  "h323gatestat",
+	1720:  "h323hostcall",
+	1721:  "caicci",
+	1722:  "hks-lm",
+	1723:  "pptp",
+	1724:  "csbphonemaster",
+	1725:  "iden-ralp",
+	1726:  "iberiagames",
+	1727:  "winddx",
+	1728:  "telindus",
+	1729:  "citynl",
+	1730:  "roketz",
+	1731:  "msiccp",
+	1732:  "proxim",
+	1733:  "siipat",
+	1734:  "cambertx-lm",
+	1735:  "privatechat",
+	1736:  "street-stream",
+	1737:  "ultimad",
+	1738:  "gamegen1",
+	1739:  "webaccess",
+	1740:  "encore",
+	1741:  "cisco-net-mgmt",
+	1742:  "3Com-nsd",
+	1743:  "cinegrfx-lm",
+	1744:  "ncpm-ft",
+	1745:  "remote-winsock",
+	1746:  "ftrapid-1",
+	1747:  "ftrapid-2",
+	1748:  "oracle-em1",
+	1749:  "aspen-services",
+	1750:  "sslp",
+	1751:  "swiftnet",
+	1752:  "lofr-lm",
+	1754:  "oracle-em2",
+	1755:  "ms-streaming",
+	1756:  "capfast-lmd",
+	1757:  "cnhrp",
+	1758:  "tftp-mcast",
+	1759:  "spss-lm",
+	1760:  "www-ldap-gw",
+	1761:  "cft-0",
+	1762:  "cft-1",
+	1763:  "cft-2",
+	1764:  "cft-3",
+	1765:  "cft-4",
+	1766:  "cft-5",
+	1767:  "cft-6",
+	1768:  "cft-7",
+	1769:  "bmc-net-adm",
+	1770:  "bmc-net-svc",
+	1771:  "vaultbase",
+	1772:  "essweb-gw",
+	1773:  "kmscontrol",
+	1774:  "global-dtserv",
+	1776:  "femis",
+	1777:  "powerguardian",
+	1778:  "prodigy-intrnet",
+	1779:  "pharmasoft",
+	1780:  "dpkeyserv",
+	1781:  "answersoft-lm",
+	1782:  "hp-hcip",
+	1784:  "finle-lm",
+	1785:  "windlm",
+	1786:  "funk-logger",
+	1787:  "funk-license",
+	1788:  "psmond",
+	1789:  "hello",
+	1790:  "nmsp",
+	1791:  "ea1",
+	1792:  "ibm-dt-2",
+	1793:  "rsc-robot",
+	1794:  "cera-bcm",
+	1795:  "dpi-proxy",
+	1796:  "vocaltec-admin",
+	1797:  "uma",
+	1798:  "etp",
+	1799:  "netrisk",
+	1800:  "ansys-lm",
+	1801:  "msmq",
+	1802:  "concomp1",
+	1803:  "hp-hcip-gwy",
+	1804:  "enl",
+	1805:  "enl-name",
+	1806:  "musiconline",
+	1807:  "fhsp",
+	1808:  "oracle-vp2",
+	1809:  "oracle-vp1",
+	1810:  "jerand-lm",
+	1811:  "scientia-sdb",
+	1812:  "radius",
+	1813:  "radius-acct",
+	1814:  "tdp-suite",
+	1815:  "mmpft",
+	1816:  "harp",
+	1817:  "rkb-oscs",
+	1818:  "etftp",
+	1819:  "plato-lm",
+	1820:  "mcagent",
+	1821:  "donnyworld",
+	1822:  "es-elmd",
+	1823:  "unisys-lm",
+	1824:  "metrics-pas",
+	1825:  "direcpc-video",
+	1826:  "ardt",
+	1827:  "asi",
+	1828:  "itm-mcell-u",
+	1829:  "optika-emedia",
+	1830:  "net8-cman",
+	1831:  "myrtle",
+	1832:  "tht-treasure",
+	1833:  "udpradio",
+	1834:  "ardusuni",
+	1835:  "ardusmul",
+	1836:  "ste-smsc",
+	1837:  "csoft1",
+	1838:  "talnet",
+	1839:  "netopia-vo1",
+	1840:  "netopia-vo2",
+	1841:  "netopia-vo3",
+	1842:  "netopia-vo4",
+	1843:  "netopia-vo5",
+	1844:  "direcpc-dll",
+	1845:  "altalink",
+	1846:  "tunstall-pnc",
+	1847:  "slp-notify",
+	1848:  "fjdocdist",
+	1849:  "alpha-sms",
+	1850:  "gsi",
+	1851:  "ctcd",
+	1852:  "virtual-time",
+	1853:  "vids-avtp",
+	1854:  "buddy-draw",
+	1855:  "fiorano-rtrsvc",
+	1856:  "fiorano-msgsvc",
+	1857:  "datacaptor",
+	1858:  "privateark",
+	1859:  "gammafetchsvr",
+	1860:  "sunscalar-svc",
+	1861:  "lecroy-vicp",
+	1862:  "mysql-cm-agent",
+	1863:  "msnp",
+	1864:  "paradym-31port",
+	1865:  "entp",
+	1866:  "swrmi",
+	1867:  "udrive",
+	1868:  "viziblebrowser",
+	1869:  "transact",
+	1870:  "sunscalar-dns",
+	1871:  "canocentral0",
+	1872:  "canocentral1",
+	1873:  "fjmpjps",
+	1874:  "fjswapsnp",
+	1875:  "westell-stats",
+	1876:  "ewcappsrv",
+	1877:  "hp-webqosdb",
+	1878:  "drmsmc",
+	1879:  "nettgain-nms",
+	1880:  "vsat-control",
+	1881:  "ibm-mqseries2",
+	1882:  "ecsqdmn",
+	1883:  "mqtt",
+	1884:  "idmaps",
+	1885:  "vrtstrapserver",
+	1886:  "leoip",
+	1887:  "filex-lport",
+	1888:  "ncconfig",
+	1889:  "unify-adapter",
+	1890:  "wilkenlistener",
+	1891:  "childkey-notif",
+	1892:  "childkey-ctrl",
+	1893:  "elad",
+	1894:  "o2server-port",
+	1896:  "b-novative-ls",
+	1897:  "metaagent",
+	1898:  "cymtec-port",
+	1899:  "mc2studios",
+	1900:  "ssdp",
+	1901:  "fjicl-tep-a",
+	1902:  "fjicl-tep-b",
+	1903:  "linkname",
+	1904:  "fjicl-tep-c",
+	1905:  "sugp",
+	1906:  "tpmd",
+	1907:  "intrastar",
+	1908:  "dawn",
+	1909:  "global-wlink",
+	1910:  "ultrabac",
+	1911:  "mtp",
+	1912:  "rhp-iibp",
+	1913:  "armadp",
+	1914:  "elm-momentum",
+	1915:  "facelink",
+	1916:  "persona",
+	1917:  "noagent",
+	1918:  "can-nds",
+	1919:  "can-dch",
+	1920:  "can-ferret",
+	1921:  "noadmin",
+	1922:  "tapestry",
+	1923:  "spice",
+	1924:  "xiip",
+	1925:  "discovery-port",
+	1926:  "egs",
+	1927:  "videte-cipc",
+	1928:  "emsd-port",
+	1929:  "bandwiz-system",
+	1930:  "driveappserver",
+	1931:  "amdsched",
+	1932:  "ctt-broker",
+	1933:  "xmapi",
+	1934:  "xaapi",
+	1935:  "macromedia-fcs",
+	1936:  "jetcmeserver",
+	1937:  "jwserver",
+	1938:  "jwclient",
+	1939:  "jvserver",
+	1940:  "jvclient",
+	1941:  "dic-aida",
+	1942:  "res",
+	1943:  "beeyond-media",
+	1944:  "close-combat",
+	1945:  "dialogic-elmd",
+	1946:  "tekpls",
+	1947:  "sentinelsrm",
+	1948:  "eye2eye",
+	1949:  "ismaeasdaqlive",
+	1950:  "ismaeasdaqtest",
+	1951:  "bcs-lmserver",
+	1952:  "mpnjsc",
+	1953:  "rapidbase",
+	1954:  "abr-api",
+	1955:  "abr-secure",
+	1956:  "vrtl-vmf-ds",
+	1957:  "unix-status",
+	1958:  "dxadmind",
+	1959:  "simp-all",
+	1960:  "nasmanager",
+	1961:  "bts-appserver",
+	1962:  "biap-mp",
+	1963:  "webmachine",
+	1964:  "solid-e-engine",
+	1965:  "tivoli-npm",
+	1966:  "slush",
+	1967:  "sns-quote",
+	1968:  "lipsinc",
+	1969:  "lipsinc1",
+	1970:  "netop-rc",
+	1971:  "netop-school",
+	1972:  "intersys-cache",
+	1973:  "dlsrap",
+	1974:  "drp",
+	1975:  "tcoflashagent",
+	1976:  "tcoregagent",
+	1977:  "tcoaddressbook",
+	1978:  "unisql",
+	1979:  "unisql-java",
+	1980:  "pearldoc-xact",
+	1981:  "p2pq",
+	1982:  "estamp",
+	1983:  "lhtp",
+	1984:  "bb",
+	1985:  "hsrp",
+	1986:  "licensedaemon",
+	1987:  "tr-rsrb-p1",
+	1988:  "tr-rsrb-p2",
+	1989:  "tr-rsrb-p3",
+	1990:  "stun-p1",
+	1991:  "stun-p2",
+	1992:  "stun-p3",
+	1993:  "snmp-tcp-port",
+	1994:  "stun-port",
+	1995:  "perf-port",
+	1996:  "tr-rsrb-port",
+	1997:  "gdp-port",
+	1998:  "x25-svc-port",
+	1999:  "tcp-id-port",
+	2000:  "cisco-sccp",
+	2001:  "wizard",
+	2002:  "globe",
+	2003:  "brutus",
+	2004:  "emce",
+	2005:  "oracle",
+	2006:  "raid-cd",
+	2007:  "raid-am",
+	2008:  "terminaldb",
+	2009:  "whosockami",
+	2010:  "pipe-server",
+	2011:  "servserv",
+	2012:  "raid-ac",
+	2013:  "raid-cd",
+	2014:  "raid-sf",
+	2015:  "raid-cs",
+	2016:  "bootserver",
+	2017:  "bootclient",
+	2018:  "rellpack",
+	2019:  "about",
+	2020:  "xinupageserver",
+	2021:  "xinuexpansion1",
+	2022:  "xinuexpansion2",
+	2023:  "xinuexpansion3",
+	2024:  "xinuexpansion4",
+	2025:  "xribs",
+	2026:  "scrabble",
+	2027:  "shadowserver",
+	2028:  "submitserver",
+	2029:  "hsrpv6",
+	2030:  "device2",
+	2031:  "mobrien-chat",
+	2032:  "blackboard",
+	2033:  "glogger",
+	2034:  "scoremgr",
+	2035:  "imsldoc",
+	2036:  "e-dpnet",
+	2037:  "applus",
+	2038:  "objectmanager",
+	2039:  "prizma",
+	2040:  "lam",
+	2041:  "interbase",
+	2042:  "isis",
+	2043:  "isis-bcast",
+	2044:  "rimsl",
+	2045:  "cdfunc",
+	2046:  "sdfunc",
+	2047:  "dls",
+	2048:  "dls-monitor",
+	2049:  "shilp",
+	2050:  "av-emb-config",
+	2051:  "epnsdp",
+	2052:  "clearvisn",
+	2053:  "lot105-ds-upd",
+	2054:  "weblogin",
+	2055:  "iop",
+	2056:  "omnisky",
+	2057:  "rich-cp",
+	2058:  "newwavesearch",
+	2059:  "bmc-messaging",
+	2060:  "teleniumdaemon",
+	2061:  "netmount",
+	2062:  "icg-swp",
+	2063:  "icg-bridge",
+	2064:  "icg-iprelay",
+	2065:  "dlsrpn",
+	2066:  "aura",
+	2067:  "dlswpn",
+	2068:  "avauthsrvprtcl",
+	2069:  "event-port",
+	2070:  "ah-esp-encap",
+	2071:  "acp-port",
+	2072:  "msync",
+	2073:  "gxs-data-port",
+	2074:  "vrtl-vmf-sa",
+	2075:  "newlixengine",
+	2076:  "newlixconfig",
+	2077:  "tsrmagt",
+	2078:  "tpcsrvr",
+	2079:  "idware-router",
+	2080:  "autodesk-nlm",
+	2081:  "kme-trap-port",
+	2082:  "infowave",
+	2083:  "radsec",
+	2084:  "sunclustergeo",
+	2085:  "ada-cip",
+	2086:  "gnunet",
+	2087:  "eli",
+	2088:  "ip-blf",
+	2089:  "sep",
+	2090:  "lrp",
+	2091:  "prp",
+	2092:  "descent3",
+	2093:  "nbx-cc",
+	2094:  "nbx-au",
+	2095:  "nbx-ser",
+	2096:  "nbx-dir",
+	2097:  "jetformpreview",
+	2098:  "dialog-port",
+	2099:  "h2250-annex-g",
+	2100:  "amiganetfs",
+	2101:  "rtcm-sc104",
+	2102:  "zephyr-srv",
+	2103:  "zephyr-clt",
+	2104:  "zephyr-hm",
+	2105:  "minipay",
+	2106:  "mzap",
+	2107:  "bintec-admin",
+	2108:  "comcam",
+	2109:  "ergolight",
+	2110:  "umsp",
+	2111:  "dsatp",
+	2112:  "idonix-metanet",
+	2113:  "hsl-storm",
+	2114:  "newheights",
+	2115:  "kdm",
+	2116:  "ccowcmr",
+	2117:  "mentaclient",
+	2118:  "mentaserver",
+	2119:  "gsigatekeeper",
+	2120:  "qencp",
+	2121:  "scientia-ssdb",
+	2122:  "caupc-remote",
+	2123:  "gtp-control",
+	2124:  "elatelink",
+	2125:  "lockstep",
+	2126:  "pktcable-cops",
+	2127:  "index-pc-wb",
+	2128:  "net-steward",
+	2129:  "cs-live",
+	2130:  "xds",
+	2131:  "avantageb2b",
+	2132:  "solera-epmap",
+	2133:  "zymed-zpp",
+	2134:  "avenue",
+	2135:  "gris",
+	2136:  "appworxsrv",
+	2137:  "connect",
+	2138:  "unbind-cluster",
+	2139:  "ias-auth",
+	2140:  "ias-reg",
+	2141:  "ias-admind",
+	2142:  "tdmoip",
+	2143:  "lv-jc",
+	2144:  "lv-ffx",
+	2145:  "lv-pici",
+	2146:  "lv-not",
+	2147:  "lv-auth",
+	2148:  "veritas-ucl",
+	2149:  "acptsys",
+	2150:  "dynamic3d",
+	2151:  "docent",
+	2152:  "gtp-user",
+	2153:  "ctlptc",
+	2154:  "stdptc",
+	2155:  "brdptc",
+	2156:  "trp",
+	2157:  "xnds",
+	2158:  "touchnetplus",
+	2159:  "gdbremote",
+	2160:  "apc-2160",
+	2161:  "apc-2161",
+	2162:  "navisphere",
+	2163:  "navisphere-sec",
+	2164:  "ddns-v3",
+	2165:  "x-bone-api",
+	2166:  "iwserver",
+	2167:  "raw-serial",
+	2168:  "easy-soft-mux",
+	2169:  "brain",
+	2170:  "eyetv",
+	2171:  "msfw-storage",
+	2172:  "msfw-s-storage",
+	2173:  "msfw-replica",
+	2174:  "msfw-array",
+	2175:  "airsync",
+	2176:  "rapi",
+	2177:  "qwave",
+	2178:  "bitspeer",
+	2179:  "vmrdp",
+	2180:  "mc-gt-srv",
+	2181:  "eforward",
+	2182:  "cgn-stat",
+	2183:  "cgn-config",
+	2184:  "nvd",
+	2185:  "onbase-dds",
+	2186:  "gtaua",
+	2187:  "ssmd",
+	2190:  "tivoconnect",
+	2191:  "tvbus",
+	2192:  "asdis",
+	2193:  "drwcs",
+	2197:  "mnp-exchange",
+	2198:  "onehome-remote",
+	2199:  "onehome-help",
+	2200:  "ici",
+	2201:  "ats",
+	2202:  "imtc-map",
+	2203:  "b2-runtime",
+	2204:  "b2-license",
+	2205:  "jps",
+	2206:  "hpocbus",
+	2207:  "hpssd",
+	2208:  "hpiod",
+	2209:  "rimf-ps",
+	2210:  "noaaport",
+	2211:  "emwin",
+	2212:  "leecoposserver",
+	2213:  "kali",
+	2214:  "rpi",
+	2215:  "ipcore",
+	2216:  "vtu-comms",
+	2217:  "gotodevice",
+	2218:  "bounzza",
+	2219:  "netiq-ncap",
+	2220:  "netiq",
+	2221:  "ethernet-ip-s",
+	2222:  "EtherNet-IP-1",
+	2223:  "rockwell-csp2",
+	2224:  "efi-mg",
+	2226:  "di-drm",
+	2227:  "di-msg",
+	2228:  "ehome-ms",
+	2229:  "datalens",
+	2230:  "queueadm",
+	2231:  "wimaxasncp",
+	2232:  "ivs-video",
+	2233:  "infocrypt",
+	2234:  "directplay",
+	2235:  "sercomm-wlink",
+	2236:  "nani",
+	2237:  "optech-port1-lm",
+	2238:  "aviva-sna",
+	2239:  "imagequery",
+	2240:  "recipe",
+	2241:  "ivsd",
+	2242:  "foliocorp",
+	2243:  "magicom",
+	2244:  "nmsserver",
+	2245:  "hao",
+	2246:  "pc-mta-addrmap",
+	2247:  "antidotemgrsvr",
+	2248:  "ums",
+	2249:  "rfmp",
+	2250:  "remote-collab",
+	2251:  "dif-port",
+	2252:  "njenet-ssl",
+	2253:  "dtv-chan-req",
+	2254:  "seispoc",
+	2255:  "vrtp",
+	2256:  "pcc-mfp",
+	2257:  "simple-tx-rx",
+	2258:  "rcts",
+	2260:  "apc-2260",
+	2261:  "comotionmaster",
+	2262:  "comotionback",
+	2263:  "ecwcfg",
+	2264:  "apx500api-1",
+	2265:  "apx500api-2",
+	2266:  "mfserver",
+	2267:  "ontobroker",
+	2268:  "amt",
+	2269:  "mikey",
+	2270:  "starschool",
+	2271:  "mmcals",
+	2272:  "mmcal",
+	2273:  "mysql-im",
+	2274:  "pcttunnell",
+	2275:  "ibridge-data",
+	2276:  "ibridge-mgmt",
+	2277:  "bluectrlproxy",
+	2278:  "s3db",
+	2279:  "xmquery",
+	2280:  "lnvpoller",
+	2281:  "lnvconsole",
+	2282:  "lnvalarm",
+	2283:  "lnvstatus",
+	2284:  "lnvmaps",
+	2285:  "lnvmailmon",
+	2286:  "nas-metering",
+	2287:  "dna",
+	2288:  "netml",
+	2289:  "dict-lookup",
+	2290:  "sonus-logging",
+	2291:  "eapsp",
+	2292:  "mib-streaming",
+	2293:  "npdbgmngr",
+	2294:  "konshus-lm",
+	2295:  "advant-lm",
+	2296:  "theta-lm",
+	2297:  "d2k-datamover1",
+	2298:  "d2k-datamover2",
+	2299:  "pc-telecommute",
+	2300:  "cvmmon",
+	2301:  "cpq-wbem",
+	2302:  "binderysupport",
+	2303:  "proxy-gateway",
+	2304:  "attachmate-uts",
+	2305:  "mt-scaleserver",
+	2306:  "tappi-boxnet",
+	2307:  "pehelp",
+	2308:  "sdhelp",
+	2309:  "sdserver",
+	2310:  "sdclient",
+	2311:  "messageservice",
+	2312:  "wanscaler",
+	2313:  "iapp",
+	2314:  "cr-websystems",
+	2315:  "precise-sft",
+	2316:  "sent-lm",
+	2317:  "attachmate-g32",
+	2318:  "cadencecontrol",
+	2319:  "infolibria",
+	2320:  "siebel-ns",
+	2321:  "rdlap",
+	2322:  "ofsd",
+	2323:  "3d-nfsd",
+	2324:  "cosmocall",
+	2325:  "ansysli",
+	2326:  "idcp",
+	2327:  "xingcsm",
+	2328:  "netrix-sftm",
+	2329:  "nvd",
+	2330:  "tscchat",
+	2331:  "agentview",
+	2332:  "rcc-host",
+	2333:  "snapp",
+	2334:  "ace-client",
+	2335:  "ace-proxy",
+	2336:  "appleugcontrol",
+	2337:  "ideesrv",
+	2338:  "norton-lambert",
+	2339:  "3com-webview",
+	2340:  "wrs-registry",
+	2341:  "xiostatus",
+	2342:  "manage-exec",
+	2343:  "nati-logos",
+	2344:  "fcmsys",
+	2345:  "dbm",
+	2346:  "redstorm-join",
+	2347:  "redstorm-find",
+	2348:  "redstorm-info",
+	2349:  "redstorm-diag",
+	2350:  "psbserver",
+	2351:  "psrserver",
+	2352:  "pslserver",
+	2353:  "pspserver",
+	2354:  "psprserver",
+	2355:  "psdbserver",
+	2356:  "gxtelmd",
+	2357:  "unihub-server",
+	2358:  "futrix",
+	2359:  "flukeserver",
+	2360:  "nexstorindltd",
+	2361:  "tl1",
+	2362:  "digiman",
+	2363:  "mediacntrlnfsd",
+	2364:  "oi-2000",
+	2365:  "dbref",
+	2366:  "qip-login",
+	2367:  "service-ctrl",
+	2368:  "opentable",
+	2370:  "l3-hbmon",
+	2372:  "lanmessenger",
+	2381:  "compaq-https",
+	2382:  "ms-olap3",
+	2383:  "ms-olap4",
+	2384:  "sd-capacity",
+	2385:  "sd-data",
+	2386:  "virtualtape",
+	2387:  "vsamredirector",
+	2388:  "mynahautostart",
+	2389:  "ovsessionmgr",
+	2390:  "rsmtp",
+	2391:  "3com-net-mgmt",
+	2392:  "tacticalauth",
+	2393:  "ms-olap1",
+	2394:  "ms-olap2",
+	2395:  "lan900-remote",
+	2396:  "wusage",
+	2397:  "ncl",
+	2398:  "orbiter",
+	2399:  "fmpro-fdal",
+	2400:  "opequus-server",
+	2401:  "cvspserver",
+	2402:  "taskmaster2000",
+	2403:  "taskmaster2000",
+	2404:  "iec-104",
+	2405:  "trc-netpoll",
+	2406:  "jediserver",
+	2407:  "orion",
+	2409:  "sns-protocol",
+	2410:  "vrts-registry",
+	2411:  "netwave-ap-mgmt",
+	2412:  "cdn",
+	2413:  "orion-rmi-reg",
+	2414:  "beeyond",
+	2415:  "codima-rtp",
+	2416:  "rmtserver",
+	2417:  "composit-server",
+	2418:  "cas",
+	2419:  "attachmate-s2s",
+	2420:  "dslremote-mgmt",
+	2421:  "g-talk",
+	2422:  "crmsbits",
+	2423:  "rnrp",
+	2424:  "kofax-svr",
+	2425:  "fjitsuappmgr",
+	2426:  "vcmp",
+	2427:  "mgcp-gateway",
+	2428:  "ott",
+	2429:  "ft-role",
+	2430:  "venus",
+	2431:  "venus-se",
+	2432:  "codasrv",
+	2433:  "codasrv-se",
+	2434:  "pxc-epmap",
+	2435:  "optilogic",
+	2436:  "topx",
+	2437:  "unicontrol",
+	2438:  "msp",
+	2439:  "sybasedbsynch",
+	2440:  "spearway",
+	2441:  "pvsw-inet",
+	2442:  "netangel",
+	2443:  "powerclientcsf",
+	2444:  "btpp2sectrans",
+	2445:  "dtn1",
+	2446:  "bues-service",
+	2447:  "ovwdb",
+	2448:  "hpppssvr",
+	2449:  "ratl",
+	2450:  "netadmin",
+	2451:  "netchat",
+	2452:  "snifferclient",
+	2453:  "madge-ltd",
+	2454:  "indx-dds",
+	2455:  "wago-io-system",
+	2456:  "altav-remmgt",
+	2457:  "rapido-ip",
+	2458:  "griffin",
+	2459:  "community",
+	2460:  "ms-theater",
+	2461:  "qadmifoper",
+	2462:  "qadmifevent",
+	2463:  "lsi-raid-mgmt",
+	2464:  "direcpc-si",
+	2465:  "lbm",
+	2466:  "lbf",
+	2467:  "high-criteria",
+	2468:  "qip-msgd",
+	2469:  "mti-tcs-comm",
+	2470:  "taskman-port",
+	2471:  "seaodbc",
+	2472:  "c3",
+	2473:  "aker-cdp",
+	2474:  "vitalanalysis",
+	2475:  "ace-server",
+	2476:  "ace-svr-prop",
+	2477:  "ssm-cvs",
+	2478:  "ssm-cssps",
+	2479:  "ssm-els",
+	2480:  "powerexchange",
+	2481:  "giop",
+	2482:  "giop-ssl",
+	2483:  "ttc",
+	2484:  "ttc-ssl",
+	2485:  "netobjects1",
+	2486:  "netobjects2",
+	2487:  "pns",
+	2488:  "moy-corp",
+	2489:  "tsilb",
+	2490:  "qip-qdhcp",
+	2491:  "conclave-cpp",
+	2492:  "groove",
+	2493:  "talarian-mqs",
+	2494:  "bmc-ar",
+	2495:  "fast-rem-serv",
+	2496:  "dirgis",
+	2497:  "quaddb",
+	2498:  "odn-castraq",
+	2499:  "unicontrol",
+	2500:  "rtsserv",
+	2501:  "rtsclient",
+	2502:  "kentrox-prot",
+	2503:  "nms-dpnss",
+	2504:  "wlbs",
+	2505:  "ppcontrol",
+	2506:  "jbroker",
+	2507:  "spock",
+	2508:  "jdatastore",
+	2509:  "fjmpss",
+	2510:  "fjappmgrbulk",
+	2511:  "metastorm",
+	2512:  "citrixima",
+	2513:  "citrixadmin",
+	2514:  "facsys-ntp",
+	2515:  "facsys-router",
+	2516:  "maincontrol",
+	2517:  "call-sig-trans",
+	2518:  "willy",
+	2519:  "globmsgsvc",
+	2520:  "pvsw",
+	2521:  "adaptecmgr",
+	2522:  "windb",
+	2523:  "qke-llc-v3",
+	2524:  "optiwave-lm",
+	2525:  "ms-v-worlds",
+	2526:  "ema-sent-lm",
+	2527:  "iqserver",
+	2528:  "ncr-ccl",
+	2529:  "utsftp",
+	2530:  "vrcommerce",
+	2531:  "ito-e-gui",
+	2532:  "ovtopmd",
+	2533:  "snifferserver",
+	2534:  "combox-web-acc",
+	2535:  "madcap",
+	2536:  "btpp2audctr1",
+	2537:  "upgrade",
+	2538:  "vnwk-prapi",
+	2539:  "vsiadmin",
+	2540:  "lonworks",
+	2541:  "lonworks2",
+	2542:  "udrawgraph",
+	2543:  "reftek",
+	2544:  "novell-zen",
+	2545:  "sis-emt",
+	2546:  "vytalvaultbrtp",
+	2547:  "vytalvaultvsmp",
+	2548:  "vytalvaultpipe",
+	2549:  "ipass",
+	2550:  "ads",
+	2551:  "isg-uda-server",
+	2552:  "call-logging",
+	2553:  "efidiningport",
+	2554:  "vcnet-link-v10",
+	2555:  "compaq-wcp",
+	2556:  "nicetec-nmsvc",
+	2557:  "nicetec-mgmt",
+	2558:  "pclemultimedia",
+	2559:  "lstp",
+	2560:  "labrat",
+	2561:  "mosaixcc",
+	2562:  "delibo",
+	2563:  "cti-redwood",
+	2564:  "hp-3000-telnet",
+	2565:  "coord-svr",
+	2566:  "pcs-pcw",
+	2567:  "clp",
+	2568:  "spamtrap",
+	2569:  "sonuscallsig",
+	2570:  "hs-port",
+	2571:  "cecsvc",
+	2572:  "ibp",
+	2573:  "trustestablish",
+	2574:  "blockade-bpsp",
+	2575:  "hl7",
+	2576:  "tclprodebugger",
+	2577:  "scipticslsrvr",
+	2578:  "rvs-isdn-dcp",
+	2579:  "mpfoncl",
+	2580:  "tributary",
+	2581:  "argis-te",
+	2582:  "argis-ds",
+	2583:  "mon",
+	2584:  "cyaserv",
+	2585:  "netx-server",
+	2586:  "netx-agent",
+	2587:  "masc",
+	2588:  "privilege",
+	2589:  "quartus-tcl",
+	2590:  "idotdist",
+	2591:  "maytagshuffle",
+	2592:  "netrek",
+	2593:  "mns-mail",
+	2594:  "dts",
+	2595:  "worldfusion1",
+	2596:  "worldfusion2",
+	2597:  "homesteadglory",
+	2598:  "citriximaclient",
+	2599:  "snapd",
+	2600:  "hpstgmgr",
+	2601:  "discp-client",
+	2602:  "discp-server",
+	2603:  "servicemeter",
+	2604:  "nsc-ccs",
+	2605:  "nsc-posa",
+	2606:  "netmon",
+	2607:  "connection",
+	2608:  "wag-service",
+	2609:  "system-monitor",
+	2610:  "versa-tek",
+	2611:  "lionhead",
+	2612:  "qpasa-agent",
+	2613:  "smntubootstrap",
+	2614:  "neveroffline",
+	2615:  "firepower",
+	2616:  "appswitch-emp",
+	2617:  "cmadmin",
+	2618:  "priority-e-com",
+	2619:  "bruce",
+	2620:  "lpsrecommender",
+	2621:  "miles-apart",
+	2622:  "metricadbc",
+	2623:  "lmdp",
+	2624:  "aria",
+	2625:  "blwnkl-port",
+	2626:  "gbjd816",
+	2627:  "moshebeeri",
+	2628:  "dict",
+	2629:  "sitaraserver",
+	2630:  "sitaramgmt",
+	2631:  "sitaradir",
+	2632:  "irdg-post",
+	2633:  "interintelli",
+	2634:  "pk-electronics",
+	2635:  "backburner",
+	2636:  "solve",
+	2637:  "imdocsvc",
+	2638:  "sybaseanywhere",
+	2639:  "aminet",
+	2640:  "ami-control",
+	2641:  "hdl-srv",
+	2642:  "tragic",
+	2643:  "gte-samp",
+	2644:  "travsoft-ipx-t",
+	2645:  "novell-ipx-cmd",
+	2646:  "and-lm",
+	2647:  "syncserver",
+	2648:  "upsnotifyprot",
+	2649:  "vpsipport",
+	2650:  "eristwoguns",
+	2651:  "ebinsite",
+	2652:  "interpathpanel",
+	2653:  "sonus",
+	2654:  "corel-vncadmin",
+	2655:  "unglue",
+	2656:  "kana",
+	2657:  "sns-dispatcher",
+	2658:  "sns-admin",
+	2659:  "sns-query",
+	2660:  "gcmonitor",
+	2661:  "olhost",
+	2662:  "bintec-capi",
+	2663:  "bintec-tapi",
+	2664:  "patrol-mq-gm",
+	2665:  "patrol-mq-nm",
+	2666:  "extensis",
+	2667:  "alarm-clock-s",
+	2668:  "alarm-clock-c",
+	2669:  "toad",
+	2670:  "tve-announce",
+	2671:  "newlixreg",
+	2672:  "nhserver",
+	2673:  "firstcall42",
+	2674:  "ewnn",
+	2675:  "ttc-etap",
+	2676:  "simslink",
+	2677:  "gadgetgate1way",
+	2678:  "gadgetgate2way",
+	2679:  "syncserverssl",
+	2680:  "pxc-sapxom",
+	2681:  "mpnjsomb",
+	2683:  "ncdloadbalance",
+	2684:  "mpnjsosv",
+	2685:  "mpnjsocl",
+	2686:  "mpnjsomg",
+	2687:  "pq-lic-mgmt",
+	2688:  "md-cg-http",
+	2689:  "fastlynx",
+	2690:  "hp-nnm-data",
+	2691:  "itinternet",
+	2692:  "admins-lms",
+	2694:  "pwrsevent",
+	2695:  "vspread",
+	2696:  "unifyadmin",
+	2697:  "oce-snmp-trap",
+	2698:  "mck-ivpip",
+	2699:  "csoft-plusclnt",
+	2700:  "tqdata",
+	2701:  "sms-rcinfo",
+	2702:  "sms-xfer",
+	2703:  "sms-chat",
+	2704:  "sms-remctrl",
+	2705:  "sds-admin",
+	2706:  "ncdmirroring",
+	2707:  "emcsymapiport",
+	2708:  "banyan-net",
+	2709:  "supermon",
+	2710:  "sso-service",
+	2711:  "sso-control",
+	2712:  "aocp",
+	2713:  "raventbs",
+	2714:  "raventdm",
+	2715:  "hpstgmgr2",
+	2716:  "inova-ip-disco",
+	2717:  "pn-requester",
+	2718:  "pn-requester2",
+	2719:  "scan-change",
+	2720:  "wkars",
+	2721:  "smart-diagnose",
+	2722:  "proactivesrvr",
+	2723:  "watchdog-nt",
+	2724:  "qotps",
+	2725:  "msolap-ptp2",
+	2726:  "tams",
+	2727:  "mgcp-callagent",
+	2728:  "sqdr",
+	2729:  "tcim-control",
+	2730:  "nec-raidplus",
+	2731:  "fyre-messanger",
+	2732:  "g5m",
+	2733:  "signet-ctf",
+	2734:  "ccs-software",
+	2735:  "netiq-mc",
+	2736:  "radwiz-nms-srv",
+	2737:  "srp-feedback",
+	2738:  "ndl-tcp-ois-gw",
+	2739:  "tn-timing",
+	2740:  "alarm",
+	2741:  "tsb",
+	2742:  "tsb2",
+	2743:  "murx",
+	2744:  "honyaku",
+	2745:  "urbisnet",
+	2746:  "cpudpencap",
+	2747:  "fjippol-swrly",
+	2748:  "fjippol-polsvr",
+	2749:  "fjippol-cnsl",
+	2750:  "fjippol-port1",
+	2751:  "fjippol-port2",
+	2752:  "rsisysaccess",
+	2753:  "de-spot",
+	2754:  "apollo-cc",
+	2755:  "expresspay",
+	2756:  "simplement-tie",
+	2757:  "cnrp",
+	2758:  "apollo-status",
+	2759:  "apollo-gms",
+	2760:  "sabams",
+	2761:  "dicom-iscl",
+	2762:  "dicom-tls",
+	2763:  "desktop-dna",
+	2764:  "data-insurance",
+	2765:  "qip-audup",
+	2766:  "compaq-scp",
+	2767:  "uadtc",
+	2768:  "uacs",
+	2769:  "exce",
+	2770:  "veronica",
+	2771:  "vergencecm",
+	2772:  "auris",
+	2773:  "rbakcup1",
+	2774:  "rbakcup2",
+	2775:  "smpp",
+	2776:  "ridgeway1",
+	2777:  "ridgeway2",
+	2778:  "gwen-sonya",
+	2779:  "lbc-sync",
+	2780:  "lbc-control",
+	2781:  "whosells",
+	2782:  "everydayrc",
+	2783:  "aises",
+	2784:  "www-dev",
+	2785:  "aic-np",
+	2786:  "aic-oncrpc",
+	2787:  "piccolo",
+	2788:  "fryeserv",
+	2789:  "media-agent",
+	2790:  "plgproxy",
+	2791:  "mtport-regist",
+	2792:  "f5-globalsite",
+	2793:  "initlsmsad",
+	2795:  "livestats",
+	2796:  "ac-tech",
+	2797:  "esp-encap",
+	2798:  "tmesis-upshot",
+	2799:  "icon-discover",
+	2800:  "acc-raid",
+	2801:  "igcp",
+	2802:  "veritas-udp1",
+	2803:  "btprjctrl",
+	2804:  "dvr-esm",
+	2805:  "wta-wsp-s",
+	2806:  "cspuni",
+	2807:  "cspmulti",
+	2808:  "j-lan-p",
+	2809:  "corbaloc",
+	2810:  "netsteward",
+	2811:  "gsiftp",
+	2812:  "atmtcp",
+	2813:  "llm-pass",
+	2814:  "llm-csv",
+	2815:  "lbc-measure",
+	2816:  "lbc-watchdog",
+	2817:  "nmsigport",
+	2818:  "rmlnk",
+	2819:  "fc-faultnotify",
+	2820:  "univision",
+	2821:  "vrts-at-port",
+	2822:  "ka0wuc",
+	2823:  "cqg-netlan",
+	2824:  "cqg-netlan-1",
+	2826:  "slc-systemlog",
+	2827:  "slc-ctrlrloops",
+	2828:  "itm-lm",
+	2829:  "silkp1",
+	2830:  "silkp2",
+	2831:  "silkp3",
+	2832:  "silkp4",
+	2833:  "glishd",
+	2834:  "evtp",
+	2835:  "evtp-data",
+	2836:  "catalyst",
+	2837:  "repliweb",
+	2838:  "starbot",
+	2839:  "nmsigport",
+	2840:  "l3-exprt",
+	2841:  "l3-ranger",
+	2842:  "l3-hawk",
+	2843:  "pdnet",
+	2844:  "bpcp-poll",
+	2845:  "bpcp-trap",
+	2846:  "aimpp-hello",
+	2847:  "aimpp-port-req",
+	2848:  "amt-blc-port",
+	2849:  "fxp",
+	2850:  "metaconsole",
+	2851:  "webemshttp",
+	2852:  "bears-01",
+	2853:  "ispipes",
+	2854:  "infomover",
+	2856:  "cesdinv",
+	2857:  "simctlp",
+	2858:  "ecnp",
+	2859:  "activememory",
+	2860:  "dialpad-voice1",
+	2861:  "dialpad-voice2",
+	2862:  "ttg-protocol",
+	2863:  "sonardata",
+	2864:  "astromed-main",
+	2865:  "pit-vpn",
+	2866:  "iwlistener",
+	2867:  "esps-portal",
+	2868:  "npep-messaging",
+	2869:  "icslap",
+	2870:  "daishi",
+	2871:  "msi-selectplay",
+	2872:  "radix",
+	2874:  "dxmessagebase1",
+	2875:  "dxmessagebase2",
+	2876:  "sps-tunnel",
+	2877:  "bluelance",
+	2878:  "aap",
+	2879:  "ucentric-ds",
+	2880:  "synapse",
+	2881:  "ndsp",
+	2882:  "ndtp",
+	2883:  "ndnp",
+	2884:  "flashmsg",
+	2885:  "topflow",
+	2886:  "responselogic",
+	2887:  "aironetddp",
+	2888:  "spcsdlobby",
+	2889:  "rsom",
+	2890:  "cspclmulti",
+	2891:  "cinegrfx-elmd",
+	2892:  "snifferdata",
+	2893:  "vseconnector",
+	2894:  "abacus-remote",
+	2895:  "natuslink",
+	2896:  "ecovisiong6-1",
+	2897:  "citrix-rtmp",
+	2898:  "appliance-cfg",
+	2899:  "powergemplus",
+	2900:  "quicksuite",
+	2901:  "allstorcns",
+	2902:  "netaspi",
+	2903:  "suitcase",
+	2904:  "m2ua",
+	2906:  "caller9",
+	2907:  "webmethods-b2b",
+	2908:  "mao",
+	2909:  "funk-dialout",
+	2910:  "tdaccess",
+	2911:  "blockade",
+	2912:  "epicon",
+	2913:  "boosterware",
+	2914:  "gamelobby",
+	2915:  "tksocket",
+	2916:  "elvin-server",
+	2917:  "elvin-client",
+	2918:  "kastenchasepad",
+	2919:  "roboer",
+	2920:  "roboeda",
+	2921:  "cesdcdman",
+	2922:  "cesdcdtrn",
+	2923:  "wta-wsp-wtp-s",
+	2924:  "precise-vip",
+	2926:  "mobile-file-dl",
+	2927:  "unimobilectrl",
+	2928:  "redstone-cpss",
+	2929:  "amx-webadmin",
+	2930:  "amx-weblinx",
+	2931:  "circle-x",
+	2932:  "incp",
+	2933:  "4-tieropmgw",
+	2934:  "4-tieropmcli",
+	2935:  "qtp",
+	2936:  "otpatch",
+	2937:  "pnaconsult-lm",
+	2938:  "sm-pas-1",
+	2939:  "sm-pas-2",
+	2940:  "sm-pas-3",
+	2941:  "sm-pas-4",
+	2942:  "sm-pas-5",
+	2943:  "ttnrepository",
+	2944:  "megaco-h248",
+	2945:  "h248-binary",
+	2946:  "fjsvmpor",
+	2947:  "gpsd",
+	2948:  "wap-push",
+	2949:  "wap-pushsecure",
+	2950:  "esip",
+	2951:  "ottp",
+	2952:  "mpfwsas",
+	2953:  "ovalarmsrv",
+	2954:  "ovalarmsrv-cmd",
+	2955:  "csnotify",
+	2956:  "ovrimosdbman",
+	2957:  "jmact5",
+	2958:  "jmact6",
+	2959:  "rmopagt",
+	2960:  "dfoxserver",
+	2961:  "boldsoft-lm",
+	2962:  "iph-policy-cli",
+	2963:  "iph-policy-adm",
+	2964:  "bullant-srap",
+	2965:  "bullant-rap",
+	2966:  "idp-infotrieve",
+	2967:  "ssc-agent",
+	2968:  "enpp",
+	2969:  "essp",
+	2970:  "index-net",
+	2971:  "netclip",
+	2972:  "pmsm-webrctl",
+	2973:  "svnetworks",
+	2974:  "signal",
+	2975:  "fjmpcm",
+	2976:  "cns-srv-port",
+	2977:  "ttc-etap-ns",
+	2978:  "ttc-etap-ds",
+	2979:  "h263-video",
+	2980:  "wimd",
+	2981:  "mylxamport",
+	2982:  "iwb-whiteboard",
+	2983:  "netplan",
+	2984:  "hpidsadmin",
+	2985:  "hpidsagent",
+	2986:  "stonefalls",
+	2987:  "identify",
+	2988:  "hippad",
+	2989:  "zarkov",
+	2990:  "boscap",
+	2991:  "wkstn-mon",
+	2992:  "avenyo",
+	2993:  "veritas-vis1",
+	2994:  "veritas-vis2",
+	2995:  "idrs",
+	2996:  "vsixml",
+	2997:  "rebol",
+	2998:  "realsecure",
+	2999:  "remoteware-un",
+	3000:  "hbci",
+	3002:  "exlm-agent",
+	3003:  "cgms",
+	3004:  "csoftragent",
+	3005:  "geniuslm",
+	3006:  "ii-admin",
+	3007:  "lotusmtap",
+	3008:  "midnight-tech",
+	3009:  "pxc-ntfy",
+	3010:  "ping-pong",
+	3011:  "trusted-web",
+	3012:  "twsdss",
+	3013:  "gilatskysurfer",
+	3014:  "broker-service",
+	3015:  "nati-dstp",
+	3016:  "notify-srvr",
+	3017:  "event-listener",
+	3018:  "srvc-registry",
+	3019:  "resource-mgr",
+	3020:  "cifs",
+	3021:  "agriserver",
+	3022:  "csregagent",
+	3023:  "magicnotes",
+	3024:  "nds-sso",
+	3025:  "arepa-raft",
+	3026:  "agri-gateway",
+	3027:  "LiebDevMgmt-C",
+	3028:  "LiebDevMgmt-DM",
+	3029:  "LiebDevMgmt-A",
+	3030:  "arepa-cas",
+	3031:  "eppc",
+	3032:  "redwood-chat",
+	3033:  "pdb",
+	3034:  "osmosis-aeea",
+	3035:  "fjsv-gssagt",
+	3036:  "hagel-dump",
+	3037:  "hp-san-mgmt",
+	3038:  "santak-ups",
+	3039:  "cogitate",
+	3040:  "tomato-springs",
+	3041:  "di-traceware",
+	3042:  "journee",
+	3043:  "brp",
+	3044:  "epp",
+	3045:  "responsenet",
+	3046:  "di-ase",
+	3047:  "hlserver",
+	3048:  "pctrader",
+	3049:  "nsws",
+	3050:  "gds-db",
+	3051:  "galaxy-server",
+	3052:  "apc-3052",
+	3053:  "dsom-server",
+	3054:  "amt-cnf-prot",
+	3055:  "policyserver",
+	3056:  "cdl-server",
+	3057:  "goahead-fldup",
+	3058:  "videobeans",
+	3059:  "qsoft",
+	3060:  "interserver",
+	3061:  "cautcpd",
+	3062:  "ncacn-ip-tcp",
+	3063:  "ncadg-ip-udp",
+	3064:  "rprt",
+	3065:  "slinterbase",
+	3066:  "netattachsdmp",
+	3067:  "fjhpjp",
+	3068:  "ls3bcast",
+	3069:  "ls3",
+	3070:  "mgxswitch",
+	3072:  "csd-monitor",
+	3073:  "vcrp",
+	3074:  "xbox",
+	3075:  "orbix-locator",
+	3076:  "orbix-config",
+	3077:  "orbix-loc-ssl",
+	3078:  "orbix-cfg-ssl",
+	3079:  "lv-frontpanel",
+	3080:  "stm-pproc",
+	3081:  "tl1-lv",
+	3082:  "tl1-raw",
+	3083:  "tl1-telnet",
+	3084:  "itm-mccs",
+	3085:  "pcihreq",
+	3086:  "jdl-dbkitchen",
+	3087:  "asoki-sma",
+	3088:  "xdtp",
+	3089:  "ptk-alink",
+	3090:  "stss",
+	3091:  "1ci-smcs",
+	3093:  "rapidmq-center",
+	3094:  "rapidmq-reg",
+	3095:  "panasas",
+	3096:  "ndl-aps",
+	3098:  "umm-port",
+	3099:  "chmd",
+	3100:  "opcon-xps",
+	3101:  "hp-pxpib",
+	3102:  "slslavemon",
+	3103:  "autocuesmi",
+	3104:  "autocuetime",
+	3105:  "cardbox",
+	3106:  "cardbox-http",
+	3107:  "business",
+	3108:  "geolocate",
+	3109:  "personnel",
+	3110:  "sim-control",
+	3111:  "wsynch",
+	3112:  "ksysguard",
+	3113:  "cs-auth-svr",
+	3114:  "ccmad",
+	3115:  "mctet-master",
+	3116:  "mctet-gateway",
+	3117:  "mctet-jserv",
+	3118:  "pkagent",
+	3119:  "d2000kernel",
+	3120:  "d2000webserver",
+	3122:  "vtr-emulator",
+	3123:  "edix",
+	3124:  "beacon-port",
+	3125:  "a13-an",
+	3127:  "ctx-bridge",
+	3128:  "ndl-aas",
+	3129:  "netport-id",
+	3130:  "icpv2",
+	3131:  "netbookmark",
+	3132:  "ms-rule-engine",
+	3133:  "prism-deploy",
+	3134:  "ecp",
+	3135:  "peerbook-port",
+	3136:  "grubd",
+	3137:  "rtnt-1",
+	3138:  "rtnt-2",
+	3139:  "incognitorv",
+	3140:  "ariliamulti",
+	3141:  "vmodem",
+	3142:  "rdc-wh-eos",
+	3143:  "seaview",
+	3144:  "tarantella",
+	3145:  "csi-lfap",
+	3146:  "bears-02",
+	3147:  "rfio",
+	3148:  "nm-game-admin",
+	3149:  "nm-game-server",
+	3150:  "nm-asses-admin",
+	3151:  "nm-assessor",
+	3152:  "feitianrockey",
+	3153:  "s8-client-port",
+	3154:  "ccmrmi",
+	3155:  "jpegmpeg",
+	3156:  "indura",
+	3157:  "e3consultants",
+	3158:  "stvp",
+	3159:  "navegaweb-port",
+	3160:  "tip-app-server",
+	3161:  "doc1lm",
+	3162:  "sflm",
+	3163:  "res-sap",
+	3164:  "imprs",
+	3165:  "newgenpay",
+	3166:  "sossecollector",
+	3167:  "nowcontact",
+	3168:  "poweronnud",
+	3169:  "serverview-as",
+	3170:  "serverview-asn",
+	3171:  "serverview-gf",
+	3172:  "serverview-rm",
+	3173:  "serverview-icc",
+	3174:  "armi-server",
+	3175:  "t1-e1-over-ip",
+	3176:  "ars-master",
+	3177:  "phonex-port",
+	3178:  "radclientport",
+	3179:  "h2gf-w-2m",
+	3180:  "mc-brk-srv",
+	3181:  "bmcpatrolagent",
+	3182:  "bmcpatrolrnvu",
+	3183:  "cops-tls",
+	3184:  "apogeex-port",
+	3185:  "smpppd",
+	3186:  "iiw-port",
+	3187:  "odi-port",
+	3188:  "brcm-comm-port",
+	3189:  "pcle-infex",
+	3190:  "csvr-proxy",
+	3191:  "csvr-sslproxy",
+	3192:  "firemonrcc",
+	3193:  "spandataport",
+	3194:  "magbind",
+	3195:  "ncu-1",
+	3196:  "ncu-2",
+	3197:  "embrace-dp-s",
+	3198:  "embrace-dp-c",
+	3199:  "dmod-workspace",
+	3200:  "tick-port",
+	3201:  "cpq-tasksmart",
+	3202:  "intraintra",
+	3203:  "netwatcher-mon",
+	3204:  "netwatcher-db",
+	3205:  "isns",
+	3206:  "ironmail",
+	3207:  "vx-auth-port",
+	3208:  "pfu-prcallback",
+	3209:  "netwkpathengine",
+	3210:  "flamenco-proxy",
+	3211:  "avsecuremgmt",
+	3212:  "surveyinst",
+	3213:  "neon24x7",
+	3214:  "jmq-daemon-1",
+	3215:  "jmq-daemon-2",
+	3216:  "ferrari-foam",
+	3217:  "unite",
+	3218:  "smartpackets",
+	3219:  "wms-messenger",
+	3220:  "xnm-ssl",
+	3221:  "xnm-clear-text",
+	3222:  "glbp",
+	3223:  "digivote",
+	3224:  "aes-discovery",
+	3225:  "fcip-port",
+	3226:  "isi-irp",
+	3227:  "dwnmshttp",
+	3228:  "dwmsgserver",
+	3229:  "global-cd-port",
+	3230:  "sftdst-port",
+	3231:  "vidigo",
+	3232:  "mdtp",
+	3233:  "whisker",
+	3234:  "alchemy",
+	3235:  "mdap-port",
+	3236:  "apparenet-ts",
+	3237:  "apparenet-tps",
+	3238:  "apparenet-as",
+	3239:  "apparenet-ui",
+	3240:  "triomotion",
+	3241:  "sysorb",
+	3242:  "sdp-id-port",
+	3243:  "timelot",
+	3244:  "onesaf",
+	3245:  "vieo-fe",
+	3246:  "dvt-system",
+	3247:  "dvt-data",
+	3248:  "procos-lm",
+	3249:  "ssp",
+	3250:  "hicp",
+	3251:  "sysscanner",
+	3252:  "dhe",
+	3253:  "pda-data",
+	3254:  "pda-sys",
+	3255:  "semaphore",
+	3256:  "cpqrpm-agent",
+	3257:  "cpqrpm-server",
+	3258:  "ivecon-port",
+	3259:  "epncdp2",
+	3260:  "iscsi-target",
+	3261:  "winshadow",
+	3262:  "necp",
+	3263:  "ecolor-imager",
+	3264:  "ccmail",
+	3265:  "altav-tunnel",
+	3266:  "ns-cfg-server",
+	3267:  "ibm-dial-out",
+	3268:  "msft-gc",
+	3269:  "msft-gc-ssl",
+	3270:  "verismart",
+	3271:  "csoft-prev",
+	3272:  "user-manager",
+	3273:  "sxmp",
+	3274:  "ordinox-server",
+	3275:  "samd",
+	3276:  "maxim-asics",
+	3277:  "awg-proxy",
+	3278:  "lkcmserver",
+	3279:  "admind",
+	3280:  "vs-server",
+	3281:  "sysopt",
+	3282:  "datusorb",
+	3283:  "Apple Remote Desktop (Net Assistant)",
+	3284:  "4talk",
+	3285:  "plato",
+	3286:  "e-net",
+	3287:  "directvdata",
+	3288:  "cops",
+	3289:  "enpc",
+	3290:  "caps-lm",
+	3291:  "sah-lm",
+	3292:  "cart-o-rama",
+	3293:  "fg-fps",
+	3294:  "fg-gip",
+	3295:  "dyniplookup",
+	3296:  "rib-slm",
+	3297:  "cytel-lm",
+	3298:  "deskview",
+	3299:  "pdrncs",
+	3302:  "mcs-fastmail",
+	3303:  "opsession-clnt",
+	3304:  "opsession-srvr",
+	3305:  "odette-ftp",
+	3306:  "mysql",
+	3307:  "opsession-prxy",
+	3308:  "tns-server",
+	3309:  "tns-adv",
+	3310:  "dyna-access",
+	3311:  "mcns-tel-ret",
+	3312:  "appman-server",
+	3313:  "uorb",
+	3314:  "uohost",
+	3315:  "cdid",
+	3316:  "aicc-cmi",
+	3317:  "vsaiport",
+	3318:  "ssrip",
+	3319:  "sdt-lmd",
+	3320:  "officelink2000",
+	3321:  "vnsstr",
+	3326:  "sftu",
+	3327:  "bbars",
+	3328:  "egptlm",
+	3329:  "hp-device-disc",
+	3330:  "mcs-calypsoicf",
+	3331:  "mcs-messaging",
+	3332:  "mcs-mailsvr",
+	3333:  "dec-notes",
+	3334:  "directv-web",
+	3335:  "directv-soft",
+	3336:  "directv-tick",
+	3337:  "directv-catlg",
+	3338:  "anet-b",
+	3339:  "anet-l",
+	3340:  "anet-m",
+	3341:  "anet-h",
+	3342:  "webtie",
+	3343:  "ms-cluster-net",
+	3344:  "bnt-manager",
+	3345:  "influence",
+	3346:  "trnsprntproxy",
+	3347:  "phoenix-rpc",
+	3348:  "pangolin-laser",
+	3349:  "chevinservices",
+	3350:  "findviatv",
+	3351:  "btrieve",
+	3352:  "ssql",
+	3353:  "fatpipe",
+	3354:  "suitjd",
+	3355:  "ordinox-dbase",
+	3356:  "upnotifyps",
+	3357:  "adtech-test",
+	3358:  "mpsysrmsvr",
+	3359:  "wg-netforce",
+	3360:  "kv-server",
+	3361:  "kv-agent",
+	3362:  "dj-ilm",
+	3363:  "nati-vi-server",
+	3364:  "creativeserver",
+	3365:  "contentserver",
+	3366:  "creativepartnr",
+	3372:  "tip2",
+	3373:  "lavenir-lm",
+	3374:  "cluster-disc",
+	3375:  "vsnm-agent",
+	3376:  "cdbroker",
+	3377:  "cogsys-lm",
+	3378:  "wsicopy",
+	3379:  "socorfs",
+	3380:  "sns-channels",
+	3381:  "geneous",
+	3382:  "fujitsu-neat",
+	3383:  "esp-lm",
+	3384:  "hp-clic",
+	3385:  "qnxnetman",
+	3386:  "gprs-sig",
+	3387:  "backroomnet",
+	3388:  "cbserver",
+	3389:  "ms-wbt-server",
+	3390:  "dsc",
+	3391:  "savant",
+	3392:  "efi-lm",
+	3393:  "d2k-tapestry1",
+	3394:  "d2k-tapestry2",
+	3395:  "dyna-lm",
+	3396:  "printer-agent",
+	3397:  "cloanto-lm",
+	3398:  "mercantile",
+	3399:  "csms",
+	3400:  "csms2",
+	3401:  "filecast",
+	3402:  "fxaengine-net",
+	3405:  "nokia-ann-ch1",
+	3406:  "nokia-ann-ch2",
+	3407:  "ldap-admin",
+	3408:  "BESApi",
+	3409:  "networklens",
+	3410:  "networklenss",
+	3411:  "biolink-auth",
+	3412:  "xmlblaster",
+	3413:  "svnet",
+	3414:  "wip-port",
+	3415:  "bcinameservice",
+	3416:  "commandport",
+	3417:  "csvr",
+	3418:  "rnmap",
+	3419:  "softaudit",
+	3420:  "ifcp-port",
+	3421:  "bmap",
+	3422:  "rusb-sys-port",
+	3423:  "xtrm",
+	3424:  "xtrms",
+	3425:  "agps-port",
+	3426:  "arkivio",
+	3427:  "websphere-snmp",
+	3428:  "twcss",
+	3429:  "gcsp",
+	3430:  "ssdispatch",
+	3431:  "ndl-als",
+	3432:  "osdcp",
+	3433:  "opnet-smp",
+	3434:  "opencm",
+	3435:  "pacom",
+	3436:  "gc-config",
+	3437:  "autocueds",
+	3438:  "spiral-admin",
+	3439:  "hri-port",
+	3440:  "ans-console",
+	3441:  "connect-client",
+	3442:  "connect-server",
+	3443:  "ov-nnm-websrv",
+	3444:  "denali-server",
+	3445:  "monp",
+	3446:  "3comfaxrpc",
+	3447:  "directnet",
+	3448:  "dnc-port",
+	3449:  "hotu-chat",
+	3450:  "castorproxy",
+	3451:  "asam",
+	3452:  "sabp-signal",
+	3453:  "pscupd",
+	3454:  "mira",
+	3455:  "prsvp",
+	3456:  "vat",
+	3457:  "vat-control",
+	3458:  "d3winosfi",
+	3459:  "integral",
+	3460:  "edm-manager",
+	3461:  "edm-stager",
+	3462:  "edm-std-notify",
+	3463:  "edm-adm-notify",
+	3464:  "edm-mgr-sync",
+	3465:  "edm-mgr-cntrl",
+	3466:  "workflow",
+	3467:  "rcst",
+	3468:  "ttcmremotectrl",
+	3469:  "pluribus",
+	3470:  "jt400",
+	3471:  "jt400-ssl",
+	3472:  "jaugsremotec-1",
+	3473:  "jaugsremotec-2",
+	3474:  "ttntspauto",
+	3475:  "genisar-port",
+	3476:  "nppmp",
+	3477:  "ecomm",
+	3478:  "stun",
+	3479:  "twrpc",
+	3480:  "plethora",
+	3481:  "cleanerliverc",
+	3482:  "vulture",
+	3483:  "slim-devices",
+	3484:  "gbs-stp",
+	3485:  "celatalk",
+	3486:  "ifsf-hb-port",
+	3487:  "ltcudp",
+	3488:  "fs-rh-srv",
+	3489:  "dtp-dia",
+	3490:  "colubris",
+	3491:  "swr-port",
+	3492:  "tvdumtray-port",
+	3493:  "nut",
+	3494:  "ibm3494",
+	3495:  "seclayer-tcp",
+	3496:  "seclayer-tls",
+	3497:  "ipether232port",
+	3498:  "dashpas-port",
+	3499:  "sccip-media",
+	3500:  "rtmp-port",
+	3501:  "isoft-p2p",
+	3502:  "avinstalldisc",
+	3503:  "lsp-ping",
+	3504:  "ironstorm",
+	3505:  "ccmcomm",
+	3506:  "apc-3506",
+	3507:  "nesh-broker",
+	3508:  "interactionweb",
+	3509:  "vt-ssl",
+	3510:  "xss-port",
+	3511:  "webmail-2",
+	3512:  "aztec",
+	3513:  "arcpd",
+	3514:  "must-p2p",
+	3515:  "must-backplane",
+	3516:  "smartcard-port",
+	3517:  "802-11-iapp",
+	3518:  "artifact-msg",
+	3519:  "galileo",
+	3520:  "galileolog",
+	3521:  "mc3ss",
+	3522:  "nssocketport",
+	3523:  "odeumservlink",
+	3524:  "ecmport",
+	3525:  "eisport",
+	3526:  "starquiz-port",
+	3527:  "beserver-msg-q",
+	3528:  "jboss-iiop",
+	3529:  "jboss-iiop-ssl",
+	3530:  "gf",
+	3531:  "joltid",
+	3532:  "raven-rmp",
+	3533:  "raven-rdp",
+	3534:  "urld-port",
+	3535:  "ms-la",
+	3536:  "snac",
+	3537:  "ni-visa-remote",
+	3538:  "ibm-diradm",
+	3539:  "ibm-diradm-ssl",
+	3540:  "pnrp-port",
+	3541:  "voispeed-port",
+	3542:  "hacl-monitor",
+	3543:  "qftest-lookup",
+	3544:  "teredo",
+	3545:  "camac",
+	3547:  "symantec-sim",
+	3548:  "interworld",
+	3549:  "tellumat-nms",
+	3550:  "ssmpp",
+	3551:  "apcupsd",
+	3552:  "taserver",
+	3553:  "rbr-discovery",
+	3554:  "questnotify",
+	3555:  "razor",
+	3556:  "sky-transport",
+	3557:  "personalos-001",
+	3558:  "mcp-port",
+	3559:  "cctv-port",
+	3560:  "iniserve-port",
+	3561:  "bmc-onekey",
+	3562:  "sdbproxy",
+	3563:  "watcomdebug",
+	3564:  "esimport",
+	3567:  "dof-eps",
+	3568:  "dof-tunnel-sec",
+	3569:  "mbg-ctrl",
+	3570:  "mccwebsvr-port",
+	3571:  "megardsvr-port",
+	3572:  "megaregsvrport",
+	3573:  "tag-ups-1",
+	3574:  "dmaf-caster",
+	3575:  "ccm-port",
+	3576:  "cmc-port",
+	3577:  "config-port",
+	3578:  "data-port",
+	3579:  "ttat3lb",
+	3580:  "nati-svrloc",
+	3581:  "kfxaclicensing",
+	3582:  "press",
+	3583:  "canex-watch",
+	3584:  "u-dbap",
+	3585:  "emprise-lls",
+	3586:  "emprise-lsc",
+	3587:  "p2pgroup",
+	3588:  "sentinel",
+	3589:  "isomair",
+	3590:  "wv-csp-sms",
+	3591:  "gtrack-server",
+	3592:  "gtrack-ne",
+	3593:  "bpmd",
+	3594:  "mediaspace",
+	3595:  "shareapp",
+	3596:  "iw-mmogame",
+	3597:  "a14",
+	3598:  "a15",
+	3599:  "quasar-server",
+	3600:  "trap-daemon",
+	3601:  "visinet-gui",
+	3602:  "infiniswitchcl",
+	3603:  "int-rcv-cntrl",
+	3604:  "bmc-jmx-port",
+	3605:  "comcam-io",
+	3606:  "splitlock",
+	3607:  "precise-i3",
+	3608:  "trendchip-dcp",
+	3609:  "cpdi-pidas-cm",
+	3610:  "echonet",
+	3611:  "six-degrees",
+	3612:  "hp-dataprotect",
+	3613:  "alaris-disc",
+	3614:  "sigma-port",
+	3615:  "start-network",
+	3616:  "cd3o-protocol",
+	3617:  "sharp-server",
+	3618:  "aairnet-1",
+	3619:  "aairnet-2",
+	3620:  "ep-pcp",
+	3621:  "ep-nsp",
+	3622:  "ff-lr-port",
+	3623:  "haipe-discover",
+	3624:  "dist-upgrade",
+	3625:  "volley",
+	3626:  "bvcdaemon-port",
+	3627:  "jamserverport",
+	3628:  "ept-machine",
+	3629:  "escvpnet",
+	3630:  "cs-remote-db",
+	3631:  "cs-services",
+	3632:  "distcc",
+	3633:  "wacp",
+	3634:  "hlibmgr",
+	3635:  "sdo",
+	3636:  "servistaitsm",
+	3637:  "scservp",
+	3638:  "ehp-backup",
+	3639:  "xap-ha",
+	3640:  "netplay-port1",
+	3641:  "netplay-port2",
+	3642:  "juxml-port",
+	3643:  "audiojuggler",
+	3644:  "ssowatch",
+	3645:  "cyc",
+	3646:  "xss-srv-port",
+	3647:  "splitlock-gw",
+	3648:  "fjcp",
+	3649:  "nmmp",
+	3650:  "prismiq-plugin",
+	3651:  "xrpc-registry",
+	3652:  "vxcrnbuport",
+	3653:  "tsp",
+	3654:  "vaprtm",
+	3655:  "abatemgr",
+	3656:  "abatjss",
+	3657:  "immedianet-bcn",
+	3658:  "ps-ams",
+	3659:  "apple-sasl",
+	3660:  "can-nds-ssl",
+	3661:  "can-ferret-ssl",
+	3662:  "pserver",
+	3663:  "dtp",
+	3664:  "ups-engine",
+	3665:  "ent-engine",
+	3666:  "eserver-pap",
+	3667:  "infoexch",
+	3668:  "dell-rm-port",
+	3669:  "casanswmgmt",
+	3670:  "smile",
+	3671:  "efcp",
+	3672:  "lispworks-orb",
+	3673:  "mediavault-gui",
+	3674:  "wininstall-ipc",
+	3675:  "calltrax",
+	3676:  "va-pacbase",
+	3677:  "roverlog",
+	3678:  "ipr-dglt",
+	3679:  "Escale (Newton Dock)",
+	3680:  "npds-tracker",
+	3681:  "bts-x73",
+	3682:  "cas-mapi",
+	3683:  "bmc-ea",
+	3684:  "faxstfx-port",
+	3685:  "dsx-agent",
+	3686:  "tnmpv2",
+	3687:  "simple-push",
+	3688:  "simple-push-s",
+	3689:  "daap",
+	3690:  "svn",
+	3691:  "magaya-network",
+	3692:  "intelsync",
+	3695:  "bmc-data-coll",
+	3696:  "telnetcpcd",
+	3697:  "nw-license",
+	3698:  "sagectlpanel",
+	3699:  "kpn-icw",
+	3700:  "lrs-paging",
+	3701:  "netcelera",
+	3702:  "ws-discovery",
+	3703:  "adobeserver-3",
+	3704:  "adobeserver-4",
+	3705:  "adobeserver-5",
+	3706:  "rt-event",
+	3707:  "rt-event-s",
+	3708:  "sun-as-iiops",
+	3709:  "ca-idms",
+	3710:  "portgate-auth",
+	3711:  "edb-server2",
+	3712:  "sentinel-ent",
+	3713:  "tftps",
+	3714:  "delos-dms",
+	3715:  "anoto-rendezv",
+	3716:  "wv-csp-sms-cir",
+	3717:  "wv-csp-udp-cir",
+	3718:  "opus-services",
+	3719:  "itelserverport",
+	3720:  "ufastro-instr",
+	3721:  "xsync",
+	3722:  "xserveraid",
+	3723:  "sychrond",
+	3724:  "blizwow",
+	3725:  "na-er-tip",
+	3726:  "array-manager",
+	3727:  "e-mdu",
+	3728:  "e-woa",
+	3729:  "fksp-audit",
+	3730:  "client-ctrl",
+	3731:  "smap",
+	3732:  "m-wnn",
+	3733:  "multip-msg",
+	3734:  "synel-data",
+	3735:  "pwdis",
+	3736:  "rs-rmi",
+	3738:  "versatalk",
+	3739:  "launchbird-lm",
+	3740:  "heartbeat",
+	3741:  "wysdma",
+	3742:  "cst-port",
+	3743:  "ipcs-command",
+	3744:  "sasg",
+	3745:  "gw-call-port",
+	3746:  "linktest",
+	3747:  "linktest-s",
+	3748:  "webdata",
+	3749:  "cimtrak",
+	3750:  "cbos-ip-port",
+	3751:  "gprs-cube",
+	3752:  "vipremoteagent",
+	3753:  "nattyserver",
+	3754:  "timestenbroker",
+	3755:  "sas-remote-hlp",
+	3756:  "canon-capt",
+	3757:  "grf-port",
+	3758:  "apw-registry",
+	3759:  "exapt-lmgr",
+	3760:  "adtempusclient",
+	3761:  "gsakmp",
+	3762:  "gbs-smp",
+	3763:  "xo-wave",
+	3764:  "mni-prot-rout",
+	3765:  "rtraceroute",
+	3767:  "listmgr-port",
+	3768:  "rblcheckd",
+	3769:  "haipe-otnk",
+	3770:  "cindycollab",
+	3771:  "paging-port",
+	3772:  "ctp",
+	3773:  "ctdhercules",
+	3774:  "zicom",
+	3775:  "ispmmgr",
+	3776:  "dvcprov-port",
+	3777:  "jibe-eb",
+	3778:  "c-h-it-port",
+	3779:  "cognima",
+	3780:  "nnp",
+	3781:  "abcvoice-port",
+	3782:  "iso-tp0s",
+	3783:  "bim-pem",
+	3784:  "bfd-control",
+	3785:  "bfd-echo",
+	3786:  "upstriggervsw",
+	3787:  "fintrx",
+	3788:  "isrp-port",
+	3789:  "remotedeploy",
+	3790:  "quickbooksrds",
+	3791:  "tvnetworkvideo",
+	3792:  "sitewatch",
+	3793:  "dcsoftware",
+	3794:  "jaus",
+	3795:  "myblast",
+	3796:  "spw-dialer",
+	3797:  "idps",
+	3798:  "minilock",
+	3799:  "radius-dynauth",
+	3800:  "pwgpsi",
+	3801:  "ibm-mgr",
+	3802:  "vhd",
+	3803:  "soniqsync",
+	3804:  "iqnet-port",
+	3805:  "tcpdataserver",
+	3806:  "wsmlb",
+	3807:  "spugna",
+	3808:  "sun-as-iiops-ca",
+	3809:  "apocd",
+	3810:  "wlanauth",
+	3811:  "amp",
+	3812:  "neto-wol-server",
+	3813:  "rap-ip",
+	3814:  "neto-dcs",
+	3815:  "lansurveyorxml",
+	3816:  "sunlps-http",
+	3817:  "tapeware",
+	3818:  "crinis-hb",
+	3819:  "epl-slp",
+	3820:  "scp",
+	3821:  "pmcp",
+	3822:  "acp-discovery",
+	3823:  "acp-conduit",
+	3824:  "acp-policy",
+	3825:  "ffserver",
+	3826:  "warmux",
+	3827:  "netmpi",
+	3828:  "neteh",
+	3829:  "neteh-ext",
+	3830:  "cernsysmgmtagt",
+	3831:  "dvapps",
+	3832:  "xxnetserver",
+	3833:  "aipn-auth",
+	3834:  "spectardata",
+	3835:  "spectardb",
+	3836:  "markem-dcp",
+	3837:  "mkm-discovery",
+	3838:  "sos",
+	3839:  "amx-rms",
+	3840:  "flirtmitmir",
+	3842:  "nhci",
+	3843:  "quest-agent",
+	3844:  "rnm",
+	3845:  "v-one-spp",
+	3846:  "an-pcp",
+	3847:  "msfw-control",
+	3848:  "item",
+	3849:  "spw-dnspreload",
+	3850:  "qtms-bootstrap",
+	3851:  "spectraport",
+	3852:  "sse-app-config",
+	3853:  "sscan",
+	3854:  "stryker-com",
+	3855:  "opentrac",
+	3856:  "informer",
+	3857:  "trap-port",
+	3858:  "trap-port-mom",
+	3859:  "nav-port",
+	3860:  "sasp",
+	3861:  "winshadow-hd",
+	3862:  "giga-pocket",
+	3863:  "asap-udp",
+	3865:  "xpl",
+	3866:  "dzdaemon",
+	3867:  "dzoglserver",
+	3869:  "ovsam-mgmt",
+	3870:  "ovsam-d-agent",
+	3871:  "avocent-adsap",
+	3872:  "oem-agent",
+	3873:  "fagordnc",
+	3874:  "sixxsconfig",
+	3875:  "pnbscada",
+	3876:  "dl-agent",
+	3877:  "xmpcr-interface",
+	3878:  "fotogcad",
+	3879:  "appss-lm",
+	3880:  "igrs",
+	3881:  "idac",
+	3882:  "msdts1",
+	3883:  "vrpn",
+	3884:  "softrack-meter",
+	3885:  "topflow-ssl",
+	3886:  "nei-management",
+	3887:  "ciphire-data",
+	3888:  "ciphire-serv",
+	3889:  "dandv-tester",
+	3890:  "ndsconnect",
+	3891:  "rtc-pm-port",
+	3892:  "pcc-image-port",
+	3893:  "cgi-starapi",
+	3894:  "syam-agent",
+	3895:  "syam-smc",
+	3896:  "sdo-tls",
+	3897:  "sdo-ssh",
+	3898:  "senip",
+	3899:  "itv-control",
+	3900:  "udt-os",
+	3901:  "nimsh",
+	3902:  "nimaux",
+	3903:  "charsetmgr",
+	3904:  "omnilink-port",
+	3905:  "mupdate",
+	3906:  "topovista-data",
+	3907:  "imoguia-port",
+	3908:  "hppronetman",
+	3909:  "surfcontrolcpa",
+	3910:  "prnrequest",
+	3911:  "prnstatus",
+	3912:  "gbmt-stars",
+	3913:  "listcrt-port",
+	3914:  "listcrt-port-2",
+	3915:  "agcat",
+	3916:  "wysdmc",
+	3917:  "aftmux",
+	3918:  "pktcablemmcops",
+	3919:  "hyperip",
+	3920:  "exasoftport1",
+	3921:  "herodotus-net",
+	3922:  "sor-update",
+	3923:  "symb-sb-port",
+	3924:  "mpl-gprs-port",
+	3925:  "zmp",
+	3926:  "winport",
+	3927:  "natdataservice",
+	3928:  "netboot-pxe",
+	3929:  "smauth-port",
+	3930:  "syam-webserver",
+	3931:  "msr-plugin-port",
+	3932:  "dyn-site",
+	3933:  "plbserve-port",
+	3934:  "sunfm-port",
+	3935:  "sdp-portmapper",
+	3936:  "mailprox",
+	3937:  "dvbservdsc",
+	3938:  "dbcontrol-agent",
+	3939:  "aamp",
+	3940:  "xecp-node",
+	3941:  "homeportal-web",
+	3942:  "srdp",
+	3943:  "tig",
+	3944:  "sops",
+	3945:  "emcads",
+	3946:  "backupedge",
+	3947:  "ccp",
+	3948:  "apdap",
+	3949:  "drip",
+	3950:  "namemunge",
+	3951:  "pwgippfax",
+	3952:  "i3-sessionmgr",
+	3953:  "xmlink-connect",
+	3954:  "adrep",
+	3955:  "p2pcommunity",
+	3956:  "gvcp",
+	3957:  "mqe-broker",
+	3958:  "mqe-agent",
+	3959:  "treehopper",
+	3960:  "bess",
+	3961:  "proaxess",
+	3962:  "sbi-agent",
+	3963:  "thrp",
+	3964:  "sasggprs",
+	3965:  "ati-ip-to-ncpe",
+	3966:  "bflckmgr",
+	3967:  "ppsms",
+	3968:  "ianywhere-dbns",
+	3969:  "landmarks",
+	3970:  "lanrevagent",
+	3971:  "lanrevserver",
+	3972:  "iconp",
+	3973:  "progistics",
+	3974:  "citysearch",
+	3975:  "airshot",
+	3976:  "opswagent",
+	3977:  "opswmanager",
+	3978:  "secure-cfg-svr",
+	3979:  "smwan",
+	3980:  "acms",
+	3981:  "starfish",
+	3982:  "eis",
+	3983:  "eisp",
+	3984:  "mapper-nodemgr",
+	3985:  "mapper-mapethd",
+	3986:  "mapper-ws-ethd",
+	3987:  "centerline",
+	3988:  "dcs-config",
+	3989:  "bv-queryengine",
+	3990:  "bv-is",
+	3991:  "bv-smcsrv",
+	3992:  "bv-ds",
+	3993:  "bv-agent",
+	3995:  "iss-mgmt-ssl",
+	3996:  "abcsoftware",
+	3997:  "agentsease-db",
+	3998:  "dnx",
+	3999:  "nvcnet",
+	4000:  "terabase",
+	4001:  "newoak",
+	4002:  "pxc-spvr-ft",
+	4003:  "pxc-splr-ft",
+	4004:  "pxc-roid",
+	4005:  "pxc-pin",
+	4006:  "pxc-spvr",
+	4007:  "pxc-splr",
+	4008:  "netcheque",
+	4009:  "chimera-hwm",
+	4010:  "samsung-unidex",
+	4011:  "altserviceboot",
+	4012:  "pda-gate",
+	4013:  "acl-manager",
+	4014:  "taiclock",
+	4015:  "talarian-mcast1",
+	4016:  "talarian-mcast2",
+	4017:  "talarian-mcast3",
+	4018:  "talarian-mcast4",
+	4019:  "talarian-mcast5",
+	4020:  "trap",
+	4021:  "nexus-portal",
+	4022:  "dnox",
+	4023:  "esnm-zoning",
+	4024:  "tnp1-port",
+	4025:  "partimage",
+	4026:  "as-debug",
+	4027:  "bxp",
+	4028:  "dtserver-port",
+	4029:  "ip-qsig",
+	4030:  "jdmn-port",
+	4031:  "suucp",
+	4032:  "vrts-auth-port",
+	4033:  "sanavigator",
+	4034:  "ubxd",
+	4035:  "wap-push-http",
+	4036:  "wap-push-https",
+	4037:  "ravehd",
+	4038:  "fazzt-ptp",
+	4039:  "fazzt-admin",
+	4040:  "yo-main",
+	4041:  "houston",
+	4042:  "ldxp",
+	4043:  "nirp",
+	4044:  "ltp",
+	4045:  "npp",
+	4046:  "acp-proto",
+	4047:  "ctp-state",
+	4049:  "wafs",
+	4050:  "cisco-wafs",
+	4051:  "cppdp",
+	4052:  "interact",
+	4053:  "ccu-comm-1",
+	4054:  "ccu-comm-2",
+	4055:  "ccu-comm-3",
+	4056:  "lms",
+	4057:  "wfm",
+	4058:  "kingfisher",
+	4059:  "dlms-cosem",
+	4060:  "dsmeter-iatc",
+	4061:  "ice-location",
+	4062:  "ice-slocation",
+	4063:  "ice-router",
+	4064:  "ice-srouter",
+	4065:  "avanti-cdp",
+	4066:  "pmas",
+	4067:  "idp",
+	4068:  "ipfltbcst",
+	4069:  "minger",
+	4070:  "tripe",
+	4071:  "aibkup",
+	4072:  "zieto-sock",
+	4073:  "iRAPP",
+	4074:  "cequint-cityid",
+	4075:  "perimlan",
+	4076:  "seraph",
+	4077:  "ascomalarm",
+	4079:  "santools",
+	4080:  "lorica-in",
+	4081:  "lorica-in-sec",
+	4082:  "lorica-out",
+	4083:  "lorica-out-sec",
+	4084:  "fortisphere-vm",
+	4086:  "ftsync",
+	4089:  "opencore",
+	4090:  "omasgport",
+	4091:  "ewinstaller",
+	4092:  "ewdgs",
+	4093:  "pvxpluscs",
+	4094:  "sysrqd",
+	4095:  "xtgui",
+	4096:  "bre",
+	4097:  "patrolview",
+	4098:  "drmsfsd",
+	4099:  "dpcp",
+	4100:  "igo-incognito",
+	4101:  "brlp-0",
+	4102:  "brlp-1",
+	4103:  "brlp-2",
+	4104:  "brlp-3",
+	4105:  "shofar",
+	4106:  "synchronite",
+	4107:  "j-ac",
+	4108:  "accel",
+	4109:  "izm",
+	4110:  "g2tag",
+	4111:  "xgrid",
+	4112:  "apple-vpns-rp",
+	4113:  "aipn-reg",
+	4114:  "jomamqmonitor",
+	4115:  "cds",
+	4116:  "smartcard-tls",
+	4117:  "hillrserv",
+	4118:  "netscript",
+	4119:  "assuria-slm",
+	4121:  "e-builder",
+	4122:  "fprams",
+	4123:  "z-wave",
+	4124:  "tigv2",
+	4125:  "opsview-envoy",
+	4126:  "ddrepl",
+	4127:  "unikeypro",
+	4128:  "nufw",
+	4129:  "nuauth",
+	4130:  "fronet",
+	4131:  "stars",
+	4132:  "nuts-dem",
+	4133:  "nuts-bootp",
+	4134:  "nifty-hmi",
+	4135:  "cl-db-attach",
+	4136:  "cl-db-request",
+	4137:  "cl-db-remote",
+	4138:  "nettest",
+	4139:  "thrtx",
+	4140:  "cedros-fds",
+	4141:  "oirtgsvc",
+	4142:  "oidocsvc",
+	4143:  "oidsr",
+	4145:  "vvr-control",
+	4146:  "tgcconnect",
+	4147:  "vrxpservman",
+	4148:  "hhb-handheld",
+	4149:  "agslb",
+	4150:  "PowerAlert-nsa",
+	4151:  "menandmice-noh",
+	4152:  "idig-mux",
+	4153:  "mbl-battd",
+	4154:  "atlinks",
+	4155:  "bzr",
+	4156:  "stat-results",
+	4157:  "stat-scanner",
+	4158:  "stat-cc",
+	4159:  "nss",
+	4160:  "jini-discovery",
+	4161:  "omscontact",
+	4162:  "omstopology",
+	4163:  "silverpeakpeer",
+	4164:  "silverpeakcomm",
+	4165:  "altcp",
+	4166:  "joost",
+	4167:  "ddgn",
+	4168:  "pslicser",
+	4169:  "iadt-disc",
+	4172:  "pcoip",
+	4173:  "mma-discovery",
+	4174:  "sm-disc",
+	4177:  "wello",
+	4178:  "storman",
+	4179:  "MaxumSP",
+	4180:  "httpx",
+	4181:  "macbak",
+	4182:  "pcptcpservice",
+	4183:  "cyborgnet",
+	4184:  "universe-suite",
+	4185:  "wcpp",
+	4188:  "vatata",
+	4191:  "dsmipv6",
+	4192:  "azeti-bd",
+	4197:  "hctl",
+	4199:  "eims-admin",
+	4300:  "corelccam",
+	4301:  "d-data",
+	4302:  "d-data-control",
+	4303:  "srcp",
+	4304:  "owserver",
+	4305:  "batman",
+	4306:  "pinghgl",
+	4307:  "trueconf",
+	4308:  "compx-lockview",
+	4309:  "dserver",
+	4310:  "mirrtex",
+	4320:  "fdt-rcatp",
+	4321:  "rwhois",
+	4322:  "trim-event",
+	4323:  "trim-ice",
+	4325:  "geognosisman",
+	4326:  "geognosis",
+	4327:  "jaxer-web",
+	4328:  "jaxer-manager",
+	4333:  "ahsp",
+	4340:  "gaia",
+	4341:  "lisp-data",
+	4342:  "lisp-control",
+	4343:  "unicall",
+	4344:  "vinainstall",
+	4345:  "m4-network-as",
+	4346:  "elanlm",
+	4347:  "lansurveyor",
+	4348:  "itose",
+	4349:  "fsportmap",
+	4350:  "net-device",
+	4351:  "plcy-net-svcs",
+	4352:  "pjlink",
+	4353:  "f5-iquery",
+	4354:  "qsnet-trans",
+	4355:  "qsnet-workst",
+	4356:  "qsnet-assist",
+	4357:  "qsnet-cond",
+	4358:  "qsnet-nucl",
+	4359:  "omabcastltkm",
+	4361:  "nacnl",
+	4362:  "afore-vdp-disc",
+	4366:  "shadowstream",
+	4368:  "wxbrief",
+	4369:  "epmd",
+	4370:  "elpro-tunnel",
+	4371:  "l2c-disc",
+	4372:  "l2c-data",
+	4373:  "remctl",
+	4375:  "tolteces",
+	4376:  "bip",
+	4377:  "cp-spxsvr",
+	4378:  "cp-spxdpy",
+	4379:  "ctdb",
+	4389:  "xandros-cms",
+	4390:  "wiegand",
+	4394:  "apwi-disc",
+	4395:  "omnivisionesx",
+	4400:  "ds-srv",
+	4401:  "ds-srvr",
+	4402:  "ds-clnt",
+	4403:  "ds-user",
+	4404:  "ds-admin",
+	4405:  "ds-mail",
+	4406:  "ds-slp",
+	4412:  "smallchat",
+	4413:  "avi-nms-disc",
+	4416:  "pjj-player-disc",
+	4418:  "axysbridge",
+	4420:  "nvm-express",
+	4425:  "netrockey6",
+	4426:  "beacon-port-2",
+	4430:  "rsqlserver",
+	4432:  "l-acoustics",
+	4441:  "netblox",
+	4442:  "saris",
+	4443:  "pharos",
+	4444:  "krb524",
+	4445:  "upnotifyp",
+	4446:  "n1-fwp",
+	4447:  "n1-rmgmt",
+	4448:  "asc-slmd",
+	4449:  "privatewire",
+	4450:  "camp",
+	4451:  "ctisystemmsg",
+	4452:  "ctiprogramload",
+	4453:  "nssalertmgr",
+	4454:  "nssagentmgr",
+	4455:  "prchat-user",
+	4456:  "prchat-server",
+	4457:  "prRegister",
+	4458:  "mcp",
+	4484:  "hpssmgmt",
+	4486:  "icms",
+	4488:  "awacs-ice",
+	4500:  "ipsec-nat-t",
+	4534:  "armagetronad",
+	4535:  "ehs",
+	4536:  "ehs-ssl",
+	4537:  "wssauthsvc",
+	4538:  "swx-gate",
+	4545:  "worldscores",
+	4546:  "sf-lm",
+	4547:  "lanner-lm",
+	4548:  "synchromesh",
+	4549:  "aegate",
+	4550:  "gds-adppiw-db",
+	4551:  "ieee-mih",
+	4552:  "menandmice-mon",
+	4554:  "msfrs",
+	4555:  "rsip",
+	4556:  "dtn-bundle",
+	4557:  "mtcevrunqss",
+	4558:  "mtcevrunqman",
+	4559:  "hylafax",
+	4566:  "kwtc",
+	4567:  "tram",
+	4568:  "bmc-reporting",
+	4569:  "iax",
+	4591:  "l3t-at-an",
+	4592:  "hrpd-ith-at-an",
+	4593:  "ipt-anri-anri",
+	4594:  "ias-session",
+	4595:  "ias-paging",
+	4596:  "ias-neighbor",
+	4597:  "a21-an-1xbs",
+	4598:  "a16-an-an",
+	4599:  "a17-an-an",
+	4600:  "piranha1",
+	4601:  "piranha2",
+	4621:  "ventoso",
+	4658:  "playsta2-app",
+	4659:  "playsta2-lob",
+	4660:  "smaclmgr",
+	4661:  "kar2ouche",
+	4662:  "oms",
+	4663:  "noteit",
+	4664:  "ems",
+	4665:  "contclientms",
+	4666:  "eportcomm",
+	4667:  "mmacomm",
+	4668:  "mmaeds",
+	4669:  "eportcommdata",
+	4670:  "light",
+	4671:  "acter",
+	4672:  "rfa",
+	4673:  "cxws",
+	4674:  "appiq-mgmt",
+	4675:  "dhct-status",
+	4676:  "dhct-alerts",
+	4677:  "bcs",
+	4678:  "traversal",
+	4679:  "mgesupervision",
+	4680:  "mgemanagement",
+	4681:  "parliant",
+	4682:  "finisar",
+	4683:  "spike",
+	4684:  "rfid-rp1",
+	4685:  "autopac",
+	4686:  "msp-os",
+	4687:  "nst",
+	4688:  "mobile-p2p",
+	4689:  "altovacentral",
+	4690:  "prelude",
+	4691:  "mtn",
+	4692:  "conspiracy",
+	4700:  "netxms-agent",
+	4701:  "netxms-mgmt",
+	4702:  "netxms-sync",
+	4711:  "trinity-dist",
+	4725:  "truckstar",
+	4726:  "a26-fap-fgw",
+	4727:  "fcis-disc",
+	4728:  "capmux",
+	4729:  "gsmtap",
+	4730:  "gearman",
+	4732:  "ohmtrigger",
+	4737:  "ipdr-sp",
+	4738:  "solera-lpn",
+	4739:  "ipfix",
+	4740:  "ipfixs",
+	4741:  "lumimgrd",
+	4742:  "sicct-sdp",
+	4743:  "openhpid",
+	4744:  "ifsp",
+	4745:  "fmp",
+	4746:  "intelliadm-disc",
+	4747:  "buschtrommel",
+	4749:  "profilemac",
+	4750:  "ssad",
+	4751:  "spocp",
+	4752:  "snap",
+	4753:  "simon-disc",
+	4754:  "gre-in-udp",
+	4755:  "gre-udp-dtls",
+	4784:  "bfd-multi-ctl",
+	4785:  "cncp",
+	4789:  "vxlan",
+	4790:  "vxlan-gpe",
+	4791:  "roce",
+	4800:  "iims",
+	4801:  "iwec",
+	4802:  "ilss",
+	4803:  "notateit-disc",
+	4804:  "aja-ntv4-disc",
+	4827:  "htcp",
+	4837:  "varadero-0",
+	4838:  "varadero-1",
+	4839:  "varadero-2",
+	4840:  "opcua-udp",
+	4841:  "quosa",
+	4842:  "gw-asv",
+	4843:  "opcua-tls",
+	4844:  "gw-log",
+	4845:  "wcr-remlib",
+	4846:  "contamac-icm",
+	4847:  "wfc",
+	4848:  "appserv-http",
+	4849:  "appserv-https",
+	4850:  "sun-as-nodeagt",
+	4851:  "derby-repli",
+	4867:  "unify-debug",
+	4868:  "phrelay",
+	4869:  "phrelaydbg",
+	4870:  "cc-tracking",
+	4871:  "wired",
+	4876:  "tritium-can",
+	4877:  "lmcs",
+	4878:  "inst-discovery",
+	4881:  "socp-t",
+	4882:  "socp-c",
+	4884:  "hivestor",
+	4885:  "abbs",
+	4894:  "lyskom",
+	4899:  "radmin-port",
+	4900:  "hfcs",
+	4914:  "bones",
+	4936:  "an-signaling",
+	4937:  "atsc-mh-ssc",
+	4940:  "eq-office-4940",
+	4941:  "eq-office-4941",
+	4942:  "eq-office-4942",
+	4949:  "munin",
+	4950:  "sybasesrvmon",
+	4951:  "pwgwims",
+	4952:  "sagxtsds",
+	4969:  "ccss-qmm",
+	4970:  "ccss-qsm",
+	4980:  "ctxs-vpp",
+	4986:  "mrip",
+	4987:  "smar-se-port1",
+	4988:  "smar-se-port2",
+	4989:  "parallel",
+	4990:  "busycal",
+	4991:  "vrt",
+	4999:  "hfcs-manager",
+	5000:  "commplex-main",
+	5001:  "commplex-link",
+	5002:  "rfe",
+	5003:  "fmpro-internal",
+	5004:  "avt-profile-1",
+	5005:  "avt-profile-2",
+	5006:  "wsm-server",
+	5007:  "wsm-server-ssl",
+	5008:  "synapsis-edge",
+	5009:  "winfs",
+	5010:  "telelpathstart",
+	5011:  "telelpathattack",
+	5012:  "nsp",
+	5013:  "fmpro-v6",
+	5014:  "onpsocket",
+	5020:  "zenginkyo-1",
+	5021:  "zenginkyo-2",
+	5022:  "mice",
+	5023:  "htuilsrv",
+	5024:  "scpi-telnet",
+	5025:  "scpi-raw",
+	5026:  "strexec-d",
+	5027:  "strexec-s",
+	5029:  "infobright",
+	5030:  "surfpass",
+	5031:  "dmp",
+	5042:  "asnaacceler8db",
+	5043:  "swxadmin",
+	5044:  "lxi-evntsvc",
+	5046:  "vpm-udp",
+	5047:  "iscape",
+	5049:  "ivocalize",
+	5050:  "mmcc",
+	5051:  "ita-agent",
+	5052:  "ita-manager",
+	5053:  "rlm-disc",
+	5055:  "unot",
+	5056:  "intecom-ps1",
+	5057:  "intecom-ps2",
+	5058:  "locus-disc",
+	5059:  "sds",
+	5060:  "sip",
+	5061:  "sips",
+	5062:  "na-localise",
+	5064:  "ca-1",
+	5065:  "ca-2",
+	5066:  "stanag-5066",
+	5067:  "authentx",
+	5069:  "i-net-2000-npr",
+	5070:  "vtsas",
+	5071:  "powerschool",
+	5072:  "ayiya",
+	5073:  "tag-pm",
+	5074:  "alesquery",
+	5078:  "pixelpusher",
+	5079:  "cp-spxrpts",
+	5080:  "onscreen",
+	5081:  "sdl-ets",
+	5082:  "qcp",
+	5083:  "qfp",
+	5084:  "llrp",
+	5085:  "encrypted-llrp",
+	5092:  "magpie",
+	5093:  "sentinel-lm",
+	5094:  "hart-ip",
+	5099:  "sentlm-srv2srv",
+	5100:  "socalia",
+	5101:  "talarian-udp",
+	5102:  "oms-nonsecure",
+	5104:  "tinymessage",
+	5105:  "hughes-ap",
+	5111:  "taep-as-svc",
+	5112:  "pm-cmdsvr",
+	5116:  "emb-proj-cmd",
+	5120:  "barracuda-bbs",
+	5133:  "nbt-pc",
+	5136:  "minotaur-sa",
+	5137:  "ctsd",
+	5145:  "rmonitor-secure",
+	5150:  "atmp",
+	5151:  "esri-sde",
+	5152:  "sde-discovery",
+	5154:  "bzflag",
+	5155:  "asctrl-agent",
+	5164:  "vpa-disc",
+	5165:  "ife-icorp",
+	5166:  "winpcs",
+	5167:  "scte104",
+	5168:  "scte30",
+	5190:  "aol",
+	5191:  "aol-1",
+	5192:  "aol-2",
+	5193:  "aol-3",
+	5200:  "targus-getdata",
+	5201:  "targus-getdata1",
+	5202:  "targus-getdata2",
+	5203:  "targus-getdata3",
+	5223:  "hpvirtgrp",
+	5224:  "hpvirtctrl",
+	5225:  "hp-server",
+	5226:  "hp-status",
+	5227:  "perfd",
+	5234:  "eenet",
+	5235:  "galaxy-network",
+	5236:  "padl2sim",
+	5237:  "mnet-discovery",
+	5245:  "downtools-disc",
+	5246:  "capwap-control",
+	5247:  "capwap-data",
+	5248:  "caacws",
+	5249:  "caaclang2",
+	5250:  "soagateway",
+	5251:  "caevms",
+	5252:  "movaz-ssc",
+	5264:  "3com-njack-1",
+	5265:  "3com-njack-2",
+	5270:  "cartographerxmp",
+	5271:  "cuelink-disc",
+	5272:  "pk",
+	5282:  "transmit-port",
+	5298:  "presence",
+	5299:  "nlg-data",
+	5300:  "hacl-hb",
+	5301:  "hacl-gs",
+	5302:  "hacl-cfg",
+	5303:  "hacl-probe",
+	5304:  "hacl-local",
+	5305:  "hacl-test",
+	5306:  "sun-mc-grp",
+	5307:  "sco-aip",
+	5308:  "cfengine",
+	5309:  "jprinter",
+	5310:  "outlaws",
+	5312:  "permabit-cs",
+	5313:  "rrdp",
+	5314:  "opalis-rbt-ipc",
+	5315:  "hacl-poll",
+	5343:  "kfserver",
+	5344:  "xkotodrcp",
+	5349:  "stuns",
+	5350:  "pcp-multicast",
+	5351:  "pcp",
+	5352:  "dns-llq",
+	5353:  "mdns",
+	5354:  "mdnsresponder",
+	5355:  "llmnr",
+	5356:  "ms-smlbiz",
+	5357:  "wsdapi",
+	5358:  "wsdapi-s",
+	5359:  "ms-alerter",
+	5360:  "ms-sideshow",
+	5361:  "ms-s-sideshow",
+	5362:  "serverwsd2",
+	5363:  "net-projection",
+	5364:  "kdnet",
+	5397:  "stresstester",
+	5398:  "elektron-admin",
+	5399:  "securitychase",
+	5400:  "excerpt",
+	5401:  "excerpts",
+	5402:  "mftp",
+	5403:  "hpoms-ci-lstn",
+	5404:  "hpoms-dps-lstn",
+	5405:  "netsupport",
+	5406:  "systemics-sox",
+	5407:  "foresyte-clear",
+	5408:  "foresyte-sec",
+	5409:  "salient-dtasrv",
+	5410:  "salient-usrmgr",
+	5411:  "actnet",
+	5412:  "continuus",
+	5413:  "wwiotalk",
+	5414:  "statusd",
+	5415:  "ns-server",
+	5416:  "sns-gateway",
+	5417:  "sns-agent",
+	5418:  "mcntp",
+	5419:  "dj-ice",
+	5420:  "cylink-c",
+	5421:  "netsupport2",
+	5422:  "salient-mux",
+	5423:  "virtualuser",
+	5424:  "beyond-remote",
+	5425:  "br-channel",
+	5426:  "devbasic",
+	5427:  "sco-peer-tta",
+	5428:  "telaconsole",
+	5429:  "base",
+	5430:  "radec-corp",
+	5431:  "park-agent",
+	5432:  "postgresql",
+	5433:  "pyrrho",
+	5434:  "sgi-arrayd",
+	5435:  "sceanics",
+	5436:  "pmip6-cntl",
+	5437:  "pmip6-data",
+	5443:  "spss",
+	5450:  "tiepie-disc",
+	5453:  "surebox",
+	5454:  "apc-5454",
+	5455:  "apc-5455",
+	5456:  "apc-5456",
+	5461:  "silkmeter",
+	5462:  "ttl-publisher",
+	5463:  "ttlpriceproxy",
+	5464:  "quailnet",
+	5465:  "netops-broker",
+	5474:  "apsolab-rpc",
+	5500:  "fcp-addr-srvr1",
+	5501:  "fcp-addr-srvr2",
+	5502:  "fcp-srvr-inst1",
+	5503:  "fcp-srvr-inst2",
+	5504:  "fcp-cics-gw1",
+	5505:  "checkoutdb",
+	5506:  "amc",
+	5553:  "sgi-eventmond",
+	5554:  "sgi-esphttp",
+	5555:  "personal-agent",
+	5556:  "freeciv",
+	5567:  "dof-dps-mc-sec",
+	5568:  "sdt",
+	5569:  "rdmnet-device",
+	5573:  "sdmmp",
+	5580:  "tmosms0",
+	5581:  "tmosms1",
+	5582:  "fac-restore",
+	5583:  "tmo-icon-sync",
+	5584:  "bis-web",
+	5585:  "bis-sync",
+	5597:  "ininmessaging",
+	5598:  "mctfeed",
+	5599:  "esinstall",
+	5600:  "esmmanager",
+	5601:  "esmagent",
+	5602:  "a1-msc",
+	5603:  "a1-bs",
+	5604:  "a3-sdunode",
+	5605:  "a4-sdunode",
+	5627:  "ninaf",
+	5628:  "htrust",
+	5629:  "symantec-sfdb",
+	5630:  "precise-comm",
+	5631:  "pcanywheredata",
+	5632:  "pcanywherestat",
+	5633:  "beorl",
+	5634:  "xprtld",
+	5670:  "zre-disc",
+	5671:  "amqps",
+	5672:  "amqp",
+	5673:  "jms",
+	5674:  "hyperscsi-port",
+	5675:  "v5ua",
+	5676:  "raadmin",
+	5677:  "questdb2-lnchr",
+	5678:  "rrac",
+	5679:  "dccm",
+	5680:  "auriga-router",
+	5681:  "ncxcp",
+	5682:  "brightcore",
+	5683:  "coap",
+	5684:  "coaps",
+	5687:  "gog-multiplayer",
+	5688:  "ggz",
+	5689:  "qmvideo",
+	5713:  "proshareaudio",
+	5714:  "prosharevideo",
+	5715:  "prosharedata",
+	5716:  "prosharerequest",
+	5717:  "prosharenotify",
+	5718:  "dpm",
+	5719:  "dpm-agent",
+	5720:  "ms-licensing",
+	5721:  "dtpt",
+	5722:  "msdfsr",
+	5723:  "omhs",
+	5724:  "omsdk",
+	5728:  "io-dist-group",
+	5729:  "openmail",
+	5730:  "unieng",
+	5741:  "ida-discover1",
+	5742:  "ida-discover2",
+	5743:  "watchdoc-pod",
+	5744:  "watchdoc",
+	5745:  "fcopy-server",
+	5746:  "fcopys-server",
+	5747:  "tunatic",
+	5748:  "tunalyzer",
+	5750:  "rscd",
+	5755:  "openmailg",
+	5757:  "x500ms",
+	5766:  "openmailns",
+	5767:  "s-openmail",
+	5768:  "openmailpxy",
+	5769:  "spramsca",
+	5770:  "spramsd",
+	5771:  "netagent",
+	5777:  "dali-port",
+	5781:  "3par-evts",
+	5782:  "3par-mgmt",
+	5783:  "3par-mgmt-ssl",
+	5784:  "ibar",
+	5785:  "3par-rcopy",
+	5786:  "cisco-redu",
+	5787:  "waascluster",
+	5793:  "xtreamx",
+	5794:  "spdp",
+	5813:  "icmpd",
+	5814:  "spt-automation",
+	5859:  "wherehoo",
+	5863:  "ppsuitemsg",
+	5900:  "rfb",
+	5910:  "cm",
+	5911:  "cpdlc",
+	5912:  "fis",
+	5913:  "ads-c",
+	5963:  "indy",
+	5968:  "mppolicy-v5",
+	5969:  "mppolicy-mgr",
+	5984:  "couchdb",
+	5985:  "wsman",
+	5986:  "wsmans",
+	5987:  "wbem-rmi",
+	5988:  "wbem-http",
+	5989:  "wbem-https",
+	5990:  "wbem-exp-https",
+	5991:  "nuxsl",
+	5992:  "consul-insight",
+	5999:  "cvsup",
+	6064:  "ndl-ahp-svc",
+	6065:  "winpharaoh",
+	6066:  "ewctsp",
+	6069:  "trip",
+	6070:  "messageasap",
+	6071:  "ssdtp",
+	6072:  "diagnose-proc",
+	6073:  "directplay8",
+	6074:  "max",
+	6080:  "gue",
+	6081:  "geneve",
+	6082:  "p25cai",
+	6083:  "miami-bcast",
+	6085:  "konspire2b",
+	6086:  "pdtp",
+	6087:  "ldss",
+	6088:  "doglms-notify",
+	6100:  "synchronet-db",
+	6101:  "synchronet-rtc",
+	6102:  "synchronet-upd",
+	6103:  "rets",
+	6104:  "dbdb",
+	6105:  "primaserver",
+	6106:  "mpsserver",
+	6107:  "etc-control",
+	6108:  "sercomm-scadmin",
+	6109:  "globecast-id",
+	6110:  "softcm",
+	6111:  "spc",
+	6112:  "dtspcd",
+	6118:  "tipc",
+	6122:  "bex-webadmin",
+	6123:  "backup-express",
+	6124:  "pnbs",
+	6133:  "nbt-wol",
+	6140:  "pulsonixnls",
+	6141:  "meta-corp",
+	6142:  "aspentec-lm",
+	6143:  "watershed-lm",
+	6144:  "statsci1-lm",
+	6145:  "statsci2-lm",
+	6146:  "lonewolf-lm",
+	6147:  "montage-lm",
+	6148:  "ricardo-lm",
+	6149:  "tal-pod",
+	6160:  "ecmp-data",
+	6161:  "patrol-ism",
+	6162:  "patrol-coll",
+	6163:  "pscribe",
+	6200:  "lm-x",
+	6201:  "thermo-calc",
+	6209:  "qmtps",
+	6222:  "radmind",
+	6241:  "jeol-nsddp-1",
+	6242:  "jeol-nsddp-2",
+	6243:  "jeol-nsddp-3",
+	6244:  "jeol-nsddp-4",
+	6251:  "tl1-raw-ssl",
+	6252:  "tl1-ssh",
+	6253:  "crip",
+	6268:  "grid",
+	6269:  "grid-alt",
+	6300:  "bmc-grx",
+	6301:  "bmc-ctd-ldap",
+	6306:  "ufmp",
+	6315:  "scup-disc",
+	6316:  "abb-escp",
+	6317:  "nav-data",
+	6320:  "repsvc",
+	6321:  "emp-server1",
+	6322:  "emp-server2",
+	6324:  "hrd-ns-disc",
+	6343:  "sflow",
+	6346:  "gnutella-svc",
+	6347:  "gnutella-rtr",
+	6350:  "adap",
+	6355:  "pmcs",
+	6360:  "metaedit-mu",
+	6363:  "ndn",
+	6370:  "metaedit-se",
+	6382:  "metatude-mds",
+	6389:  "clariion-evr01",
+	6390:  "metaedit-ws",
+	6417:  "faxcomservice",
+	6419:  "svdrp-disc",
+	6420:  "nim-vdrshell",
+	6421:  "nim-wan",
+	6443:  "sun-sr-https",
+	6444:  "sge-qmaster",
+	6445:  "sge-execd",
+	6446:  "mysql-proxy",
+	6455:  "skip-cert-recv",
+	6456:  "skip-cert-send",
+	6464:  "ieee11073-20701",
+	6471:  "lvision-lm",
+	6480:  "sun-sr-http",
+	6481:  "servicetags",
+	6482:  "ldoms-mgmt",
+	6483:  "SunVTS-RMI",
+	6484:  "sun-sr-jms",
+	6485:  "sun-sr-iiop",
+	6486:  "sun-sr-iiops",
+	6487:  "sun-sr-iiop-aut",
+	6488:  "sun-sr-jmx",
+	6489:  "sun-sr-admin",
+	6500:  "boks",
+	6501:  "boks-servc",
+	6502:  "boks-servm",
+	6503:  "boks-clntd",
+	6505:  "badm-priv",
+	6506:  "badm-pub",
+	6507:  "bdir-priv",
+	6508:  "bdir-pub",
+	6509:  "mgcs-mfp-port",
+	6510:  "mcer-port",
+	6511:  "dccp-udp",
+	6514:  "syslog-tls",
+	6515:  "elipse-rec",
+	6543:  "lds-distrib",
+	6544:  "lds-dump",
+	6547:  "apc-6547",
+	6548:  "apc-6548",
+	6549:  "apc-6549",
+	6550:  "fg-sysupdate",
+	6551:  "sum",
+	6558:  "xdsxdm",
+	6566:  "sane-port",
+	6568:  "rp-reputation",
+	6579:  "affiliate",
+	6580:  "parsec-master",
+	6581:  "parsec-peer",
+	6582:  "parsec-game",
+	6583:  "joaJewelSuite",
+	6619:  "odette-ftps",
+	6620:  "kftp-data",
+	6621:  "kftp",
+	6622:  "mcftp",
+	6623:  "ktelnet",
+	6626:  "wago-service",
+	6627:  "nexgen",
+	6628:  "afesc-mc",
+	6629:  "nexgen-aux",
+	6633:  "cisco-vpath-tun",
+	6634:  "mpls-pm",
+	6635:  "mpls-udp",
+	6636:  "mpls-udp-dtls",
+	6653:  "openflow",
+	6657:  "palcom-disc",
+	6670:  "vocaltec-gold",
+	6671:  "p4p-portal",
+	6672:  "vision-server",
+	6673:  "vision-elmd",
+	6678:  "vfbp-disc",
+	6679:  "osaut",
+	6689:  "tsa",
+	6696:  "babel",
+	6701:  "kti-icad-srvr",
+	6702:  "e-design-net",
+	6703:  "e-design-web",
+	6714:  "ibprotocol",
+	6715:  "fibotrader-com",
+	6767:  "bmc-perf-agent",
+	6768:  "bmc-perf-mgrd",
+	6769:  "adi-gxp-srvprt",
+	6770:  "plysrv-http",
+	6771:  "plysrv-https",
+	6784:  "bfd-lag",
+	6785:  "dgpf-exchg",
+	6786:  "smc-jmx",
+	6787:  "smc-admin",
+	6788:  "smc-http",
+	6790:  "hnmp",
+	6791:  "hnm",
+	6801:  "acnet",
+	6831:  "ambit-lm",
+	6841:  "netmo-default",
+	6842:  "netmo-http",
+	6850:  "iccrushmore",
+	6868:  "acctopus-st",
+	6888:  "muse",
+	6935:  "ethoscan",
+	6936:  "xsmsvc",
+	6946:  "bioserver",
+	6951:  "otlp",
+	6961:  "jmact3",
+	6962:  "jmevt2",
+	6963:  "swismgr1",
+	6964:  "swismgr2",
+	6965:  "swistrap",
+	6966:  "swispol",
+	6969:  "acmsoda",
+	6997:  "MobilitySrv",
+	6998:  "iatp-highpri",
+	6999:  "iatp-normalpri",
+	7000:  "afs3-fileserver",
+	7001:  "afs3-callback",
+	7002:  "afs3-prserver",
+	7003:  "afs3-vlserver",
+	7004:  "afs3-kaserver",
+	7005:  "afs3-volser",
+	7006:  "afs3-errors",
+	7007:  "afs3-bos",
+	7008:  "afs3-update",
+	7009:  "afs3-rmtsys",
+	7010:  "ups-onlinet",
+	7011:  "talon-disc",
+	7012:  "talon-engine",
+	7013:  "microtalon-dis",
+	7014:  "microtalon-com",
+	7015:  "talon-webserver",
+	7016:  "spg",
+	7017:  "grasp",
+	7019:  "doceri-view",
+	7020:  "dpserve",
+	7021:  "dpserveadmin",
+	7022:  "ctdp",
+	7023:  "ct2nmcs",
+	7024:  "vmsvc",
+	7025:  "vmsvc-2",
+	7030:  "op-probe",
+	7040:  "quest-disc",
+	7070:  "arcp",
+	7071:  "iwg1",
+	7080:  "empowerid",
+	7088:  "zixi-transport",
+	7095:  "jdp-disc",
+	7099:  "lazy-ptop",
+	7100:  "font-service",
+	7101:  "elcn",
+	7107:  "aes-x170",
+	7121:  "virprot-lm",
+	7128:  "scenidm",
+	7129:  "scenccs",
+	7161:  "cabsm-comm",
+	7162:  "caistoragemgr",
+	7163:  "cacsambroker",
+	7164:  "fsr",
+	7165:  "doc-server",
+	7166:  "aruba-server",
+	7169:  "ccag-pib",
+	7170:  "nsrp",
+	7171:  "drm-production",
+	7174:  "clutild",
+	7181:  "janus-disc",
+	7200:  "fodms",
+	7201:  "dlip",
+	7227:  "ramp",
+	7235:  "aspcoordination",
+	7244:  "frc-hicp-disc",
+	7262:  "cnap",
+	7272:  "watchme-7272",
+	7273:  "oma-rlp",
+	7274:  "oma-rlp-s",
+	7275:  "oma-ulp",
+	7276:  "oma-ilp",
+	7277:  "oma-ilp-s",
+	7278:  "oma-dcdocbs",
+	7279:  "ctxlic",
+	7280:  "itactionserver1",
+	7281:  "itactionserver2",
+	7282:  "mzca-alert",
+	7365:  "lcm-server",
+	7391:  "mindfilesys",
+	7392:  "mrssrendezvous",
+	7393:  "nfoldman",
+	7394:  "fse",
+	7395:  "winqedit",
+	7397:  "hexarc",
+	7400:  "rtps-discovery",
+	7401:  "rtps-dd-ut",
+	7402:  "rtps-dd-mt",
+	7410:  "ionixnetmon",
+	7411:  "daqstream",
+	7421:  "mtportmon",
+	7426:  "pmdmgr",
+	7427:  "oveadmgr",
+	7428:  "ovladmgr",
+	7429:  "opi-sock",
+	7430:  "xmpv7",
+	7431:  "pmd",
+	7437:  "faximum",
+	7443:  "oracleas-https",
+	7473:  "rise",
+	7491:  "telops-lmd",
+	7500:  "silhouette",
+	7501:  "ovbus",
+	7510:  "ovhpas",
+	7511:  "pafec-lm",
+	7542:  "saratoga",
+	7543:  "atul",
+	7544:  "nta-ds",
+	7545:  "nta-us",
+	7546:  "cfs",
+	7547:  "cwmp",
+	7548:  "tidp",
+	7549:  "nls-tl",
+	7550:  "cloudsignaling",
+	7560:  "sncp",
+	7566:  "vsi-omega",
+	7570:  "aries-kfinder",
+	7574:  "coherence-disc",
+	7588:  "sun-lm",
+	7606:  "mipi-debug",
+	7624:  "indi",
+	7627:  "soap-http",
+	7628:  "zen-pawn",
+	7629:  "xdas",
+	7633:  "pmdfmgt",
+	7648:  "cuseeme",
+	7674:  "imqtunnels",
+	7675:  "imqtunnel",
+	7676:  "imqbrokerd",
+	7677:  "sun-user-https",
+	7680:  "pando-pub",
+	7689:  "collaber",
+	7697:  "klio",
+	7707:  "sync-em7",
+	7708:  "scinet",
+	7720:  "medimageportal",
+	7724:  "nsdeepfreezectl",
+	7725:  "nitrogen",
+	7726:  "freezexservice",
+	7727:  "trident-data",
+	7728:  "osvr",
+	7734:  "smip",
+	7738:  "aiagent",
+	7741:  "scriptview",
+	7743:  "sstp-1",
+	7744:  "raqmon-pdu",
+	7747:  "prgp",
+	7777:  "cbt",
+	7778:  "interwise",
+	7779:  "vstat",
+	7781:  "accu-lmgr",
+	7784:  "s-bfd",
+	7786:  "minivend",
+	7787:  "popup-reminders",
+	7789:  "office-tools",
+	7794:  "q3ade",
+	7797:  "pnet-conn",
+	7798:  "pnet-enc",
+	7799:  "altbsdp",
+	7800:  "asr",
+	7801:  "ssp-client",
+	7802:  "vns-tp",
+	7810:  "rbt-wanopt",
+	7845:  "apc-7845",
+	7846:  "apc-7846",
+	7872:  "mipv6tls",
+	7880:  "pss",
+	7887:  "ubroker",
+	7900:  "mevent",
+	7901:  "tnos-sp",
+	7902:  "tnos-dp",
+	7903:  "tnos-dps",
+	7913:  "qo-secure",
+	7932:  "t2-drm",
+	7933:  "t2-brm",
+	7962:  "generalsync",
+	7967:  "supercell",
+	7979:  "micromuse-ncps",
+	7980:  "quest-vista",
+	7982:  "sossd-disc",
+	7998:  "usicontentpush",
+	7999:  "irdmi2",
+	8000:  "irdmi",
+	8001:  "vcom-tunnel",
+	8002:  "teradataordbms",
+	8003:  "mcreport",
+	8005:  "mxi",
+	8006:  "wpl-disc",
+	8007:  "warppipe",
+	8008:  "http-alt",
+	8019:  "qbdb",
+	8020:  "intu-ec-svcdisc",
+	8021:  "intu-ec-client",
+	8022:  "oa-system",
+	8025:  "ca-audit-da",
+	8026:  "ca-audit-ds",
+	8032:  "pro-ed",
+	8033:  "mindprint",
+	8034:  "vantronix-mgmt",
+	8040:  "ampify",
+	8041:  "enguity-xccetp",
+	8052:  "senomix01",
+	8053:  "senomix02",
+	8054:  "senomix03",
+	8055:  "senomix04",
+	8056:  "senomix05",
+	8057:  "senomix06",
+	8058:  "senomix07",
+	8059:  "senomix08",
+	8060:  "aero",
+	8074:  "gadugadu",
+	8080:  "http-alt",
+	8081:  "sunproxyadmin",
+	8082:  "us-cli",
+	8083:  "us-srv",
+	8086:  "d-s-n",
+	8087:  "simplifymedia",
+	8088:  "radan-http",
+	8097:  "sac",
+	8100:  "xprint-server",
+	8115:  "mtl8000-matrix",
+	8116:  "cp-cluster",
+	8118:  "privoxy",
+	8121:  "apollo-data",
+	8122:  "apollo-admin",
+	8128:  "paycash-online",
+	8129:  "paycash-wbp",
+	8130:  "indigo-vrmi",
+	8131:  "indigo-vbcp",
+	8132:  "dbabble",
+	8148:  "isdd",
+	8149:  "eor-game",
+	8160:  "patrol",
+	8161:  "patrol-snmp",
+	8182:  "vmware-fdm",
+	8184:  "itach",
+	8192:  "spytechphone",
+	8194:  "blp1",
+	8195:  "blp2",
+	8199:  "vvr-data",
+	8200:  "trivnet1",
+	8201:  "trivnet2",
+	8202:  "aesop",
+	8204:  "lm-perfworks",
+	8205:  "lm-instmgr",
+	8206:  "lm-dta",
+	8207:  "lm-sserver",
+	8208:  "lm-webwatcher",
+	8230:  "rexecj",
+	8231:  "hncp-udp-port",
+	8232:  "hncp-dtls-port",
+	8243:  "synapse-nhttps",
+	8276:  "pando-sec",
+	8280:  "synapse-nhttp",
+	8282:  "libelle-disc",
+	8292:  "blp3",
+	8294:  "blp4",
+	8300:  "tmi",
+	8301:  "amberon",
+	8320:  "tnp-discover",
+	8321:  "tnp",
+	8322:  "garmin-marine",
+	8351:  "server-find",
+	8376:  "cruise-enum",
+	8377:  "cruise-swroute",
+	8378:  "cruise-config",
+	8379:  "cruise-diags",
+	8380:  "cruise-update",
+	8383:  "m2mservices",
+	8384:  "marathontp",
+	8400:  "cvd",
+	8401:  "sabarsd",
+	8402:  "abarsd",
+	8403:  "admind",
+	8416:  "espeech",
+	8417:  "espeech-rtp",
+	8442:  "cybro-a-bus",
+	8443:  "pcsync-https",
+	8444:  "pcsync-http",
+	8445:  "copy-disc",
+	8450:  "npmp",
+	8472:  "otv",
+	8473:  "vp2p",
+	8474:  "noteshare",
+	8500:  "fmtp",
+	8501:  "cmtp-av",
+	8503:  "lsp-self-ping",
+	8554:  "rtsp-alt",
+	8555:  "d-fence",
+	8567:  "dof-tunnel",
+	8600:  "asterix",
+	8609:  "canon-cpp-disc",
+	8610:  "canon-mfnp",
+	8611:  "canon-bjnp1",
+	8612:  "canon-bjnp2",
+	8613:  "canon-bjnp3",
+	8614:  "canon-bjnp4",
+	8675:  "msi-cps-rm-disc",
+	8686:  "sun-as-jmxrmi",
+	8732:  "dtp-net",
+	8733:  "ibus",
+	8763:  "mc-appserver",
+	8764:  "openqueue",
+	8765:  "ultraseek-http",
+	8766:  "amcs",
+	8770:  "dpap",
+	8786:  "msgclnt",
+	8787:  "msgsrvr",
+	8793:  "acd-pm",
+	8800:  "sunwebadmin",
+	8804:  "truecm",
+	8805:  "pfcp",
+	8808:  "ssports-bcast",
+	8873:  "dxspider",
+	8880:  "cddbp-alt",
+	8883:  "secure-mqtt",
+	8888:  "ddi-udp-1",
+	8889:  "ddi-udp-2",
+	8890:  "ddi-udp-3",
+	8891:  "ddi-udp-4",
+	8892:  "ddi-udp-5",
+	8893:  "ddi-udp-6",
+	8894:  "ddi-udp-7",
+	8899:  "ospf-lite",
+	8900:  "jmb-cds1",
+	8901:  "jmb-cds2",
+	8910:  "manyone-http",
+	8911:  "manyone-xml",
+	8912:  "wcbackup",
+	8913:  "dragonfly",
+	8954:  "cumulus-admin",
+	8980:  "nod-provider",
+	8981:  "nod-client",
+	8989:  "sunwebadmins",
+	8990:  "http-wmap",
+	8991:  "https-wmap",
+	8999:  "bctp",
+	9000:  "cslistener",
+	9001:  "etlservicemgr",
+	9002:  "dynamid",
+	9007:  "ogs-client",
+	9009:  "pichat",
+	9020:  "tambora",
+	9021:  "panagolin-ident",
+	9022:  "paragent",
+	9023:  "swa-1",
+	9024:  "swa-2",
+	9025:  "swa-3",
+	9026:  "swa-4",
+	9060:  "CardWeb-RT",
+	9080:  "glrpc",
+	9084:  "aurora",
+	9085:  "ibm-rsyscon",
+	9086:  "net2display",
+	9087:  "classic",
+	9088:  "sqlexec",
+	9089:  "sqlexec-ssl",
+	9090:  "websm",
+	9091:  "xmltec-xmlmail",
+	9092:  "XmlIpcRegSvc",
+	9100:  "hp-pdl-datastr",
+	9101:  "bacula-dir",
+	9102:  "bacula-fd",
+	9103:  "bacula-sd",
+	9104:  "peerwire",
+	9105:  "xadmin",
+	9106:  "astergate-disc",
+	9119:  "mxit",
+	9131:  "dddp",
+	9160:  "apani1",
+	9161:  "apani2",
+	9162:  "apani3",
+	9163:  "apani4",
+	9164:  "apani5",
+	9191:  "sun-as-jpda",
+	9200:  "wap-wsp",
+	9201:  "wap-wsp-wtp",
+	9202:  "wap-wsp-s",
+	9203:  "wap-wsp-wtp-s",
+	9204:  "wap-vcard",
+	9205:  "wap-vcal",
+	9206:  "wap-vcard-s",
+	9207:  "wap-vcal-s",
+	9208:  "rjcdb-vcards",
+	9209:  "almobile-system",
+	9210:  "oma-mlp",
+	9211:  "oma-mlp-s",
+	9212:  "serverviewdbms",
+	9213:  "serverstart",
+	9214:  "ipdcesgbs",
+	9215:  "insis",
+	9216:  "acme",
+	9217:  "fsc-port",
+	9222:  "teamcoherence",
+	9255:  "mon",
+	9277:  "traingpsdata",
+	9278:  "pegasus",
+	9279:  "pegasus-ctl",
+	9280:  "pgps",
+	9281:  "swtp-port1",
+	9282:  "swtp-port2",
+	9283:  "callwaveiam",
+	9284:  "visd",
+	9285:  "n2h2server",
+	9286:  "n2receive",
+	9287:  "cumulus",
+	9292:  "armtechdaemon",
+	9293:  "storview",
+	9294:  "armcenterhttp",
+	9295:  "armcenterhttps",
+	9300:  "vrace",
+	9318:  "secure-ts",
+	9321:  "guibase",
+	9343:  "mpidcmgr",
+	9344:  "mphlpdmc",
+	9346:  "ctechlicensing",
+	9374:  "fjdmimgr",
+	9380:  "boxp",
+	9396:  "fjinvmgr",
+	9397:  "mpidcagt",
+	9400:  "sec-t4net-srv",
+	9401:  "sec-t4net-clt",
+	9402:  "sec-pc2fax-srv",
+	9418:  "git",
+	9443:  "tungsten-https",
+	9444:  "wso2esb-console",
+	9450:  "sntlkeyssrvr",
+	9500:  "ismserver",
+	9522:  "sma-spw",
+	9535:  "mngsuite",
+	9536:  "laes-bf",
+	9555:  "trispen-sra",
+	9592:  "ldgateway",
+	9593:  "cba8",
+	9594:  "msgsys",
+	9595:  "pds",
+	9596:  "mercury-disc",
+	9597:  "pd-admin",
+	9598:  "vscp",
+	9599:  "robix",
+	9600:  "micromuse-ncpw",
+	9612:  "streamcomm-ds",
+	9618:  "condor",
+	9628:  "odbcpathway",
+	9629:  "uniport",
+	9632:  "mc-comm",
+	9667:  "xmms2",
+	9668:  "tec5-sdctp",
+	9694:  "client-wakeup",
+	9695:  "ccnx",
+	9700:  "board-roar",
+	9747:  "l5nas-parchan",
+	9750:  "board-voip",
+	9753:  "rasadv",
+	9762:  "tungsten-http",
+	9800:  "davsrc",
+	9801:  "sstp-2",
+	9802:  "davsrcs",
+	9875:  "sapv1",
+	9878:  "kca-service",
+	9888:  "cyborg-systems",
+	9889:  "gt-proxy",
+	9898:  "monkeycom",
+	9899:  "sctp-tunneling",
+	9900:  "iua",
+	9901:  "enrp",
+	9903:  "multicast-ping",
+	9909:  "domaintime",
+	9911:  "sype-transport",
+	9950:  "apc-9950",
+	9951:  "apc-9951",
+	9952:  "apc-9952",
+	9953:  "acis",
+	9955:  "alljoyn-mcm",
+	9956:  "alljoyn",
+	9966:  "odnsp",
+	9987:  "dsm-scm-target",
+	9990:  "osm-appsrvr",
+	9991:  "osm-oev",
+	9992:  "palace-1",
+	9993:  "palace-2",
+	9994:  "palace-3",
+	9995:  "palace-4",
+	9996:  "palace-5",
+	9997:  "palace-6",
+	9998:  "distinct32",
+	9999:  "distinct",
+	10000: "ndmp",
+	10001: "scp-config",
+	10002: "documentum",
+	10003: "documentum-s",
+	10007: "mvs-capacity",
+	10008: "octopus",
+	10009: "swdtp-sv",
+	10050: "zabbix-agent",
+	10051: "zabbix-trapper",
+	10080: "amanda",
+	10081: "famdc",
+	10100: "itap-ddtp",
+	10101: "ezmeeting-2",
+	10102: "ezproxy-2",
+	10103: "ezrelay",
+	10104: "swdtp",
+	10107: "bctp-server",
+	10110: "nmea-0183",
+	10111: "nmea-onenet",
+	10113: "netiq-endpoint",
+	10114: "netiq-qcheck",
+	10115: "netiq-endpt",
+	10116: "netiq-voipa",
+	10117: "iqrm",
+	10128: "bmc-perf-sd",
+	10160: "qb-db-server",
+	10161: "snmpdtls",
+	10162: "snmpdtls-trap",
+	10200: "trisoap",
+	10201: "rscs",
+	10252: "apollo-relay",
+	10253: "eapol-relay",
+	10260: "axis-wimp-port",
+	10288: "blocks",
+	10439: "bngsync",
+	10500: "hip-nat-t",
+	10540: "MOS-lower",
+	10541: "MOS-upper",
+	10542: "MOS-aux",
+	10543: "MOS-soap",
+	10544: "MOS-soap-opt",
+	10800: "gap",
+	10805: "lpdg",
+	10810: "nmc-disc",
+	10860: "helix",
+	10880: "bveapi",
+	10990: "rmiaux",
+	11000: "irisa",
+	11001: "metasys",
+	10023: "cefd-vmp",
+	11095: "weave",
+	11106: "sgi-lk",
+	11108: "myq-termlink",
+	11111: "vce",
+	11112: "dicom",
+	11161: "suncacao-snmp",
+	11162: "suncacao-jmxmp",
+	11163: "suncacao-rmi",
+	11164: "suncacao-csa",
+	11165: "suncacao-websvc",
+	11171: "snss",
+	11201: "smsqp",
+	11208: "wifree",
+	11211: "memcache",
+	11319: "imip",
+	11320: "imip-channels",
+	11321: "arena-server",
+	11367: "atm-uhas",
+	11371: "hkp",
+	11430: "lsdp",
+	11600: "tempest-port",
+	11720: "h323callsigalt",
+	11723: "emc-xsw-dcache",
+	11751: "intrepid-ssl",
+	11796: "lanschool-mpt",
+	11876: "xoraya",
+	11877: "x2e-disc",
+	11967: "sysinfo-sp",
+	12000: "entextxid",
+	12001: "entextnetwk",
+	12002: "entexthigh",
+	12003: "entextmed",
+	12004: "entextlow",
+	12005: "dbisamserver1",
+	12006: "dbisamserver2",
+	12007: "accuracer",
+	12008: "accuracer-dbms",
+	12009: "ghvpn",
+	12012: "vipera",
+	12013: "vipera-ssl",
+	12109: "rets-ssl",
+	12121: "nupaper-ss",
+	12168: "cawas",
+	12172: "hivep",
+	12300: "linogridengine",
+	12321: "warehouse-sss",
+	12322: "warehouse",
+	12345: "italk",
+	12753: "tsaf",
+	13160: "i-zipqd",
+	13216: "bcslogc",
+	13217: "rs-pias",
+	13218: "emc-vcas-udp",
+	13223: "powwow-client",
+	13224: "powwow-server",
+	13400: "doip-disc",
+	13720: "bprd",
+	13721: "bpdbm",
+	13722: "bpjava-msvc",
+	13724: "vnetd",
+	13782: "bpcd",
+	13783: "vopied",
+	13785: "nbdb",
+	13786: "nomdb",
+	13818: "dsmcc-config",
+	13819: "dsmcc-session",
+	13820: "dsmcc-passthru",
+	13821: "dsmcc-download",
+	13822: "dsmcc-ccp",
+	13894: "ucontrol",
+	13929: "dta-systems",
+	14000: "scotty-ft",
+	14001: "sua",
+	14002: "scotty-disc",
+	14033: "sage-best-com1",
+	14034: "sage-best-com2",
+	14141: "vcs-app",
+	14142: "icpp",
+	14145: "gcm-app",
+	14149: "vrts-tdd",
+	14154: "vad",
+	14250: "cps",
+	14414: "ca-web-update",
+	14936: "hde-lcesrvr-1",
+	14937: "hde-lcesrvr-2",
+	15000: "hydap",
+	15118: "v2g-secc",
+	15345: "xpilot",
+	15363: "3link",
+	15555: "cisco-snat",
+	15660: "bex-xr",
+	15740: "ptp",
+	15998: "2ping",
+	16003: "alfin",
+	16161: "sun-sea-port",
+	16309: "etb4j",
+	16310: "pduncs",
+	16311: "pdefmns",
+	16360: "netserialext1",
+	16361: "netserialext2",
+	16367: "netserialext3",
+	16368: "netserialext4",
+	16384: "connected",
+	16666: "vtp",
+	16900: "newbay-snc-mc",
+	16950: "sgcip",
+	16991: "intel-rci-mp",
+	16992: "amt-soap-http",
+	16993: "amt-soap-https",
+	16994: "amt-redir-tcp",
+	16995: "amt-redir-tls",
+	17007: "isode-dua",
+	17185: "soundsvirtual",
+	17219: "chipper",
+	17220: "avtp",
+	17221: "avdecc",
+	17222: "cpsp",
+	17224: "trdp-pd",
+	17225: "trdp-md",
+	17234: "integrius-stp",
+	17235: "ssh-mgmt",
+	17500: "db-lsp-disc",
+	17729: "ea",
+	17754: "zep",
+	17755: "zigbee-ip",
+	17756: "zigbee-ips",
+	18000: "biimenu",
+	18181: "opsec-cvp",
+	18182: "opsec-ufp",
+	18183: "opsec-sam",
+	18184: "opsec-lea",
+	18185: "opsec-omi",
+	18186: "ohsc",
+	18187: "opsec-ela",
+	18241: "checkpoint-rtm",
+	18262: "gv-pf",
+	18463: "ac-cluster",
+	18634: "rds-ib",
+	18635: "rds-ip",
+	18668: "vdmmesh-disc",
+	18769: "ique",
+	18881: "infotos",
+	18888: "apc-necmp",
+	19000: "igrid",
+	19007: "scintilla",
+	19191: "opsec-uaa",
+	19194: "ua-secureagent",
+	19220: "cora-disc",
+	19283: "keysrvr",
+	19315: "keyshadow",
+	19398: "mtrgtrans",
+	19410: "hp-sco",
+	19411: "hp-sca",
+	19412: "hp-sessmon",
+	19539: "fxuptp",
+	19540: "sxuptp",
+	19541: "jcp",
+	19788: "mle",
+	19999: "dnp-sec",
+	20000: "dnp",
+	20001: "microsan",
+	20002: "commtact-http",
+	20003: "commtact-https",
+	20005: "openwebnet",
+	20012: "ss-idi-disc",
+	20014: "opendeploy",
+	20034: "nburn-id",
+	20046: "tmophl7mts",
+	20048: "mountd",
+	20049: "nfsrdma",
+	20167: "tolfab",
+	20202: "ipdtp-port",
+	20222: "ipulse-ics",
+	20480: "emwavemsg",
+	20670: "track",
+	20999: "athand-mmp",
+	21000: "irtrans",
+	21554: "dfserver",
+	21590: "vofr-gateway",
+	21800: "tvpm",
+	21845: "webphone",
+	21846: "netspeak-is",
+	21847: "netspeak-cs",
+	21848: "netspeak-acd",
+	21849: "netspeak-cps",
+	22000: "snapenetio",
+	22001: "optocontrol",
+	22002: "optohost002",
+	22003: "optohost003",
+	22004: "optohost004",
+	22005: "optohost004",
+	22273: "wnn6",
+	22305: "cis",
+	22335: "shrewd-stream",
+	22343: "cis-secure",
+	22347: "wibukey",
+	22350: "codemeter",
+	22555: "vocaltec-phone",
+	22763: "talikaserver",
+	22800: "aws-brf",
+	22951: "brf-gw",
+	23000: "inovaport1",
+	23001: "inovaport2",
+	23002: "inovaport3",
+	23003: "inovaport4",
+	23004: "inovaport5",
+	23005: "inovaport6",
+	23272: "s102",
+	23294: "5afe-disc",
+	23333: "elxmgmt",
+	23400: "novar-dbase",
+	23401: "novar-alarm",
+	23402: "novar-global",
+	24000: "med-ltp",
+	24001: "med-fsp-rx",
+	24002: "med-fsp-tx",
+	24003: "med-supp",
+	24004: "med-ovw",
+	24005: "med-ci",
+	24006: "med-net-svc",
+	24242: "filesphere",
+	24249: "vista-4gl",
+	24321: "ild",
+	24322: "hid",
+	24386: "intel-rci",
+	24465: "tonidods",
+	24554: "binkp",
+	24577: "bilobit-update",
+	24676: "canditv",
+	24677: "flashfiler",
+	24678: "proactivate",
+	24680: "tcc-http",
+	24850: "assoc-disc",
+	24922: "find",
+	25000: "icl-twobase1",
+	25001: "icl-twobase2",
+	25002: "icl-twobase3",
+	25003: "icl-twobase4",
+	25004: "icl-twobase5",
+	25005: "icl-twobase6",
+	25006: "icl-twobase7",
+	25007: "icl-twobase8",
+	25008: "icl-twobase9",
+	25009: "icl-twobase10",
+	25793: "vocaltec-hos",
+	25900: "tasp-net",
+	25901: "niobserver",
+	25902: "nilinkanalyst",
+	25903: "niprobe",
+	25954: "bf-game",
+	25955: "bf-master",
+	26000: "quake",
+	26133: "scscp",
+	26208: "wnn6-ds",
+	26260: "ezproxy",
+	26261: "ezmeeting",
+	26262: "k3software-svr",
+	26263: "k3software-cli",
+	26486: "exoline-udp",
+	26487: "exoconfig",
+	26489: "exonet",
+	27345: "imagepump",
+	27442: "jesmsjc",
+	27504: "kopek-httphead",
+	27782: "ars-vista",
+	27999: "tw-auth-key",
+	28000: "nxlmd",
+	28119: "a27-ran-ran",
+	28200: "voxelstorm",
+	28240: "siemensgsm",
+	29167: "otmp",
+	30001: "pago-services1",
+	30002: "pago-services2",
+	30003: "amicon-fpsu-ra",
+	30004: "amicon-fpsu-s",
+	30260: "kingdomsonline",
+	30832: "samsung-disc",
+	30999: "ovobs",
+	31016: "ka-kdp",
+	31029: "yawn",
+	31416: "xqosd",
+	31457: "tetrinet",
+	31620: "lm-mon",
+	31765: "gamesmith-port",
+	31948: "iceedcp-tx",
+	31949: "iceedcp-rx",
+	32034: "iracinghelper",
+	32249: "t1distproc60",
+	32483: "apm-link",
+	32635: "sec-ntb-clnt",
+	32636: "DMExpress",
+	32767: "filenet-powsrm",
+	32768: "filenet-tms",
+	32769: "filenet-rpc",
+	32770: "filenet-nch",
+	32771: "filenet-rmi",
+	32772: "filenet-pa",
+	32773: "filenet-cm",
+	32774: "filenet-re",
+	32775: "filenet-pch",
+	32776: "filenet-peior",
+	32777: "filenet-obrok",
+	32801: "mlsn",
+	32896: "idmgratm",
+	33123: "aurora-balaena",
+	33331: "diamondport",
+	33334: "speedtrace-disc",
+	33434: "traceroute",
+	33656: "snip-slave",
+	34249: "turbonote-2",
+	34378: "p-net-local",
+	34379: "p-net-remote",
+	34567: "edi_service",
+	34962: "profinet-rt",
+	34963: "profinet-rtm",
+	34964: "profinet-cm",
+	34980: "ethercat",
+	35001: "rt-viewer",
+	35004: "rt-classmanager",
+	35100: "axio-disc",
+	35355: "altova-lm-disc",
+	36001: "allpeers",
+	36411: "wlcp",
+	36865: "kastenxpipe",
+	37475: "neckar",
+	37654: "unisys-eportal",
+	38002: "crescoctrl-disc",
+	38201: "galaxy7-data",
+	38202: "fairview",
+	38203: "agpolicy",
+	39681: "turbonote-1",
+	40000: "safetynetp",
+	40023: "k-patentssensor",
+	40841: "cscp",
+	40842: "csccredir",
+	40843: "csccfirewall",
+	40853: "ortec-disc",
+	41111: "fs-qos",
+	41230: "z-wave-s",
+	41794: "crestron-cip",
+	41795: "crestron-ctp",
+	42508: "candp",
+	42509: "candrp",
+	42510: "caerpc",
+	43000: "recvr-rc-disc",
+	43188: "reachout",
+	43189: "ndm-agent-port",
+	43190: "ip-provision",
+	43210: "shaperai-disc",
+	43439: "eq3-config",
+	43440: "ew-disc-cmd",
+	43441: "ciscocsdb",
+	44321: "pmcd",
+	44322: "pmcdproxy",
+	44544: "domiq",
+	44553: "rbr-debug",
+	44600: "asihpi",
+	44818: "EtherNet-IP-2",
+	44900: "m3da-disc",
+	45000: "asmp-mon",
+	45054: "invision-ag",
+	45514: "cloudcheck-ping",
+	45678: "eba",
+	45825: "qdb2service",
+	45966: "ssr-servermgr",
+	46999: "mediabox",
+	47000: "mbus",
+	47100: "jvl-mactalk",
+	47557: "dbbrowse",
+	47624: "directplaysrvr",
+	47806: "ap",
+	47808: "bacnet",
+	47809: "presonus-ucnet",
+	48000: "nimcontroller",
+	48001: "nimspooler",
+	48002: "nimhub",
+	48003: "nimgtw",
+	48128: "isnetserv",
+	48129: "blp5",
+	48556: "com-bardac-dw",
+	48619: "iqobject",
+	48653: "robotraconteur",
+	49001: "nusdp-disc",
+}
+var sctpPortNames = map[SCTPPort]string{
+	9:     "discard",
+	20:    "ftp-data",
+	21:    "ftp",
+	22:    "ssh",
+	80:    "http",
+	179:   "bgp",
+	443:   "https",
+	1021:  "exp1",
+	1022:  "exp2",
+	1167:  "cisco-ipsla",
+	1720:  "h323hostcall",
+	2049:  "nfs",
+	2225:  "rcip-itu",
+	2904:  "m2ua",
+	2905:  "m3ua",
+	2944:  "megaco-h248",
+	2945:  "h248-binary",
+	3097:  "itu-bicc-stc",
+	3565:  "m2pa",
+	3863:  "asap-sctp",
+	3864:  "asap-sctp-tls",
+	3868:  "diameter",
+	4333:  "ahsp",
+	4502:  "a25-fap-fgw",
+	4711:  "trinity-dist",
+	4739:  "ipfix",
+	4740:  "ipfixs",
+	5060:  "sip",
+	5061:  "sips",
+	5090:  "car",
+	5091:  "cxtp",
+	5215:  "noteza",
+	5445:  "smbdirect",
+	5672:  "amqp",
+	5675:  "v5ua",
+	5868:  "diameters",
+	5910:  "cm",
+	5911:  "cpdlc",
+	5912:  "fis",
+	5913:  "ads-c",
+	6704:  "frc-hp",
+	6705:  "frc-mp",
+	6706:  "frc-lp",
+	6970:  "conductor-mpx",
+	7626:  "simco",
+	7701:  "nfapi",
+	7728:  "osvr",
+	8471:  "pim-port",
+	9082:  "lcs-ap",
+	9084:  "aurora",
+	9900:  "iua",
+	9901:  "enrp-sctp",
+	9902:  "enrp-sctp-tls",
+	11997: "wmereceiving",
+	11998: "wmedistribution",
+	11999: "wmereporting",
+	14001: "sua",
+	20049: "nfsrdma",
+	25471: "rna",
+	29118: "sgsap",
+	29168: "sbcap",
+	29169: "iuhsctpassoc",
+	30100: "rwp",
+	36412: "s1-control",
+	36422: "x2-control",
+	36423: "slmap",
+	36424: "nq-ap",
+	36443: "m2ap",
+	36444: "m3ap",
+	36462: "xw-control",
+	38412: "ng-control",
+	38422: "xn-control",
+	38472: "f1-control",
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/icmp4.go b/go-controller/vendor/github.com/google/gopacket/layers/icmp4.go
new file mode 100644
index 00000000000..bd3f03f00c2
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/icmp4.go
@@ -0,0 +1,267 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"reflect"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	ICMPv4TypeEchoReply              = 0
+	ICMPv4TypeDestinationUnreachable = 3
+	ICMPv4TypeSourceQuench           = 4
+	ICMPv4TypeRedirect               = 5
+	ICMPv4TypeEchoRequest            = 8
+	ICMPv4TypeRouterAdvertisement    = 9
+	ICMPv4TypeRouterSolicitation     = 10
+	ICMPv4TypeTimeExceeded           = 11
+	ICMPv4TypeParameterProblem       = 12
+	ICMPv4TypeTimestampRequest       = 13
+	ICMPv4TypeTimestampReply         = 14
+	ICMPv4TypeInfoRequest            = 15
+	ICMPv4TypeInfoReply              = 16
+	ICMPv4TypeAddressMaskRequest     = 17
+	ICMPv4TypeAddressMaskReply       = 18
+)
+
+const (
+	// DestinationUnreachable
+	ICMPv4CodeNet                 = 0
+	ICMPv4CodeHost                = 1
+	ICMPv4CodeProtocol            = 2
+	ICMPv4CodePort                = 3
+	ICMPv4CodeFragmentationNeeded = 4
+	ICMPv4CodeSourceRoutingFailed = 5
+	ICMPv4CodeNetUnknown          = 6
+	ICMPv4CodeHostUnknown         = 7
+	ICMPv4CodeSourceIsolated      = 8
+	ICMPv4CodeNetAdminProhibited  = 9
+	ICMPv4CodeHostAdminProhibited = 10
+	ICMPv4CodeNetTOS              = 11
+	ICMPv4CodeHostTOS             = 12
+	ICMPv4CodeCommAdminProhibited = 13
+	ICMPv4CodeHostPrecedence      = 14
+	ICMPv4CodePrecedenceCutoff    = 15
+
+	// TimeExceeded
+	ICMPv4CodeTTLExceeded                    = 0
+	ICMPv4CodeFragmentReassemblyTimeExceeded = 1
+
+	// ParameterProblem
+	ICMPv4CodePointerIndicatesError = 0
+	ICMPv4CodeMissingOption         = 1
+	ICMPv4CodeBadLength             = 2
+
+	// Redirect
+	// ICMPv4CodeNet  = same as for DestinationUnreachable
+	// ICMPv4CodeHost = same as for DestinationUnreachable
+	ICMPv4CodeTOSNet  = 2
+	ICMPv4CodeTOSHost = 3
+)
+
+type icmpv4TypeCodeInfoStruct struct {
+	typeStr string
+	codeStr *map[uint8]string
+}
+
+var (
+	icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
+		ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
+			"DestinationUnreachable", &map[uint8]string{
+				ICMPv4CodeNet:                 "Net",
+				ICMPv4CodeHost:                "Host",
+				ICMPv4CodeProtocol:            "Protocol",
+				ICMPv4CodePort:                "Port",
+				ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
+				ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
+				ICMPv4CodeNetUnknown:          "NetUnknown",
+				ICMPv4CodeHostUnknown:         "HostUnknown",
+				ICMPv4CodeSourceIsolated:      "SourceIsolated",
+				ICMPv4CodeNetAdminProhibited:  "NetAdminProhibited",
+				ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
+				ICMPv4CodeNetTOS:              "NetTOS",
+				ICMPv4CodeHostTOS:             "HostTOS",
+				ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
+				ICMPv4CodeHostPrecedence:      "HostPrecedence",
+				ICMPv4CodePrecedenceCutoff:    "PrecedenceCutoff",
+			},
+		},
+		ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
+			"TimeExceeded", &map[uint8]string{
+				ICMPv4CodeTTLExceeded:                    "TTLExceeded",
+				ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
+			},
+		},
+		ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
+			"ParameterProblem", &map[uint8]string{
+				ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
+				ICMPv4CodeMissingOption:         "MissingOption",
+				ICMPv4CodeBadLength:             "BadLength",
+			},
+		},
+		ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
+			"SourceQuench", nil,
+		},
+		ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
+			"Redirect", &map[uint8]string{
+				ICMPv4CodeNet:     "Net",
+				ICMPv4CodeHost:    "Host",
+				ICMPv4CodeTOSNet:  "TOS+Net",
+				ICMPv4CodeTOSHost: "TOS+Host",
+			},
+		},
+		ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
+			"EchoRequest", nil,
+		},
+		ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
+			"EchoReply", nil,
+		},
+		ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
+			"TimestampRequest", nil,
+		},
+		ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
+			"TimestampReply", nil,
+		},
+		ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
+			"InfoRequest", nil,
+		},
+		ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
+			"InfoReply", nil,
+		},
+		ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
+			"RouterSolicitation", nil,
+		},
+		ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
+			"RouterAdvertisement", nil,
+		},
+		ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
+			"AddressMaskRequest", nil,
+		},
+		ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
+			"AddressMaskReply", nil,
+		},
+	}
+)
+
+type ICMPv4TypeCode uint16
+
+// Type returns the ICMPv4 type field.
+func (a ICMPv4TypeCode) Type() uint8 {
+	return uint8(a >> 8)
+}
+
+// Code returns the ICMPv4 code field.
+func (a ICMPv4TypeCode) Code() uint8 {
+	return uint8(a)
+}
+
+func (a ICMPv4TypeCode) String() string {
+	t, c := a.Type(), a.Code()
+	strInfo, ok := icmpv4TypeCodeInfo[t]
+	if !ok {
+		// Unknown ICMPv4 type field
+		return fmt.Sprintf("%d(%d)", t, c)
+	}
+	typeStr := strInfo.typeStr
+	if strInfo.codeStr == nil && c == 0 {
+		// The ICMPv4 type does not make use of the code field
+		return fmt.Sprintf("%s", strInfo.typeStr)
+	}
+	if strInfo.codeStr == nil && c != 0 {
+		// The ICMPv4 type does not make use of the code field, but it is present anyway
+		return fmt.Sprintf("%s(Code: %d)", typeStr, c)
+	}
+	codeStr, ok := (*strInfo.codeStr)[c]
+	if !ok {
+		// We don't know this ICMPv4 code; print the numerical value
+		return fmt.Sprintf("%s(Code: %d)", typeStr, c)
+	}
+	return fmt.Sprintf("%s(%s)", typeStr, codeStr)
+}
+
+func (a ICMPv4TypeCode) GoString() string {
+	t := reflect.TypeOf(a)
+	return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
+}
+
+// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
+func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
+	binary.BigEndian.PutUint16(bytes, uint16(a))
+}
+
+// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
+// gopacket type from the ICMPv4 type and code values.
+func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
+	return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
+}
+
+// ICMPv4 is the layer for IPv4 ICMP packet data.
+type ICMPv4 struct {
+	BaseLayer
+	TypeCode ICMPv4TypeCode
+	Checksum uint16
+	Id       uint16
+	Seq      uint16
+}
+
+// LayerType returns LayerTypeICMPv4.
+func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
+	}
+	i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.Id = binary.BigEndian.Uint16(data[4:6])
+	i.Seq = binary.BigEndian.Uint16(data[6:8])
+	i.BaseLayer = BaseLayer{data[:8], data[8:]}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+	i.TypeCode.SerializeTo(bytes)
+	binary.BigEndian.PutUint16(bytes[4:], i.Id)
+	binary.BigEndian.PutUint16(bytes[6:], i.Seq)
+	if opts.ComputeChecksums {
+		bytes[2] = 0
+		bytes[3] = 0
+		i.Checksum = tcpipChecksum(b.Bytes(), 0)
+	}
+	binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv4) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv4
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv4) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv4{}
+	return decodingLayerDecoder(i, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/icmp6.go b/go-controller/vendor/github.com/google/gopacket/layers/icmp6.go
new file mode 100644
index 00000000000..09afd11a609
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/icmp6.go
@@ -0,0 +1,266 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"reflect"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// The following are from RFC 4443
+	ICMPv6TypeDestinationUnreachable = 1
+	ICMPv6TypePacketTooBig           = 2
+	ICMPv6TypeTimeExceeded           = 3
+	ICMPv6TypeParameterProblem       = 4
+	ICMPv6TypeEchoRequest            = 128
+	ICMPv6TypeEchoReply              = 129
+
+	// The following are from RFC 4861
+	ICMPv6TypeRouterSolicitation    = 133
+	ICMPv6TypeRouterAdvertisement   = 134
+	ICMPv6TypeNeighborSolicitation  = 135
+	ICMPv6TypeNeighborAdvertisement = 136
+	ICMPv6TypeRedirect              = 137
+
+	// The following are from RFC 2710
+	ICMPv6TypeMLDv1MulticastListenerQueryMessage  = 130
+	ICMPv6TypeMLDv1MulticastListenerReportMessage = 131
+	ICMPv6TypeMLDv1MulticastListenerDoneMessage   = 132
+
+	// The following are from RFC 3810
+	ICMPv6TypeMLDv2MulticastListenerReportMessageV2 = 143
+)
+
+const (
+	// DestinationUnreachable
+	ICMPv6CodeNoRouteToDst           = 0
+	ICMPv6CodeAdminProhibited        = 1
+	ICMPv6CodeBeyondScopeOfSrc       = 2
+	ICMPv6CodeAddressUnreachable     = 3
+	ICMPv6CodePortUnreachable        = 4
+	ICMPv6CodeSrcAddressFailedPolicy = 5
+	ICMPv6CodeRejectRouteToDst       = 6
+
+	// TimeExceeded
+	ICMPv6CodeHopLimitExceeded               = 0
+	ICMPv6CodeFragmentReassemblyTimeExceeded = 1
+
+	// ParameterProblem
+	ICMPv6CodeErroneousHeaderField   = 0
+	ICMPv6CodeUnrecognizedNextHeader = 1
+	ICMPv6CodeUnrecognizedIPv6Option = 2
+)
+
+type icmpv6TypeCodeInfoStruct struct {
+	typeStr string
+	codeStr *map[uint8]string
+}
+
+var (
+	icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{
+		ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{
+			"DestinationUnreachable", &map[uint8]string{
+				ICMPv6CodeNoRouteToDst:           "NoRouteToDst",
+				ICMPv6CodeAdminProhibited:        "AdminProhibited",
+				ICMPv6CodeBeyondScopeOfSrc:       "BeyondScopeOfSrc",
+				ICMPv6CodeAddressUnreachable:     "AddressUnreachable",
+				ICMPv6CodePortUnreachable:        "PortUnreachable",
+				ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy",
+				ICMPv6CodeRejectRouteToDst:       "RejectRouteToDst",
+			},
+		},
+		ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{
+			"PacketTooBig", nil,
+		},
+		ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{
+			"TimeExceeded", &map[uint8]string{
+				ICMPv6CodeHopLimitExceeded:               "HopLimitExceeded",
+				ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
+			},
+		},
+		ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{
+			"ParameterProblem", &map[uint8]string{
+				ICMPv6CodeErroneousHeaderField:   "ErroneousHeaderField",
+				ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader",
+				ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option",
+			},
+		},
+		ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{
+			"EchoRequest", nil,
+		},
+		ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{
+			"EchoReply", nil,
+		},
+		ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{
+			"RouterSolicitation", nil,
+		},
+		ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{
+			"RouterAdvertisement", nil,
+		},
+		ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{
+			"NeighborSolicitation", nil,
+		},
+		ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{
+			"NeighborAdvertisement", nil,
+		},
+		ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{
+			"Redirect", nil,
+		},
+	}
+)
+
+type ICMPv6TypeCode uint16
+
+// Type returns the ICMPv6 type field.
+func (a ICMPv6TypeCode) Type() uint8 {
+	return uint8(a >> 8)
+}
+
+// Code returns the ICMPv6 code field.
+func (a ICMPv6TypeCode) Code() uint8 {
+	return uint8(a)
+}
+
+func (a ICMPv6TypeCode) String() string {
+	t, c := a.Type(), a.Code()
+	strInfo, ok := icmpv6TypeCodeInfo[t]
+	if !ok {
+		// Unknown ICMPv6 type field
+		return fmt.Sprintf("%d(%d)", t, c)
+	}
+	typeStr := strInfo.typeStr
+	if strInfo.codeStr == nil && c == 0 {
+		// The ICMPv6 type does not make use of the code field
+		return fmt.Sprintf("%s", strInfo.typeStr)
+	}
+	if strInfo.codeStr == nil && c != 0 {
+		// The ICMPv6 type does not make use of the code field, but it is present anyway
+		return fmt.Sprintf("%s(Code: %d)", typeStr, c)
+	}
+	codeStr, ok := (*strInfo.codeStr)[c]
+	if !ok {
+		// We don't know this ICMPv6 code; print the numerical value
+		return fmt.Sprintf("%s(Code: %d)", typeStr, c)
+	}
+	return fmt.Sprintf("%s(%s)", typeStr, codeStr)
+}
+
+func (a ICMPv6TypeCode) GoString() string {
+	t := reflect.TypeOf(a)
+	return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
+}
+
+// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer.
+func (a ICMPv6TypeCode) SerializeTo(bytes []byte) {
+	binary.BigEndian.PutUint16(bytes, uint16(a))
+}
+
+// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode
+// gopacket type from the ICMPv6 type and code values.
+func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode {
+	return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
+}
+
+// ICMPv6 is the layer for IPv6 ICMP packet data
+type ICMPv6 struct {
+	BaseLayer
+	TypeCode ICMPv6TypeCode
+	Checksum uint16
+	// TypeBytes is deprecated and always nil. See the different ICMPv6 message types
+	// instead (e.g. ICMPv6TypeRouterSolicitation).
+	TypeBytes []byte
+	tcpipchecksum
+}
+
+// LayerType returns LayerTypeICMPv6.
+func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 4 bytes for ICMPv6 packet")
+	}
+	i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.BaseLayer = BaseLayer{data[:4], data[4:]}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	i.TypeCode.SerializeTo(bytes)
+
+	if opts.ComputeChecksums {
+		bytes[2] = 0
+		bytes[3] = 0
+		csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6)
+		if err != nil {
+			return err
+		}
+		i.Checksum = csum
+	}
+	binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
+
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6) NextLayerType() gopacket.LayerType {
+	switch i.TypeCode.Type() {
+	case ICMPv6TypeEchoRequest:
+		return LayerTypeICMPv6Echo
+	case ICMPv6TypeEchoReply:
+		return LayerTypeICMPv6Echo
+	case ICMPv6TypeRouterSolicitation:
+		return LayerTypeICMPv6RouterSolicitation
+	case ICMPv6TypeRouterAdvertisement:
+		return LayerTypeICMPv6RouterAdvertisement
+	case ICMPv6TypeNeighborSolicitation:
+		return LayerTypeICMPv6NeighborSolicitation
+	case ICMPv6TypeNeighborAdvertisement:
+		return LayerTypeICMPv6NeighborAdvertisement
+	case ICMPv6TypeRedirect:
+		return LayerTypeICMPv6Redirect
+	case ICMPv6TypeMLDv1MulticastListenerQueryMessage: // Same Code for MLDv1 Query and MLDv2 Query
+		if len(i.Payload) > 20 { // Only payload size differs
+			return LayerTypeMLDv2MulticastListenerQuery
+		} else {
+			return LayerTypeMLDv1MulticastListenerQuery
+		}
+	case ICMPv6TypeMLDv1MulticastListenerDoneMessage:
+		return LayerTypeMLDv1MulticastListenerDone
+	case ICMPv6TypeMLDv1MulticastListenerReportMessage:
+		return LayerTypeMLDv1MulticastListenerReport
+	case ICMPv6TypeMLDv2MulticastListenerReportMessageV2:
+		return LayerTypeMLDv2MulticastListenerReport
+	}
+
+	return gopacket.LayerTypePayload
+}
+
+func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6{}
+	return decodingLayerDecoder(i, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/icmp6msg.go b/go-controller/vendor/github.com/google/gopacket/layers/icmp6msg.go
new file mode 100644
index 00000000000..d9268db0563
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/icmp6msg.go
@@ -0,0 +1,578 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"net"
+	"time"
+
+	"github.com/google/gopacket"
+)
+
+// Based on RFC 4861
+
+// ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option.
+type ICMPv6Opt uint8
+
+const (
+	_ ICMPv6Opt = iota
+
+	// ICMPv6OptSourceAddress contains the link-layer address of the sender of
+	// the packet. It is used in the Neighbor Solicitation, Router
+	// Solicitation, and Router Advertisement packets. Must be ignored for other
+	// Neighbor discovery messages.
+	ICMPv6OptSourceAddress
+
+	// ICMPv6OptTargetAddress contains the link-layer address of the target. It
+	// is used in Neighbor Advertisement and Redirect packets. Must be ignored
+	// for other Neighbor discovery messages.
+	ICMPv6OptTargetAddress
+
+	// ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes
+	// for Address Autoconfiguration. The Prefix Information option appears in
+	// Router Advertisement packets and MUST be silently ignored for other
+	// messages.
+	ICMPv6OptPrefixInfo
+
+	// ICMPv6OptRedirectedHeader is used in Redirect messages and contains all
+	// or part of the packet that is being redirected.
+	ICMPv6OptRedirectedHeader
+
+	// ICMPv6OptMTU is used in Router Advertisement messages to ensure that all
+	// nodes on a link use the same MTU value in those cases where the link MTU
+	// is not well known. This option MUST be silently ignored for other
+	// Neighbor Discovery messages.
+	ICMPv6OptMTU
+)
+
+// ICMPv6Echo represents the structure of a ping.
+type ICMPv6Echo struct {
+	BaseLayer
+	Identifier uint16
+	SeqNumber  uint16
+}
+
+// ICMPv6RouterSolicitation is sent by hosts to find routers.
+type ICMPv6RouterSolicitation struct {
+	BaseLayer
+	Options ICMPv6Options
+}
+
+// ICMPv6RouterAdvertisement is sent by routers in response to Solicitation.
+type ICMPv6RouterAdvertisement struct {
+	BaseLayer
+	HopLimit       uint8
+	Flags          uint8
+	RouterLifetime uint16
+	ReachableTime  uint32
+	RetransTimer   uint32
+	Options        ICMPv6Options
+}
+
+// ICMPv6NeighborSolicitation is sent to request the link-layer address of a
+// target node.
+type ICMPv6NeighborSolicitation struct {
+	BaseLayer
+	TargetAddress net.IP
+	Options       ICMPv6Options
+}
+
+// ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation.
+type ICMPv6NeighborAdvertisement struct {
+	BaseLayer
+	Flags         uint8
+	TargetAddress net.IP
+	Options       ICMPv6Options
+}
+
+// ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node
+// on the path to a destination.
+type ICMPv6Redirect struct {
+	BaseLayer
+	TargetAddress      net.IP
+	DestinationAddress net.IP
+	Options            ICMPv6Options
+}
+
+// ICMPv6Option contains the type and data for a single option.
+type ICMPv6Option struct {
+	Type ICMPv6Opt
+	Data []byte
+}
+
+// ICMPv6Options is a slice of ICMPv6Option.
+type ICMPv6Options []ICMPv6Option
+
+func (i ICMPv6Opt) String() string {
+	switch i {
+	case ICMPv6OptSourceAddress:
+		return "SourceAddress"
+	case ICMPv6OptTargetAddress:
+		return "TargetAddress"
+	case ICMPv6OptPrefixInfo:
+		return "PrefixInfo"
+	case ICMPv6OptRedirectedHeader:
+		return "RedirectedHeader"
+	case ICMPv6OptMTU:
+		return "MTU"
+	default:
+		return fmt.Sprintf("Unknown(%d)", i)
+	}
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6Echo
+}
+
+// LayerType returns LayerTypeICMPv6Echo.
+func (i *ICMPv6Echo) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6Echo
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6Echo) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6Echo) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 4 bytes for ICMPv6 Echo")
+	}
+	i.Identifier = binary.BigEndian.Uint16(data[0:2])
+	i.SeqNumber = binary.BigEndian.Uint16(data[2:4])
+
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6Echo) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+
+	binary.BigEndian.PutUint16(buf, i.Identifier)
+	binary.BigEndian.PutUint16(buf[2:], i.SeqNumber)
+	return nil
+}
+
+// LayerType returns LayerTypeICMPv6.
+func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6RouterSolicitation
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	// first 4 bytes are reserved followed by options
+	if len(data) < 4 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation")
+	}
+
+	// truncate old options
+	i.Options = i.Options[:0]
+
+	return i.Options.DecodeFromBytes(data[4:], df)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := i.Options.SerializeTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+
+	copy(buf, lotsOfZeros[:4])
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6RouterSolicitation) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6RouterSolicitation
+}
+
+// LayerType returns LayerTypeICMPv6RouterAdvertisement.
+func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6RouterAdvertisement
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 12 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement")
+	}
+
+	i.HopLimit = uint8(data[0])
+	// M, O bit followed by 6 reserved bits
+	i.Flags = uint8(data[1])
+	i.RouterLifetime = binary.BigEndian.Uint16(data[2:4])
+	i.ReachableTime = binary.BigEndian.Uint32(data[4:8])
+	i.RetransTimer = binary.BigEndian.Uint32(data[8:12])
+	i.BaseLayer = BaseLayer{data, nil} // assume no payload
+
+	// truncate old options
+	i.Options = i.Options[:0]
+
+	return i.Options.DecodeFromBytes(data[12:], df)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := i.Options.SerializeTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(12)
+	if err != nil {
+		return err
+	}
+
+	buf[0] = byte(i.HopLimit)
+	buf[1] = byte(i.Flags)
+	binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime)
+	binary.BigEndian.PutUint32(buf[4:], i.ReachableTime)
+	binary.BigEndian.PutUint32(buf[8:], i.RetransTimer)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6RouterAdvertisement) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6RouterAdvertisement
+}
+
+// ManagedAddressConfig is true when addresses are available via DHCPv6. If
+// set, the OtherConfig flag is redundant.
+func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool {
+	return i.Flags&0x80 != 0
+}
+
+// OtherConfig is true when there is other configuration information available
+// via DHCPv6. For example, DNS-related information.
+func (i *ICMPv6RouterAdvertisement) OtherConfig() bool {
+	return i.Flags&0x40 != 0
+}
+
+// LayerType returns LayerTypeICMPv6NeighborSolicitation.
+func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6NeighborSolicitation
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation")
+	}
+
+	i.TargetAddress = net.IP(data[4:20])
+	i.BaseLayer = BaseLayer{data, nil} // assume no payload
+
+	// truncate old options
+	i.Options = i.Options[:0]
+
+	return i.Options.DecodeFromBytes(data[20:], df)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := i.Options.SerializeTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(20)
+	if err != nil {
+		return err
+	}
+
+	copy(buf, lotsOfZeros[:4])
+	copy(buf[4:], i.TargetAddress)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6NeighborSolicitation) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6NeighborSolicitation
+}
+
+// LayerType returns LayerTypeICMPv6NeighborAdvertisement.
+func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6NeighborAdvertisement
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement")
+	}
+
+	i.Flags = uint8(data[0])
+	i.TargetAddress = net.IP(data[4:20])
+	i.BaseLayer = BaseLayer{data, nil} // assume no payload
+
+	// truncate old options
+	i.Options = i.Options[:0]
+
+	return i.Options.DecodeFromBytes(data[20:], df)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := i.Options.SerializeTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(20)
+	if err != nil {
+		return err
+	}
+
+	buf[0] = byte(i.Flags)
+	copy(buf[1:], lotsOfZeros[:3])
+	copy(buf[4:], i.TargetAddress)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6NeighborAdvertisement) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6NeighborAdvertisement
+}
+
+// Router indicates whether the sender is a router or not.
+func (i *ICMPv6NeighborAdvertisement) Router() bool {
+	return i.Flags&0x80 != 0
+}
+
+// Solicited indicates whether the advertisement was solicited or not.
+func (i *ICMPv6NeighborAdvertisement) Solicited() bool {
+	return i.Flags&0x40 != 0
+}
+
+// Override indicates whether the advertisement should Override an existing
+// cache entry.
+func (i *ICMPv6NeighborAdvertisement) Override() bool {
+	return i.Flags&0x20 != 0
+}
+
+// LayerType returns LayerTypeICMPv6Redirect.
+func (i *ICMPv6Redirect) LayerType() gopacket.LayerType {
+	return LayerTypeICMPv6Redirect
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 36 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect")
+	}
+
+	i.TargetAddress = net.IP(data[4:20])
+	i.DestinationAddress = net.IP(data[20:36])
+	i.BaseLayer = BaseLayer{data, nil} // assume no payload
+
+	// truncate old options
+	i.Options = i.Options[:0]
+
+	return i.Options.DecodeFromBytes(data[36:], df)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := i.Options.SerializeTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(36)
+	if err != nil {
+		return err
+	}
+
+	copy(buf, lotsOfZeros[:4])
+	copy(buf[4:], i.TargetAddress)
+	copy(buf[20:], i.DestinationAddress)
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *ICMPv6Redirect) CanDecode() gopacket.LayerClass {
+	return LayerTypeICMPv6Redirect
+}
+
+func (i ICMPv6Option) String() string {
+	hd := hex.EncodeToString(i.Data)
+	if len(hd) > 0 {
+		hd = " 0x" + hd
+	}
+
+	switch i.Type {
+	case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress:
+		return fmt.Sprintf("ICMPv6Option(%s:%v)",
+			i.Type,
+			net.HardwareAddr(i.Data))
+	case ICMPv6OptPrefixInfo:
+		if len(i.Data) == 30 {
+			prefixLen := uint8(i.Data[0])
+			onLink := (i.Data[1]&0x80 != 0)
+			autonomous := (i.Data[1]&0x40 != 0)
+			validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second
+			preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second
+
+			prefix := net.IP(i.Data[14:])
+
+			return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)",
+				i.Type,
+				prefix, prefixLen,
+				onLink, autonomous,
+				validLifetime, preferredLifetime)
+		}
+	case ICMPv6OptRedirectedHeader:
+		// could invoke IP decoder on data... probably best not to
+		break
+	case ICMPv6OptMTU:
+		if len(i.Data) == 6 {
+			return fmt.Sprintf("ICMPv6Option(%s:%v)",
+				i.Type,
+				binary.BigEndian.Uint32(i.Data[2:]))
+		}
+
+	}
+	return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd)
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	for len(data) > 0 {
+		if len(data) < 2 {
+			df.SetTruncated()
+			return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option")
+		}
+
+		// unit is 8 octets, convert to bytes
+		length := int(data[1]) * 8
+
+		if length == 0 {
+			df.SetTruncated()
+			return errors.New("ICMPv6 message option with length 0")
+		}
+
+		if len(data) < length {
+			df.SetTruncated()
+			return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length)
+		}
+
+		o := ICMPv6Option{
+			Type: ICMPv6Opt(data[0]),
+			Data: data[2:length],
+		}
+
+		// chop off option we just consumed
+		data = data[length:]
+
+		*i = append(*i, o)
+	}
+
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	for _, opt := range []ICMPv6Option(*i) {
+		length := len(opt.Data) + 2
+		buf, err := b.PrependBytes(length)
+		if err != nil {
+			return err
+		}
+
+		buf[0] = byte(opt.Type)
+		buf[1] = byte(length / 8)
+		copy(buf[2:], opt.Data)
+	}
+
+	return nil
+}
+
+func decodeICMPv6Echo(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6Echo{}
+	return decodingLayerDecoder(i, data, p)
+}
+
+func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6RouterSolicitation{}
+	return decodingLayerDecoder(i, data, p)
+}
+
+func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6RouterAdvertisement{}
+	return decodingLayerDecoder(i, data, p)
+}
+
+func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6NeighborSolicitation{}
+	return decodingLayerDecoder(i, data, p)
+}
+
+func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6NeighborAdvertisement{}
+	return decodingLayerDecoder(i, data, p)
+}
+
+func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error {
+	i := &ICMPv6Redirect{}
+	return decodingLayerDecoder(i, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/igmp.go b/go-controller/vendor/github.com/google/gopacket/layers/igmp.go
new file mode 100644
index 00000000000..d00841535bb
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/igmp.go
@@ -0,0 +1,355 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"net"
+	"time"
+
+	"github.com/google/gopacket"
+)
+
+type IGMPType uint8
+
+const (
+	IGMPMembershipQuery    IGMPType = 0x11 // General or group specific query
+	IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
+	IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
+	IGMPLeaveGroup         IGMPType = 0x17 // Leave Group
+	IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
+)
+
+// String conversions for IGMP message types
+func (i IGMPType) String() string {
+	switch i {
+	case IGMPMembershipQuery:
+		return "IGMP Membership Query"
+	case IGMPMembershipReportV1:
+		return "IGMPv1 Membership Report"
+	case IGMPMembershipReportV2:
+		return "IGMPv2 Membership Report"
+	case IGMPMembershipReportV3:
+		return "IGMPv3 Membership Report"
+	case IGMPLeaveGroup:
+		return "Leave Group"
+	default:
+		return ""
+	}
+}
+
+type IGMPv3GroupRecordType uint8
+
+const (
+	IGMPIsIn  IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
+	IGMPIsEx  IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
+	IGMPToIn  IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
+	IGMPToEx  IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
+	IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
+	IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
+)
+
+func (i IGMPv3GroupRecordType) String() string {
+	switch i {
+	case IGMPIsIn:
+		return "MODE_IS_INCLUDE"
+	case IGMPIsEx:
+		return "MODE_IS_EXCLUDE"
+	case IGMPToIn:
+		return "CHANGE_TO_INCLUDE_MODE"
+	case IGMPToEx:
+		return "CHANGE_TO_EXCLUDE_MODE"
+	case IGMPAllow:
+		return "ALLOW_NEW_SOURCES"
+	case IGMPBlock:
+		return "BLOCK_OLD_SOURCES"
+	default:
+		return ""
+	}
+}
+
+// IGMP represents an IGMPv3 message.
+type IGMP struct {
+	BaseLayer
+	Type                    IGMPType
+	MaxResponseTime         time.Duration
+	Checksum                uint16
+	GroupAddress            net.IP
+	SupressRouterProcessing bool
+	RobustnessValue         uint8
+	IntervalTime            time.Duration
+	SourceAddresses         []net.IP
+	NumberOfGroupRecords    uint16
+	NumberOfSources         uint16
+	GroupRecords            []IGMPv3GroupRecord
+	Version                 uint8 // IGMP protocol version
+}
+
+// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
+//
+//  0                   1                   2                   3
+//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |      Type     | Max Resp Time |           Checksum            |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                         Group Address                         |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+type IGMPv1or2 struct {
+	BaseLayer
+	Type            IGMPType      // IGMP message type
+	MaxResponseTime time.Duration // meaningful only in Membership Query messages
+	Checksum        uint16        // 16-bit checksum of entire ip payload
+	GroupAddress    net.IP        // either 0 or an IP multicast address
+	Version         uint8
+}
+
+// decodeResponse dissects IGMPv1 or IGMPv2 packet.
+func (i *IGMPv1or2) decodeResponse(data []byte) error {
+	if len(data) < 8 {
+		return errors.New("IGMP packet too small")
+	}
+
+	i.MaxResponseTime = igmpTimeDecode(data[1])
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.GroupAddress = net.IP(data[4:8])
+
+	return nil
+}
+
+//  0                   1                   2                   3
+//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |  Type = 0x22  |    Reserved   |           Checksum            |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |           Reserved            |  Number of Group Records (M)  |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                                                               |
+// .                        Group Record [1]                       .
+// |                                                               |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                                                               |
+// .                        Group Record [2]                       .
+// |                                                               |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                                                               |
+// .                        Group Record [M]                       .
+// |                                                               |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |  Record Type  |  Aux Data Len |     Number of Sources (N)     |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       Multicast Address                       |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       Source Address [1]                      |
+// +-                                                             -+
+// |                       Source Address [2]                      |
+// +-                                                             -+
+// |                       Source Address [N]                      |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                                                               |
+// .                         Auxiliary Data                        .
+// |                                                               |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
+type IGMPv3GroupRecord struct {
+	Type             IGMPv3GroupRecordType
+	AuxDataLen       uint8 // this should always be 0 as per IGMPv3 spec.
+	NumberOfSources  uint16
+	MulticastAddress net.IP
+	SourceAddresses  []net.IP
+	AuxData          uint32 // NOT USED
+}
+
+func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
+	if len(data) < 8 {
+		return errors.New("IGMPv3 Membership Report too small #1")
+	}
+
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
+
+	recordOffset := 8
+	for j := 0; j < int(i.NumberOfGroupRecords); j++ {
+		if len(data) < recordOffset+8 {
+			return errors.New("IGMPv3 Membership Report too small #2")
+		}
+
+		var gr IGMPv3GroupRecord
+		gr.Type = IGMPv3GroupRecordType(data[recordOffset])
+		gr.AuxDataLen = data[recordOffset+1]
+		gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
+		gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
+
+		if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
+			return errors.New("IGMPv3 Membership Report too small #3")
+		}
+
+		// append source address records.
+		for i := 0; i < int(gr.NumberOfSources); i++ {
+			sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
+			gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
+		}
+
+		i.GroupRecords = append(i.GroupRecords, gr)
+		recordOffset += 8 + 4*int(gr.NumberOfSources)
+	}
+	return nil
+}
+
+//  0                   1                   2                   3
+//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |  Type = 0x11  | Max Resp Code |           Checksum            |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                         Group Address                         |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Resv  |S| QRV |     QQIC      |     Number of Sources (N)     |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |                       Source Address [1]                      |
+// +-                                                             -+
+// |                       Source Address [2]                      |
+// +-                              .                              -+
+// |                       Source Address [N]                      |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
+func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
+	if len(data) < 12 {
+		return errors.New("IGMPv3 Membership Query too small #1")
+	}
+
+	i.MaxResponseTime = igmpTimeDecode(data[1])
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.SupressRouterProcessing = data[8]&0x8 != 0
+	i.GroupAddress = net.IP(data[4:8])
+	i.RobustnessValue = data[8] & 0x7
+	i.IntervalTime = igmpTimeDecode(data[9])
+	i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
+
+	if len(data) < 12+int(i.NumberOfSources)*4 {
+		return errors.New("IGMPv3 Membership Query too small #2")
+	}
+
+	for j := 0; j < int(i.NumberOfSources); j++ {
+		i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
+	}
+
+	return nil
+}
+
+// igmpTimeDecode decodes the duration created by the given byte, using the
+// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
+func igmpTimeDecode(t uint8) time.Duration {
+	if t&0x80 == 0 {
+		return time.Millisecond * 100 * time.Duration(t)
+	}
+	mant := (t & 0x70) >> 4
+	exp := t & 0x0F
+	return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
+}
+
+// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
+func (i *IGMP) LayerType() gopacket.LayerType      { return LayerTypeIGMP }
+func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
+
+func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		return errors.New("IGMP Packet too small")
+	}
+
+	i.Type = IGMPType(data[0])
+	i.MaxResponseTime = igmpTimeDecode(data[1])
+	i.Checksum = binary.BigEndian.Uint16(data[2:4])
+	i.GroupAddress = net.IP(data[4:8])
+
+	return nil
+}
+
+func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
+	return LayerTypeIGMP
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 1 {
+		return errors.New("IGMP packet is too small")
+	}
+
+	// common IGMP header values between versions 1..3 of IGMP specification..
+	i.Type = IGMPType(data[0])
+
+	switch i.Type {
+	case IGMPMembershipQuery:
+		i.decodeIGMPv3MembershipQuery(data)
+	case IGMPMembershipReportV3:
+		i.decodeIGMPv3MembershipReport(data)
+	default:
+		return errors.New("unsupported IGMP type")
+	}
+
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (i *IGMP) CanDecode() gopacket.LayerClass {
+	return LayerTypeIGMP
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (i *IGMP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
+// IGMP type are performed against byte[0], logic then iniitalizes and
+// passes the appropriate struct (IGMP or IGMPv1or2) to
+// decodingLayerDecoder.
+func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
+	if len(data) < 1 {
+		return errors.New("IGMP packet is too small")
+	}
+
+	// byte 0 contains IGMP message type.
+	switch IGMPType(data[0]) {
+	case IGMPMembershipQuery:
+		// IGMPv3 Membership Query payload is >= 12
+		if len(data) >= 12 {
+			i := &IGMP{Version: 3}
+			return decodingLayerDecoder(i, data, p)
+		} else if len(data) == 8 {
+			i := &IGMPv1or2{}
+			if data[1] == 0x00 {
+				i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
+			} else {
+				i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
+			}
+
+			return decodingLayerDecoder(i, data, p)
+		}
+	case IGMPMembershipReportV3:
+		i := &IGMP{Version: 3}
+		return decodingLayerDecoder(i, data, p)
+	case IGMPMembershipReportV1:
+		i := &IGMPv1or2{Version: 1}
+		return decodingLayerDecoder(i, data, p)
+	case IGMPLeaveGroup, IGMPMembershipReportV2:
+		// leave group and Query Report v2 used in IGMPv2 only.
+		i := &IGMPv1or2{Version: 2}
+		return decodingLayerDecoder(i, data, p)
+	default:
+	}
+
+	return errors.New("Unable to determine IGMP type.")
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ip4.go b/go-controller/vendor/github.com/google/gopacket/layers/ip4.go
new file mode 100644
index 00000000000..2b3c0c6bff9
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ip4.go
@@ -0,0 +1,325 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+	"strings"
+
+	"github.com/google/gopacket"
+)
+
+type IPv4Flag uint8
+
+const (
+	IPv4EvilBit       IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;)
+	IPv4DontFragment  IPv4Flag = 1 << 1
+	IPv4MoreFragments IPv4Flag = 1 << 0
+)
+
+func (f IPv4Flag) String() string {
+	var s []string
+	if f&IPv4EvilBit != 0 {
+		s = append(s, "Evil")
+	}
+	if f&IPv4DontFragment != 0 {
+		s = append(s, "DF")
+	}
+	if f&IPv4MoreFragments != 0 {
+		s = append(s, "MF")
+	}
+	return strings.Join(s, "|")
+}
+
+// IPv4 is the header of an IP packet.
+type IPv4 struct {
+	BaseLayer
+	Version    uint8
+	IHL        uint8
+	TOS        uint8
+	Length     uint16
+	Id         uint16
+	Flags      IPv4Flag
+	FragOffset uint16
+	TTL        uint8
+	Protocol   IPProtocol
+	Checksum   uint16
+	SrcIP      net.IP
+	DstIP      net.IP
+	Options    []IPv4Option
+	Padding    []byte
+}
+
+// LayerType returns LayerTypeIPv4
+func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 }
+func (i *IPv4) NetworkFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP)
+}
+
+type IPv4Option struct {
+	OptionType   uint8
+	OptionLength uint8
+	OptionData   []byte
+}
+
+func (i IPv4Option) String() string {
+	return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData)
+}
+
+// for the current ipv4 options, return the number of bytes (including
+// padding that the options used)
+func (ip *IPv4) getIPv4OptionSize() uint8 {
+	optionSize := uint8(0)
+	for _, opt := range ip.Options {
+		switch opt.OptionType {
+		case 0:
+			// this is the end of option lists
+			optionSize++
+		case 1:
+			// this is the padding
+			optionSize++
+		default:
+			optionSize += opt.OptionLength
+
+		}
+	}
+	// make sure the options are aligned to 32 bit boundary
+	if (optionSize % 4) != 0 {
+		optionSize += 4 - (optionSize % 4)
+	}
+	return optionSize
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	optionLength := ip.getIPv4OptionSize()
+	bytes, err := b.PrependBytes(20 + int(optionLength))
+	if err != nil {
+		return err
+	}
+	if opts.FixLengths {
+		ip.IHL = 5 + (optionLength / 4)
+		ip.Length = uint16(len(b.Bytes()))
+	}
+	bytes[0] = (ip.Version << 4) | ip.IHL
+	bytes[1] = ip.TOS
+	binary.BigEndian.PutUint16(bytes[2:], ip.Length)
+	binary.BigEndian.PutUint16(bytes[4:], ip.Id)
+	binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags())
+	bytes[8] = ip.TTL
+	bytes[9] = byte(ip.Protocol)
+	if err := ip.AddressTo4(); err != nil {
+		return err
+	}
+	copy(bytes[12:16], ip.SrcIP)
+	copy(bytes[16:20], ip.DstIP)
+
+	curLocation := 20
+	// Now, we will encode the options
+	for _, opt := range ip.Options {
+		switch opt.OptionType {
+		case 0:
+			// this is the end of option lists
+			bytes[curLocation] = 0
+			curLocation++
+		case 1:
+			// this is the padding
+			bytes[curLocation] = 1
+			curLocation++
+		default:
+			bytes[curLocation] = opt.OptionType
+			bytes[curLocation+1] = opt.OptionLength
+
+			// sanity checking to protect us from buffer overrun
+			if len(opt.OptionData) > int(opt.OptionLength-2) {
+				return errors.New("option length is smaller than length of option data")
+			}
+			copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData)
+			curLocation += int(opt.OptionLength)
+		}
+	}
+
+	if opts.ComputeChecksums {
+		ip.Checksum = checksum(bytes)
+	}
+	binary.BigEndian.PutUint16(bytes[10:], ip.Checksum)
+	return nil
+}
+
+func checksum(bytes []byte) uint16 {
+	// Clear checksum bytes
+	bytes[10] = 0
+	bytes[11] = 0
+
+	// Compute checksum
+	var csum uint32
+	for i := 0; i < len(bytes); i += 2 {
+		csum += uint32(bytes[i]) << 8
+		csum += uint32(bytes[i+1])
+	}
+	for {
+		// Break when sum is less or equals to 0xFFFF
+		if csum <= 65535 {
+			break
+		}
+		// Add carry to the sum
+		csum = (csum >> 16) + uint32(uint16(csum))
+	}
+	// Flip all the bits
+	return ^uint16(csum)
+}
+
+func (ip *IPv4) flagsfrags() (ff uint16) {
+	ff |= uint16(ip.Flags) << 13
+	ff |= ip.FragOffset
+	return
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data))
+	}
+	flagsfrags := binary.BigEndian.Uint16(data[6:8])
+
+	ip.Version = uint8(data[0]) >> 4
+	ip.IHL = uint8(data[0]) & 0x0F
+	ip.TOS = data[1]
+	ip.Length = binary.BigEndian.Uint16(data[2:4])
+	ip.Id = binary.BigEndian.Uint16(data[4:6])
+	ip.Flags = IPv4Flag(flagsfrags >> 13)
+	ip.FragOffset = flagsfrags & 0x1FFF
+	ip.TTL = data[8]
+	ip.Protocol = IPProtocol(data[9])
+	ip.Checksum = binary.BigEndian.Uint16(data[10:12])
+	ip.SrcIP = data[12:16]
+	ip.DstIP = data[16:20]
+	ip.Options = ip.Options[:0]
+	ip.Padding = nil
+	// Set up an initial guess for contents/payload... we'll reset these soon.
+	ip.BaseLayer = BaseLayer{Contents: data}
+
+	// This code is added for the following enviroment:
+	// * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver )
+	if ip.Length == 0 {
+		// If using TSO(TCP Segmentation Offload), length is zero.
+		// The actual packet length is the length of data.
+		ip.Length = uint16(len(data))
+	}
+
+	if ip.Length < 20 {
+		return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length)
+	} else if ip.IHL < 5 {
+		return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL)
+	} else if int(ip.IHL*4) > int(ip.Length) {
+		return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length)
+	}
+	if cmp := len(data) - int(ip.Length); cmp > 0 {
+		data = data[:ip.Length]
+	} else if cmp < 0 {
+		df.SetTruncated()
+		if int(ip.IHL)*4 > len(data) {
+			return errors.New("Not all IP header bytes available")
+		}
+	}
+	ip.Contents = data[:ip.IHL*4]
+	ip.Payload = data[ip.IHL*4:]
+	// From here on, data contains the header options.
+	data = data[20 : ip.IHL*4]
+	// Pull out IP options
+	for len(data) > 0 {
+		if ip.Options == nil {
+			// Pre-allocate to avoid growing the slice too much.
+			ip.Options = make([]IPv4Option, 0, 4)
+		}
+		opt := IPv4Option{OptionType: data[0]}
+		switch opt.OptionType {
+		case 0: // End of options
+			opt.OptionLength = 1
+			ip.Options = append(ip.Options, opt)
+			ip.Padding = data[1:]
+			return nil
+		case 1: // 1 byte padding
+			opt.OptionLength = 1
+			data = data[1:]
+			ip.Options = append(ip.Options, opt)
+		default:
+			if len(data) < 2 {
+				df.SetTruncated()
+				return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data))
+			}
+			opt.OptionLength = data[1]
+			if len(data) < int(opt.OptionLength) {
+				df.SetTruncated()
+				return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
+			}
+			if opt.OptionLength <= 2 {
+				return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength)
+			}
+			opt.OptionData = data[2:opt.OptionLength]
+			data = data[opt.OptionLength:]
+			ip.Options = append(ip.Options, opt)
+		}
+	}
+	return nil
+}
+
+func (i *IPv4) CanDecode() gopacket.LayerClass {
+	return LayerTypeIPv4
+}
+
+func (i *IPv4) NextLayerType() gopacket.LayerType {
+	if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 {
+		return gopacket.LayerTypeFragment
+	}
+	return i.Protocol.LayerType()
+}
+
+func decodeIPv4(data []byte, p gopacket.PacketBuilder) error {
+	ip := &IPv4{}
+	err := ip.DecodeFromBytes(data, p)
+	p.AddLayer(ip)
+	p.SetNetworkLayer(ip)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(ip.NextLayerType())
+}
+
+func checkIPv4Address(addr net.IP) (net.IP, error) {
+	if c := addr.To4(); c != nil {
+		return c, nil
+	}
+	if len(addr) == net.IPv6len {
+		return nil, errors.New("address is IPv6")
+	}
+	return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len)
+}
+
+func (ip *IPv4) AddressTo4() error {
+	var src, dst net.IP
+
+	if addr, err := checkIPv4Address(ip.SrcIP); err != nil {
+		return fmt.Errorf("Invalid source IPv4 address (%s)", err)
+	} else {
+		src = addr
+	}
+	if addr, err := checkIPv4Address(ip.DstIP); err != nil {
+		return fmt.Errorf("Invalid destination IPv4 address (%s)", err)
+	} else {
+		dst = addr
+	}
+	ip.SrcIP = src
+	ip.DstIP = dst
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ip6.go b/go-controller/vendor/github.com/google/gopacket/layers/ip6.go
new file mode 100644
index 00000000000..87b9d33d513
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ip6.go
@@ -0,0 +1,722 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// IPv6HopByHopOptionJumbogram code as defined in RFC 2675
+	IPv6HopByHopOptionJumbogram = 0xC2
+)
+
+const (
+	ipv6MaxPayloadLength = 65535
+)
+
+// IPv6 is the layer for the IPv6 header.
+type IPv6 struct {
+	// http://www.networksorcery.com/enp/protocol/ipv6.htm
+	BaseLayer
+	Version      uint8
+	TrafficClass uint8
+	FlowLabel    uint32
+	Length       uint16
+	NextHeader   IPProtocol
+	HopLimit     uint8
+	SrcIP        net.IP
+	DstIP        net.IP
+	HopByHop     *IPv6HopByHop
+	// hbh will be pointed to by HopByHop if that layer exists.
+	hbh IPv6HopByHop
+}
+
+// LayerType returns LayerTypeIPv6
+func (ipv6 *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 }
+
+// NetworkFlow returns this new Flow (EndpointIPv6, SrcIP, DstIP)
+func (ipv6 *IPv6) NetworkFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointIPv6, ipv6.SrcIP, ipv6.DstIP)
+}
+
+// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found
+func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) {
+	var tlv *IPv6HopByHopOption
+
+	for _, t := range hopopts.Options {
+		if t.OptionType == IPv6HopByHopOptionJumbogram {
+			tlv = t
+			break
+		}
+	}
+	if tlv == nil {
+		// Not found
+		return 0, false, nil
+	}
+	if len(tlv.OptionData) != 4 {
+		return 0, false, errors.New("Jumbo length TLV data must have length 4")
+	}
+	l := binary.BigEndian.Uint32(tlv.OptionData)
+	if l <= ipv6MaxPayloadLength {
+		return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1)
+	}
+	// Found
+	return l, true, nil
+}
+
+// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist
+// (if necessary add hop-by-hop header)
+func addIPv6JumboOption(ip6 *IPv6) {
+	var tlv *IPv6HopByHopOption
+
+	if ip6.HopByHop == nil {
+		// Add IPv6 HopByHop
+		ip6.HopByHop = &IPv6HopByHop{}
+		ip6.HopByHop.NextHeader = ip6.NextHeader
+		ip6.HopByHop.HeaderLength = 0
+		ip6.NextHeader = IPProtocolIPv6HopByHop
+	}
+	for _, t := range ip6.HopByHop.Options {
+		if t.OptionType == IPv6HopByHopOptionJumbogram {
+			tlv = t
+			break
+		}
+	}
+	if tlv == nil {
+		// Add Jumbo TLV
+		tlv = &IPv6HopByHopOption{}
+		ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv)
+	}
+	tlv.SetJumboLength(0)
+}
+
+// Set jumbo length in serialized IPv6 payload (starting with HopByHop header)
+func setIPv6PayloadJumboLength(hbh []byte) error {
+	pLen := len(hbh)
+	if pLen < 8 {
+		//HopByHop is minimum 8 bytes
+		return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen)
+	}
+	hbhLen := int((hbh[1] + 1) * 8)
+	if hbhLen > pLen {
+		return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen)
+	}
+	offset := 2 //start with options
+	for offset < hbhLen {
+		opt := hbh[offset]
+		if opt == 0 {
+			//Pad1
+			offset++
+			continue
+		}
+		optLen := int(hbh[offset+1])
+		if opt == IPv6HopByHopOptionJumbogram {
+			if optLen == 4 {
+				binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen))
+				return nil
+			}
+			return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen)
+		}
+		offset += 2 + optLen
+	}
+	return errors.New("Jumbo TLV not found")
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (ipv6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var jumbo bool
+	var err error
+
+	payload := b.Bytes()
+	pLen := len(payload)
+	if pLen > ipv6MaxPayloadLength {
+		jumbo = true
+		if opts.FixLengths {
+			// We need to set the length later because the hop-by-hop header may
+			// not exist or else need padding, so pLen may yet change
+			addIPv6JumboOption(ipv6)
+		} else if ipv6.HopByHop == nil {
+			return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen)
+		} else {
+			_, ok, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
+			if err != nil {
+				return err
+			}
+			if !ok {
+				return errors.New("Missing jumbo length hop-by-hop option")
+			}
+		}
+	}
+
+	hbhAlreadySerialized := false
+	if ipv6.HopByHop != nil {
+		for _, l := range b.Layers() {
+			if l == LayerTypeIPv6HopByHop {
+				hbhAlreadySerialized = true
+				break
+			}
+		}
+	}
+	if ipv6.HopByHop != nil && !hbhAlreadySerialized {
+		if ipv6.NextHeader != IPProtocolIPv6HopByHop {
+			// Just fix it instead of throwing an error
+			ipv6.NextHeader = IPProtocolIPv6HopByHop
+		}
+		err = ipv6.HopByHop.SerializeTo(b, opts)
+		if err != nil {
+			return err
+		}
+		payload = b.Bytes()
+		pLen = len(payload)
+		if opts.FixLengths && jumbo {
+			err := setIPv6PayloadJumboLength(payload)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	if !jumbo && pLen > ipv6MaxPayloadLength {
+		return errors.New("Cannot fit payload into IPv6 header")
+	}
+	bytes, err := b.PrependBytes(40)
+	if err != nil {
+		return err
+	}
+	bytes[0] = (ipv6.Version << 4) | (ipv6.TrafficClass >> 4)
+	bytes[1] = (ipv6.TrafficClass << 4) | uint8(ipv6.FlowLabel>>16)
+	binary.BigEndian.PutUint16(bytes[2:], uint16(ipv6.FlowLabel))
+	if opts.FixLengths {
+		if jumbo {
+			ipv6.Length = 0
+		} else {
+			ipv6.Length = uint16(pLen)
+		}
+	}
+	binary.BigEndian.PutUint16(bytes[4:], ipv6.Length)
+	bytes[6] = byte(ipv6.NextHeader)
+	bytes[7] = byte(ipv6.HopLimit)
+	if err := ipv6.AddressTo16(); err != nil {
+		return err
+	}
+	copy(bytes[8:], ipv6.SrcIP)
+	copy(bytes[24:], ipv6.DstIP)
+	return nil
+}
+
+// DecodeFromBytes implementation according to gopacket.DecodingLayer
+func (ipv6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 40 {
+		df.SetTruncated()
+		return fmt.Errorf("Invalid ip6 header. Length %d less than 40", len(data))
+	}
+	ipv6.Version = uint8(data[0]) >> 4
+	ipv6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
+	ipv6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
+	ipv6.Length = binary.BigEndian.Uint16(data[4:6])
+	ipv6.NextHeader = IPProtocol(data[6])
+	ipv6.HopLimit = data[7]
+	ipv6.SrcIP = data[8:24]
+	ipv6.DstIP = data[24:40]
+	ipv6.HopByHop = nil
+	ipv6.BaseLayer = BaseLayer{data[:40], data[40:]}
+
+	// We treat a HopByHop IPv6 option as part of the IPv6 packet, since its
+	// options are crucial for understanding what's actually happening per packet.
+	if ipv6.NextHeader == IPProtocolIPv6HopByHop {
+		err := ipv6.hbh.DecodeFromBytes(ipv6.Payload, df)
+		if err != nil {
+			return err
+		}
+		ipv6.HopByHop = &ipv6.hbh
+		pEnd, jumbo, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
+		if err != nil {
+			return err
+		}
+		if jumbo && ipv6.Length == 0 {
+			pEnd := int(pEnd)
+			if pEnd > len(ipv6.Payload) {
+				df.SetTruncated()
+				pEnd = len(ipv6.Payload)
+			}
+			ipv6.Payload = ipv6.Payload[:pEnd]
+			return nil
+		} else if jumbo && ipv6.Length != 0 {
+			return errors.New("IPv6 has jumbo length and IPv6 length is not 0")
+		} else if !jumbo && ipv6.Length == 0 {
+			return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option")
+		} else {
+			ipv6.Payload = ipv6.Payload[ipv6.hbh.ActualLength:]
+		}
+	}
+
+	if ipv6.Length == 0 {
+		return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ipv6.NextHeader)
+	}
+
+	pEnd := int(ipv6.Length)
+	if pEnd > len(ipv6.Payload) {
+		df.SetTruncated()
+		pEnd = len(ipv6.Payload)
+	}
+	ipv6.Payload = ipv6.Payload[:pEnd]
+
+	return nil
+}
+
+// CanDecode implementation according to gopacket.DecodingLayer
+func (ipv6 *IPv6) CanDecode() gopacket.LayerClass {
+	return LayerTypeIPv6
+}
+
+// NextLayerType implementation according to gopacket.DecodingLayer
+func (ipv6 *IPv6) NextLayerType() gopacket.LayerType {
+	if ipv6.HopByHop != nil {
+		return ipv6.HopByHop.NextHeader.LayerType()
+	}
+	return ipv6.NextHeader.LayerType()
+}
+
+func decodeIPv6(data []byte, p gopacket.PacketBuilder) error {
+	ip6 := &IPv6{}
+	err := ip6.DecodeFromBytes(data, p)
+	p.AddLayer(ip6)
+	p.SetNetworkLayer(ip6)
+	if ip6.HopByHop != nil {
+		p.AddLayer(ip6.HopByHop)
+	}
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(ip6.NextLayerType())
+}
+
+type ipv6HeaderTLVOption struct {
+	OptionType, OptionLength uint8
+	ActualLength             int
+	OptionData               []byte
+	OptionAlignment          [2]uint8 // Xn+Y = [2]uint8{X, Y}
+}
+
+func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int {
+	if fixLengths {
+		h.OptionLength = uint8(len(h.OptionData))
+	}
+	length := int(h.OptionLength) + 2
+	if !dryrun {
+		data[0] = h.OptionType
+		data[1] = h.OptionLength
+		copy(data[2:], h.OptionData)
+	}
+	return length
+}
+
+func decodeIPv6HeaderTLVOption(data []byte, df gopacket.DecodeFeedback) (h *ipv6HeaderTLVOption, _ error) {
+	if len(data) < 2 {
+		df.SetTruncated()
+		return nil, errors.New("IPv6 header option too small")
+	}
+	h = &ipv6HeaderTLVOption{}
+	if data[0] == 0 {
+		h.ActualLength = 1
+		return
+	}
+	h.OptionType = data[0]
+	h.OptionLength = data[1]
+	h.ActualLength = int(h.OptionLength) + 2
+	if len(data) < h.ActualLength {
+		df.SetTruncated()
+		return nil, errors.New("IPv6 header TLV option too small")
+	}
+	h.OptionData = data[2:h.ActualLength]
+	return
+}
+
+func serializeTLVOptionPadding(data []byte, padLength int) {
+	if padLength <= 0 {
+		return
+	}
+	if padLength == 1 {
+		data[0] = 0x0
+		return
+	}
+	tlvLength := uint8(padLength) - 2
+	data[0] = 0x1
+	data[1] = tlvLength
+	if tlvLength != 0 {
+		for k := range data[2:] {
+			data[k+2] = 0x0
+		}
+	}
+	return
+}
+
+// If buf is 'nil' do a serialize dry run
+func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int {
+	var l int
+
+	dryrun := buf == nil
+	length := 2
+	for _, opt := range options {
+		if fixLengths {
+			x := int(opt.OptionAlignment[0])
+			y := int(opt.OptionAlignment[1])
+			if x != 0 {
+				n := length / x
+				offset := x*n + y
+				if offset < length {
+					offset += x
+				}
+				if length != offset {
+					pad := offset - length
+					if !dryrun {
+						serializeTLVOptionPadding(buf[length-2:], pad)
+					}
+					length += pad
+				}
+			}
+		}
+		if dryrun {
+			l = opt.serializeTo(nil, fixLengths, true)
+		} else {
+			l = opt.serializeTo(buf[length-2:], fixLengths, false)
+		}
+		length += l
+	}
+	if fixLengths {
+		pad := length % 8
+		if pad != 0 {
+			if !dryrun {
+				serializeTLVOptionPadding(buf[length-2:], pad)
+			}
+			length += pad
+		}
+	}
+	return length - 2
+}
+
+type ipv6ExtensionBase struct {
+	BaseLayer
+	NextHeader   IPProtocol
+	HeaderLength uint8
+	ActualLength int
+}
+
+func decodeIPv6ExtensionBase(data []byte, df gopacket.DecodeFeedback) (i ipv6ExtensionBase, returnedErr error) {
+	if len(data) < 2 {
+		df.SetTruncated()
+		return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than 2", len(data))
+	}
+	i.NextHeader = IPProtocol(data[0])
+	i.HeaderLength = data[1]
+	i.ActualLength = int(i.HeaderLength)*8 + 8
+	if len(data) < i.ActualLength {
+		return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than specified length %d", len(data), i.ActualLength)
+	}
+	i.Contents = data[:i.ActualLength]
+	i.Payload = data[i.ActualLength:]
+	return
+}
+
+// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6
+// extensions.  You can use it with a DecodingLayerParser to handle IPv6 stacks
+// which may or may not have extensions.
+type IPv6ExtensionSkipper struct {
+	NextHeader IPProtocol
+	BaseLayer
+}
+
+// DecodeFromBytes implementation according to gopacket.DecodingLayer
+func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	extension, err := decodeIPv6ExtensionBase(data, df)
+	if err != nil {
+		return err
+	}
+	i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
+	i.NextHeader = extension.NextHeader
+	return nil
+}
+
+// CanDecode implementation according to gopacket.DecodingLayer
+func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass {
+	return LayerClassIPv6Extension
+}
+
+// NextLayerType implementation according to gopacket.DecodingLayer
+func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType {
+	return i.NextHeader.LayerType()
+}
+
+// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension.
+type IPv6HopByHopOption ipv6HeaderTLVOption
+
+// IPv6HopByHop is the IPv6 hop-by-hop extension.
+type IPv6HopByHop struct {
+	ipv6ExtensionBase
+	Options []*IPv6HopByHopOption
+}
+
+// LayerType returns LayerTypeIPv6HopByHop.
+func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop }
+
+// SerializeTo implementation according to gopacket.SerializableLayer
+func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var bytes []byte
+	var err error
+
+	o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
+	for _, v := range i.Options {
+		o = append(o, (*ipv6HeaderTLVOption)(v))
+	}
+
+	l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
+	bytes, err = b.PrependBytes(l)
+	if err != nil {
+		return err
+	}
+	serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
+
+	length := len(bytes) + 2
+	if length%8 != 0 {
+		return errors.New("IPv6HopByHop actual length must be multiple of 8")
+	}
+	bytes, err = b.PrependBytes(2)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(i.NextHeader)
+	if opts.FixLengths {
+		i.HeaderLength = uint8((length / 8) - 1)
+	}
+	bytes[1] = uint8(i.HeaderLength)
+	return nil
+}
+
+// DecodeFromBytes implementation according to gopacket.DecodingLayer
+func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	var err error
+	i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
+	if err != nil {
+		return err
+	}
+	i.Options = i.Options[:0]
+	offset := 2
+	for offset < i.ActualLength {
+		opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
+		if err != nil {
+			return err
+		}
+		i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
+		offset += opt.ActualLength
+	}
+	return nil
+}
+
+func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error {
+	i := &IPv6HopByHop{}
+	err := i.DecodeFromBytes(data, p)
+	p.AddLayer(i)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(i.NextHeader)
+}
+
+// SetJumboLength adds the IPv6HopByHopOptionJumbogram with the given length
+func (o *IPv6HopByHopOption) SetJumboLength(len uint32) {
+	o.OptionType = IPv6HopByHopOptionJumbogram
+	o.OptionLength = 4
+	o.ActualLength = 6
+	if o.OptionData == nil {
+		o.OptionData = make([]byte, 4)
+	}
+	binary.BigEndian.PutUint32(o.OptionData, len)
+	o.OptionAlignment = [2]uint8{4, 2}
+}
+
+// IPv6Routing is the IPv6 routing extension.
+type IPv6Routing struct {
+	ipv6ExtensionBase
+	RoutingType  uint8
+	SegmentsLeft uint8
+	// This segment is supposed to be zero according to RFC2460, the second set of
+	// 4 bytes in the extension.
+	Reserved []byte
+	// SourceRoutingIPs is the set of IPv6 addresses requested for source routing,
+	// set only if RoutingType == 0.
+	SourceRoutingIPs []net.IP
+}
+
+// LayerType returns LayerTypeIPv6Routing.
+func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }
+
+func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
+	base, err := decodeIPv6ExtensionBase(data, p)
+	if err != nil {
+		return err
+	}
+	i := &IPv6Routing{
+		ipv6ExtensionBase: base,
+		RoutingType:       data[2],
+		SegmentsLeft:      data[3],
+		Reserved:          data[4:8],
+	}
+	switch i.RoutingType {
+	case 0: // Source routing
+		if (i.ActualLength-8)%16 != 0 {
+			return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)
+		}
+		for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
+			i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
+		}
+	default:
+		return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType)
+	}
+	p.AddLayer(i)
+	return p.NextDecoder(i.NextHeader)
+}
+
+// IPv6Fragment is the IPv6 fragment header, used for packet
+// fragmentation/defragmentation.
+type IPv6Fragment struct {
+	BaseLayer
+	NextHeader IPProtocol
+	// Reserved1 is bits [8-16), from least to most significant, 0-indexed
+	Reserved1      uint8
+	FragmentOffset uint16
+	// Reserved2 is bits [29-31), from least to most significant, 0-indexed
+	Reserved2      uint8
+	MoreFragments  bool
+	Identification uint32
+}
+
+// LayerType returns LayerTypeIPv6Fragment.
+func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment }
+
+func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error {
+	if len(data) < 8 {
+		p.SetTruncated()
+		return fmt.Errorf("Invalid ip6-fragment header. Length %d less than 8", len(data))
+	}
+	i := &IPv6Fragment{
+		BaseLayer:      BaseLayer{data[:8], data[8:]},
+		NextHeader:     IPProtocol(data[0]),
+		Reserved1:      data[1],
+		FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3,
+		Reserved2:      data[3] & 0x6 >> 1,
+		MoreFragments:  data[3]&0x1 != 0,
+		Identification: binary.BigEndian.Uint32(data[4:8]),
+	}
+	p.AddLayer(i)
+	return p.NextDecoder(gopacket.DecodeFragment)
+}
+
+// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension.
+type IPv6DestinationOption ipv6HeaderTLVOption
+
+// IPv6Destination is the IPv6 destination options header.
+type IPv6Destination struct {
+	ipv6ExtensionBase
+	Options []*IPv6DestinationOption
+}
+
+// LayerType returns LayerTypeIPv6Destination.
+func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination }
+
+// DecodeFromBytes implementation according to gopacket.DecodingLayer
+func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	var err error
+	i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
+	if err != nil {
+		return err
+	}
+	offset := 2
+	for offset < i.ActualLength {
+		opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
+		if err != nil {
+			return err
+		}
+		i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
+		offset += opt.ActualLength
+	}
+	return nil
+}
+
+func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error {
+	i := &IPv6Destination{}
+	err := i.DecodeFromBytes(data, p)
+	p.AddLayer(i)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(i.NextHeader)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var bytes []byte
+	var err error
+
+	o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
+	for _, v := range i.Options {
+		o = append(o, (*ipv6HeaderTLVOption)(v))
+	}
+
+	l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
+	bytes, err = b.PrependBytes(l)
+	if err != nil {
+		return err
+	}
+	serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
+
+	length := len(bytes) + 2
+	if length%8 != 0 {
+		return errors.New("IPv6Destination actual length must be multiple of 8")
+	}
+	bytes, err = b.PrependBytes(2)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(i.NextHeader)
+	if opts.FixLengths {
+		i.HeaderLength = uint8((length / 8) - 1)
+	}
+	bytes[1] = uint8(i.HeaderLength)
+	return nil
+}
+
+func checkIPv6Address(addr net.IP) error {
+	if len(addr) == net.IPv6len {
+		return nil
+	}
+	if len(addr) == net.IPv4len {
+		return errors.New("address is IPv4")
+	}
+	return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len)
+}
+
+// AddressTo16 ensures IPv6.SrcIP and IPv6.DstIP are actually IPv6 addresses (i.e. 16 byte addresses)
+func (ipv6 *IPv6) AddressTo16() error {
+	if err := checkIPv6Address(ipv6.SrcIP); err != nil {
+		return fmt.Errorf("Invalid source IPv6 address (%s)", err)
+	}
+	if err := checkIPv6Address(ipv6.DstIP); err != nil {
+		return fmt.Errorf("Invalid destination IPv6 address (%s)", err)
+	}
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ipsec.go b/go-controller/vendor/github.com/google/gopacket/layers/ipsec.go
new file mode 100644
index 00000000000..12f31caf673
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ipsec.go
@@ -0,0 +1,77 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"github.com/google/gopacket"
+)
+
+// IPSecAH is the authentication header for IPv4/6 defined in
+// http://tools.ietf.org/html/rfc2402
+type IPSecAH struct {
+	// While the auth header can be used for both IPv4 and v6, its format is that of
+	// an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase
+	// to build it.
+	ipv6ExtensionBase
+	Reserved           uint16
+	SPI, Seq           uint32
+	AuthenticationData []byte
+}
+
+// LayerType returns LayerTypeIPSecAH.
+func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
+
+func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
+	if len(data) < 12 {
+		p.SetTruncated()
+		return errors.New("IPSec AH packet less than 12 bytes")
+	}
+	i := &IPSecAH{
+		ipv6ExtensionBase: ipv6ExtensionBase{
+			NextHeader:   IPProtocol(data[0]),
+			HeaderLength: data[1],
+		},
+		Reserved: binary.BigEndian.Uint16(data[2:4]),
+		SPI:      binary.BigEndian.Uint32(data[4:8]),
+		Seq:      binary.BigEndian.Uint32(data[8:12]),
+	}
+	i.ActualLength = (int(i.HeaderLength) + 2) * 4
+	if len(data) < i.ActualLength {
+		p.SetTruncated()
+		return errors.New("Truncated AH packet < ActualLength")
+	}
+	i.AuthenticationData = data[12:i.ActualLength]
+	i.Contents = data[:i.ActualLength]
+	i.Payload = data[i.ActualLength:]
+	p.AddLayer(i)
+	return p.NextDecoder(i.NextHeader)
+}
+
+// IPSecESP is the encapsulating security payload defined in
+// http://tools.ietf.org/html/rfc2406
+type IPSecESP struct {
+	BaseLayer
+	SPI, Seq uint32
+	// Encrypted contains the encrypted set of bytes sent in an ESP
+	Encrypted []byte
+}
+
+// LayerType returns LayerTypeIPSecESP.
+func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP }
+
+func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error {
+	i := &IPSecESP{
+		BaseLayer: BaseLayer{data, nil},
+		SPI:       binary.BigEndian.Uint32(data[:4]),
+		Seq:       binary.BigEndian.Uint32(data[4:8]),
+		Encrypted: data[8:],
+	}
+	p.AddLayer(i)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/layertypes.go b/go-controller/vendor/github.com/google/gopacket/layers/layertypes.go
new file mode 100644
index 00000000000..69d25ae8023
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/layertypes.go
@@ -0,0 +1,223 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"github.com/google/gopacket"
+)
+
+var (
+	LayerTypeARP                          = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)})
+	LayerTypeCiscoDiscovery               = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)})
+	LayerTypeEthernetCTP                  = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)})
+	LayerTypeEthernetCTPForwardData       = gopacket.RegisterLayerType(13, gopacket.LayerTypeMetadata{Name: "EthernetCTPForwardData", Decoder: nil})
+	LayerTypeEthernetCTPReply             = gopacket.RegisterLayerType(14, gopacket.LayerTypeMetadata{Name: "EthernetCTPReply", Decoder: nil})
+	LayerTypeDot1Q                        = gopacket.RegisterLayerType(15, gopacket.LayerTypeMetadata{Name: "Dot1Q", Decoder: gopacket.DecodeFunc(decodeDot1Q)})
+	LayerTypeEtherIP                      = gopacket.RegisterLayerType(16, gopacket.LayerTypeMetadata{Name: "EtherIP", Decoder: gopacket.DecodeFunc(decodeEtherIP)})
+	LayerTypeEthernet                     = gopacket.RegisterLayerType(17, gopacket.LayerTypeMetadata{Name: "Ethernet", Decoder: gopacket.DecodeFunc(decodeEthernet)})
+	LayerTypeGRE                          = gopacket.RegisterLayerType(18, gopacket.LayerTypeMetadata{Name: "GRE", Decoder: gopacket.DecodeFunc(decodeGRE)})
+	LayerTypeICMPv4                       = gopacket.RegisterLayerType(19, gopacket.LayerTypeMetadata{Name: "ICMPv4", Decoder: gopacket.DecodeFunc(decodeICMPv4)})
+	LayerTypeIPv4                         = gopacket.RegisterLayerType(20, gopacket.LayerTypeMetadata{Name: "IPv4", Decoder: gopacket.DecodeFunc(decodeIPv4)})
+	LayerTypeIPv6                         = gopacket.RegisterLayerType(21, gopacket.LayerTypeMetadata{Name: "IPv6", Decoder: gopacket.DecodeFunc(decodeIPv6)})
+	LayerTypeLLC                          = gopacket.RegisterLayerType(22, gopacket.LayerTypeMetadata{Name: "LLC", Decoder: gopacket.DecodeFunc(decodeLLC)})
+	LayerTypeSNAP                         = gopacket.RegisterLayerType(23, gopacket.LayerTypeMetadata{Name: "SNAP", Decoder: gopacket.DecodeFunc(decodeSNAP)})
+	LayerTypeMPLS                         = gopacket.RegisterLayerType(24, gopacket.LayerTypeMetadata{Name: "MPLS", Decoder: gopacket.DecodeFunc(decodeMPLS)})
+	LayerTypePPP                          = gopacket.RegisterLayerType(25, gopacket.LayerTypeMetadata{Name: "PPP", Decoder: gopacket.DecodeFunc(decodePPP)})
+	LayerTypePPPoE                        = gopacket.RegisterLayerType(26, gopacket.LayerTypeMetadata{Name: "PPPoE", Decoder: gopacket.DecodeFunc(decodePPPoE)})
+	LayerTypeRUDP                         = gopacket.RegisterLayerType(27, gopacket.LayerTypeMetadata{Name: "RUDP", Decoder: gopacket.DecodeFunc(decodeRUDP)})
+	LayerTypeSCTP                         = gopacket.RegisterLayerType(28, gopacket.LayerTypeMetadata{Name: "SCTP", Decoder: gopacket.DecodeFunc(decodeSCTP)})
+	LayerTypeSCTPUnknownChunkType         = gopacket.RegisterLayerType(29, gopacket.LayerTypeMetadata{Name: "SCTPUnknownChunkType", Decoder: nil})
+	LayerTypeSCTPData                     = gopacket.RegisterLayerType(30, gopacket.LayerTypeMetadata{Name: "SCTPData", Decoder: nil})
+	LayerTypeSCTPInit                     = gopacket.RegisterLayerType(31, gopacket.LayerTypeMetadata{Name: "SCTPInit", Decoder: nil})
+	LayerTypeSCTPSack                     = gopacket.RegisterLayerType(32, gopacket.LayerTypeMetadata{Name: "SCTPSack", Decoder: nil})
+	LayerTypeSCTPHeartbeat                = gopacket.RegisterLayerType(33, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeat", Decoder: nil})
+	LayerTypeSCTPError                    = gopacket.RegisterLayerType(34, gopacket.LayerTypeMetadata{Name: "SCTPError", Decoder: nil})
+	LayerTypeSCTPShutdown                 = gopacket.RegisterLayerType(35, gopacket.LayerTypeMetadata{Name: "SCTPShutdown", Decoder: nil})
+	LayerTypeSCTPShutdownAck              = gopacket.RegisterLayerType(36, gopacket.LayerTypeMetadata{Name: "SCTPShutdownAck", Decoder: nil})
+	LayerTypeSCTPCookieEcho               = gopacket.RegisterLayerType(37, gopacket.LayerTypeMetadata{Name: "SCTPCookieEcho", Decoder: nil})
+	LayerTypeSCTPEmptyLayer               = gopacket.RegisterLayerType(38, gopacket.LayerTypeMetadata{Name: "SCTPEmptyLayer", Decoder: nil})
+	LayerTypeSCTPInitAck                  = gopacket.RegisterLayerType(39, gopacket.LayerTypeMetadata{Name: "SCTPInitAck", Decoder: nil})
+	LayerTypeSCTPHeartbeatAck             = gopacket.RegisterLayerType(40, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeatAck", Decoder: nil})
+	LayerTypeSCTPAbort                    = gopacket.RegisterLayerType(41, gopacket.LayerTypeMetadata{Name: "SCTPAbort", Decoder: nil})
+	LayerTypeSCTPShutdownComplete         = gopacket.RegisterLayerType(42, gopacket.LayerTypeMetadata{Name: "SCTPShutdownComplete", Decoder: nil})
+	LayerTypeSCTPCookieAck                = gopacket.RegisterLayerType(43, gopacket.LayerTypeMetadata{Name: "SCTPCookieAck", Decoder: nil})
+	LayerTypeTCP                          = gopacket.RegisterLayerType(44, gopacket.LayerTypeMetadata{Name: "TCP", Decoder: gopacket.DecodeFunc(decodeTCP)})
+	LayerTypeUDP                          = gopacket.RegisterLayerType(45, gopacket.LayerTypeMetadata{Name: "UDP", Decoder: gopacket.DecodeFunc(decodeUDP)})
+	LayerTypeIPv6HopByHop                 = gopacket.RegisterLayerType(46, gopacket.LayerTypeMetadata{Name: "IPv6HopByHop", Decoder: gopacket.DecodeFunc(decodeIPv6HopByHop)})
+	LayerTypeIPv6Routing                  = gopacket.RegisterLayerType(47, gopacket.LayerTypeMetadata{Name: "IPv6Routing", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)})
+	LayerTypeIPv6Fragment                 = gopacket.RegisterLayerType(48, gopacket.LayerTypeMetadata{Name: "IPv6Fragment", Decoder: gopacket.DecodeFunc(decodeIPv6Fragment)})
+	LayerTypeIPv6Destination              = gopacket.RegisterLayerType(49, gopacket.LayerTypeMetadata{Name: "IPv6Destination", Decoder: gopacket.DecodeFunc(decodeIPv6Destination)})
+	LayerTypeIPSecAH                      = gopacket.RegisterLayerType(50, gopacket.LayerTypeMetadata{Name: "IPSecAH", Decoder: gopacket.DecodeFunc(decodeIPSecAH)})
+	LayerTypeIPSecESP                     = gopacket.RegisterLayerType(51, gopacket.LayerTypeMetadata{Name: "IPSecESP", Decoder: gopacket.DecodeFunc(decodeIPSecESP)})
+	LayerTypeUDPLite                      = gopacket.RegisterLayerType(52, gopacket.LayerTypeMetadata{Name: "UDPLite", Decoder: gopacket.DecodeFunc(decodeUDPLite)})
+	LayerTypeFDDI                         = gopacket.RegisterLayerType(53, gopacket.LayerTypeMetadata{Name: "FDDI", Decoder: gopacket.DecodeFunc(decodeFDDI)})
+	LayerTypeLoopback                     = gopacket.RegisterLayerType(54, gopacket.LayerTypeMetadata{Name: "Loopback", Decoder: gopacket.DecodeFunc(decodeLoopback)})
+	LayerTypeEAP                          = gopacket.RegisterLayerType(55, gopacket.LayerTypeMetadata{Name: "EAP", Decoder: gopacket.DecodeFunc(decodeEAP)})
+	LayerTypeEAPOL                        = gopacket.RegisterLayerType(56, gopacket.LayerTypeMetadata{Name: "EAPOL", Decoder: gopacket.DecodeFunc(decodeEAPOL)})
+	LayerTypeICMPv6                       = gopacket.RegisterLayerType(57, gopacket.LayerTypeMetadata{Name: "ICMPv6", Decoder: gopacket.DecodeFunc(decodeICMPv6)})
+	LayerTypeLinkLayerDiscovery           = gopacket.RegisterLayerType(58, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscovery", Decoder: gopacket.DecodeFunc(decodeLinkLayerDiscovery)})
+	LayerTypeCiscoDiscoveryInfo           = gopacket.RegisterLayerType(59, gopacket.LayerTypeMetadata{Name: "CiscoDiscoveryInfo", Decoder: gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)})
+	LayerTypeLinkLayerDiscoveryInfo       = gopacket.RegisterLayerType(60, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscoveryInfo", Decoder: nil})
+	LayerTypeNortelDiscovery              = gopacket.RegisterLayerType(61, gopacket.LayerTypeMetadata{Name: "NortelDiscovery", Decoder: gopacket.DecodeFunc(decodeNortelDiscovery)})
+	LayerTypeIGMP                         = gopacket.RegisterLayerType(62, gopacket.LayerTypeMetadata{Name: "IGMP", Decoder: gopacket.DecodeFunc(decodeIGMP)})
+	LayerTypePFLog                        = gopacket.RegisterLayerType(63, gopacket.LayerTypeMetadata{Name: "PFLog", Decoder: gopacket.DecodeFunc(decodePFLog)})
+	LayerTypeRadioTap                     = gopacket.RegisterLayerType(64, gopacket.LayerTypeMetadata{Name: "RadioTap", Decoder: gopacket.DecodeFunc(decodeRadioTap)})
+	LayerTypeDot11                        = gopacket.RegisterLayerType(65, gopacket.LayerTypeMetadata{Name: "Dot11", Decoder: gopacket.DecodeFunc(decodeDot11)})
+	LayerTypeDot11Ctrl                    = gopacket.RegisterLayerType(66, gopacket.LayerTypeMetadata{Name: "Dot11Ctrl", Decoder: gopacket.DecodeFunc(decodeDot11Ctrl)})
+	LayerTypeDot11Data                    = gopacket.RegisterLayerType(67, gopacket.LayerTypeMetadata{Name: "Dot11Data", Decoder: gopacket.DecodeFunc(decodeDot11Data)})
+	LayerTypeDot11DataCFAck               = gopacket.RegisterLayerType(68, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
+	LayerTypeDot11DataCFPoll              = gopacket.RegisterLayerType(69, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
+	LayerTypeDot11DataCFAckPoll           = gopacket.RegisterLayerType(70, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
+	LayerTypeDot11DataNull                = gopacket.RegisterLayerType(71, gopacket.LayerTypeMetadata{Name: "Dot11DataNull", Decoder: gopacket.DecodeFunc(decodeDot11DataNull)})
+	LayerTypeDot11DataCFAckNoData         = gopacket.RegisterLayerType(72, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
+	LayerTypeDot11DataCFPollNoData        = gopacket.RegisterLayerType(73, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
+	LayerTypeDot11DataCFAckPollNoData     = gopacket.RegisterLayerType(74, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
+	LayerTypeDot11DataQOSData             = gopacket.RegisterLayerType(75, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSData", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSData)})
+	LayerTypeDot11DataQOSDataCFAck        = gopacket.RegisterLayerType(76, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck)})
+	LayerTypeDot11DataQOSDataCFPoll       = gopacket.RegisterLayerType(77, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll)})
+	LayerTypeDot11DataQOSDataCFAckPoll    = gopacket.RegisterLayerType(78, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll)})
+	LayerTypeDot11DataQOSNull             = gopacket.RegisterLayerType(79, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSNull", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSNull)})
+	LayerTypeDot11DataQOSCFPollNoData     = gopacket.RegisterLayerType(80, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData)})
+	LayerTypeDot11DataQOSCFAckPollNoData  = gopacket.RegisterLayerType(81, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData)})
+	LayerTypeDot11InformationElement      = gopacket.RegisterLayerType(82, gopacket.LayerTypeMetadata{Name: "Dot11InformationElement", Decoder: gopacket.DecodeFunc(decodeDot11InformationElement)})
+	LayerTypeDot11CtrlCTS                 = gopacket.RegisterLayerType(83, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCTS)})
+	LayerTypeDot11CtrlRTS                 = gopacket.RegisterLayerType(84, gopacket.LayerTypeMetadata{Name: "Dot11CtrlRTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlRTS)})
+	LayerTypeDot11CtrlBlockAckReq         = gopacket.RegisterLayerType(85, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAckReq", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq)})
+	LayerTypeDot11CtrlBlockAck            = gopacket.RegisterLayerType(86, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAck)})
+	LayerTypeDot11CtrlPowersavePoll       = gopacket.RegisterLayerType(87, gopacket.LayerTypeMetadata{Name: "Dot11CtrlPowersavePoll", Decoder: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll)})
+	LayerTypeDot11CtrlAck                 = gopacket.RegisterLayerType(88, gopacket.LayerTypeMetadata{Name: "Dot11CtrlAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlAck)})
+	LayerTypeDot11CtrlCFEnd               = gopacket.RegisterLayerType(89, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEnd", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEnd)})
+	LayerTypeDot11CtrlCFEndAck            = gopacket.RegisterLayerType(90, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEndAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck)})
+	LayerTypeDot11MgmtAssociationReq      = gopacket.RegisterLayerType(91, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq)})
+	LayerTypeDot11MgmtAssociationResp     = gopacket.RegisterLayerType(92, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp)})
+	LayerTypeDot11MgmtReassociationReq    = gopacket.RegisterLayerType(93, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq)})
+	LayerTypeDot11MgmtReassociationResp   = gopacket.RegisterLayerType(94, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp)})
+	LayerTypeDot11MgmtProbeReq            = gopacket.RegisterLayerType(95, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeReq)})
+	LayerTypeDot11MgmtProbeResp           = gopacket.RegisterLayerType(96, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeResp)})
+	LayerTypeDot11MgmtMeasurementPilot    = gopacket.RegisterLayerType(97, gopacket.LayerTypeMetadata{Name: "Dot11MgmtMeasurementPilot", Decoder: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot)})
+	LayerTypeDot11MgmtBeacon              = gopacket.RegisterLayerType(98, gopacket.LayerTypeMetadata{Name: "Dot11MgmtBeacon", Decoder: gopacket.DecodeFunc(decodeDot11MgmtBeacon)})
+	LayerTypeDot11MgmtATIM                = gopacket.RegisterLayerType(99, gopacket.LayerTypeMetadata{Name: "Dot11MgmtATIM", Decoder: gopacket.DecodeFunc(decodeDot11MgmtATIM)})
+	LayerTypeDot11MgmtDisassociation      = gopacket.RegisterLayerType(100, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDisassociation", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDisassociation)})
+	LayerTypeDot11MgmtAuthentication      = gopacket.RegisterLayerType(101, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAuthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAuthentication)})
+	LayerTypeDot11MgmtDeauthentication    = gopacket.RegisterLayerType(102, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDeauthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication)})
+	LayerTypeDot11MgmtAction              = gopacket.RegisterLayerType(103, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAction", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAction)})
+	LayerTypeDot11MgmtActionNoAck         = gopacket.RegisterLayerType(104, gopacket.LayerTypeMetadata{Name: "Dot11MgmtActionNoAck", Decoder: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck)})
+	LayerTypeDot11MgmtArubaWLAN           = gopacket.RegisterLayerType(105, gopacket.LayerTypeMetadata{Name: "Dot11MgmtArubaWLAN", Decoder: gopacket.DecodeFunc(decodeDot11MgmtArubaWLAN)})
+	LayerTypeDot11WEP                     = gopacket.RegisterLayerType(106, gopacket.LayerTypeMetadata{Name: "Dot11WEP", Decoder: gopacket.DecodeFunc(decodeDot11WEP)})
+	LayerTypeDNS                          = gopacket.RegisterLayerType(107, gopacket.LayerTypeMetadata{Name: "DNS", Decoder: gopacket.DecodeFunc(decodeDNS)})
+	LayerTypeUSB                          = gopacket.RegisterLayerType(108, gopacket.LayerTypeMetadata{Name: "USB", Decoder: gopacket.DecodeFunc(decodeUSB)})
+	LayerTypeUSBRequestBlockSetup         = gopacket.RegisterLayerType(109, gopacket.LayerTypeMetadata{Name: "USBRequestBlockSetup", Decoder: gopacket.DecodeFunc(decodeUSBRequestBlockSetup)})
+	LayerTypeUSBControl                   = gopacket.RegisterLayerType(110, gopacket.LayerTypeMetadata{Name: "USBControl", Decoder: gopacket.DecodeFunc(decodeUSBControl)})
+	LayerTypeUSBInterrupt                 = gopacket.RegisterLayerType(111, gopacket.LayerTypeMetadata{Name: "USBInterrupt", Decoder: gopacket.DecodeFunc(decodeUSBInterrupt)})
+	LayerTypeUSBBulk                      = gopacket.RegisterLayerType(112, gopacket.LayerTypeMetadata{Name: "USBBulk", Decoder: gopacket.DecodeFunc(decodeUSBBulk)})
+	LayerTypeLinuxSLL                     = gopacket.RegisterLayerType(113, gopacket.LayerTypeMetadata{Name: "Linux SLL", Decoder: gopacket.DecodeFunc(decodeLinuxSLL)})
+	LayerTypeSFlow                        = gopacket.RegisterLayerType(114, gopacket.LayerTypeMetadata{Name: "SFlow", Decoder: gopacket.DecodeFunc(decodeSFlow)})
+	LayerTypePrismHeader                  = gopacket.RegisterLayerType(115, gopacket.LayerTypeMetadata{Name: "Prism monitor mode header", Decoder: gopacket.DecodeFunc(decodePrismHeader)})
+	LayerTypeVXLAN                        = gopacket.RegisterLayerType(116, gopacket.LayerTypeMetadata{Name: "VXLAN", Decoder: gopacket.DecodeFunc(decodeVXLAN)})
+	LayerTypeNTP                          = gopacket.RegisterLayerType(117, gopacket.LayerTypeMetadata{Name: "NTP", Decoder: gopacket.DecodeFunc(decodeNTP)})
+	LayerTypeDHCPv4                       = gopacket.RegisterLayerType(118, gopacket.LayerTypeMetadata{Name: "DHCPv4", Decoder: gopacket.DecodeFunc(decodeDHCPv4)})
+	LayerTypeVRRP                         = gopacket.RegisterLayerType(119, gopacket.LayerTypeMetadata{Name: "VRRP", Decoder: gopacket.DecodeFunc(decodeVRRP)})
+	LayerTypeGeneve                       = gopacket.RegisterLayerType(120, gopacket.LayerTypeMetadata{Name: "Geneve", Decoder: gopacket.DecodeFunc(decodeGeneve)})
+	LayerTypeSTP                          = gopacket.RegisterLayerType(121, gopacket.LayerTypeMetadata{Name: "STP", Decoder: gopacket.DecodeFunc(decodeSTP)})
+	LayerTypeBFD                          = gopacket.RegisterLayerType(122, gopacket.LayerTypeMetadata{Name: "BFD", Decoder: gopacket.DecodeFunc(decodeBFD)})
+	LayerTypeOSPF                         = gopacket.RegisterLayerType(123, gopacket.LayerTypeMetadata{Name: "OSPF", Decoder: gopacket.DecodeFunc(decodeOSPF)})
+	LayerTypeICMPv6RouterSolicitation     = gopacket.RegisterLayerType(124, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterSolicitation)})
+	LayerTypeICMPv6RouterAdvertisement    = gopacket.RegisterLayerType(125, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterAdvertisement)})
+	LayerTypeICMPv6NeighborSolicitation   = gopacket.RegisterLayerType(126, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborSolicitation)})
+	LayerTypeICMPv6NeighborAdvertisement  = gopacket.RegisterLayerType(127, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborAdvertisement)})
+	LayerTypeICMPv6Redirect               = gopacket.RegisterLayerType(128, gopacket.LayerTypeMetadata{Name: "ICMPv6Redirect", Decoder: gopacket.DecodeFunc(decodeICMPv6Redirect)})
+	LayerTypeGTPv1U                       = gopacket.RegisterLayerType(129, gopacket.LayerTypeMetadata{Name: "GTPv1U", Decoder: gopacket.DecodeFunc(decodeGTPv1u)})
+	LayerTypeEAPOLKey                     = gopacket.RegisterLayerType(130, gopacket.LayerTypeMetadata{Name: "EAPOLKey", Decoder: gopacket.DecodeFunc(decodeEAPOLKey)})
+	LayerTypeLCM                          = gopacket.RegisterLayerType(131, gopacket.LayerTypeMetadata{Name: "LCM", Decoder: gopacket.DecodeFunc(decodeLCM)})
+	LayerTypeICMPv6Echo                   = gopacket.RegisterLayerType(132, gopacket.LayerTypeMetadata{Name: "ICMPv6Echo", Decoder: gopacket.DecodeFunc(decodeICMPv6Echo)})
+	LayerTypeSIP                          = gopacket.RegisterLayerType(133, gopacket.LayerTypeMetadata{Name: "SIP", Decoder: gopacket.DecodeFunc(decodeSIP)})
+	LayerTypeDHCPv6                       = gopacket.RegisterLayerType(134, gopacket.LayerTypeMetadata{Name: "DHCPv6", Decoder: gopacket.DecodeFunc(decodeDHCPv6)})
+	LayerTypeMLDv1MulticastListenerReport = gopacket.RegisterLayerType(135, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerReport)})
+	LayerTypeMLDv1MulticastListenerDone   = gopacket.RegisterLayerType(136, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerDone", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerDone)})
+	LayerTypeMLDv1MulticastListenerQuery  = gopacket.RegisterLayerType(137, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerQuery)})
+	LayerTypeMLDv2MulticastListenerReport = gopacket.RegisterLayerType(138, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerReport)})
+	LayerTypeMLDv2MulticastListenerQuery  = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)})
+	LayerTypeTLS                          = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)})
+	LayerTypeModbusTCP                    = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)})
+	LayerTypeRMCP                         = gopacket.RegisterLayerType(142, gopacket.LayerTypeMetadata{Name: "RMCP", Decoder: gopacket.DecodeFunc(decodeRMCP)})
+	LayerTypeASF                          = gopacket.RegisterLayerType(143, gopacket.LayerTypeMetadata{Name: "ASF", Decoder: gopacket.DecodeFunc(decodeASF)})
+	LayerTypeASFPresencePong              = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)})
+	LayerTypeERSPANII                     = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
+	LayerTypeRADIUS                       = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
+)
+
+var (
+	// LayerClassIPNetwork contains TCP/IP network layer types.
+	LayerClassIPNetwork = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeIPv4,
+		LayerTypeIPv6,
+	})
+	// LayerClassIPTransport contains TCP/IP transport layer types.
+	LayerClassIPTransport = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeTCP,
+		LayerTypeUDP,
+		LayerTypeSCTP,
+	})
+	// LayerClassIPControl contains TCP/IP control protocols.
+	LayerClassIPControl = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeICMPv4,
+		LayerTypeICMPv6,
+	})
+	// LayerClassSCTPChunk contains SCTP chunk types (not the top-level SCTP
+	// layer).
+	LayerClassSCTPChunk = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeSCTPUnknownChunkType,
+		LayerTypeSCTPData,
+		LayerTypeSCTPInit,
+		LayerTypeSCTPSack,
+		LayerTypeSCTPHeartbeat,
+		LayerTypeSCTPError,
+		LayerTypeSCTPShutdown,
+		LayerTypeSCTPShutdownAck,
+		LayerTypeSCTPCookieEcho,
+		LayerTypeSCTPEmptyLayer,
+		LayerTypeSCTPInitAck,
+		LayerTypeSCTPHeartbeatAck,
+		LayerTypeSCTPAbort,
+		LayerTypeSCTPShutdownComplete,
+		LayerTypeSCTPCookieAck,
+	})
+	// LayerClassIPv6Extension contains IPv6 extension headers.
+	LayerClassIPv6Extension = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeIPv6HopByHop,
+		LayerTypeIPv6Routing,
+		LayerTypeIPv6Fragment,
+		LayerTypeIPv6Destination,
+	})
+	LayerClassIPSec = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeIPSecAH,
+		LayerTypeIPSecESP,
+	})
+	// LayerClassICMPv6NDP contains ICMPv6 neighbor discovery protocol
+	// messages.
+	LayerClassICMPv6NDP = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeICMPv6RouterSolicitation,
+		LayerTypeICMPv6RouterAdvertisement,
+		LayerTypeICMPv6NeighborSolicitation,
+		LayerTypeICMPv6NeighborAdvertisement,
+		LayerTypeICMPv6Redirect,
+	})
+	// LayerClassMLDv1 contains multicast listener discovery protocol
+	LayerClassMLDv1 = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeMLDv1MulticastListenerQuery,
+		LayerTypeMLDv1MulticastListenerReport,
+		LayerTypeMLDv1MulticastListenerDone,
+	})
+	// LayerClassMLDv2 contains multicast listener discovery protocol v2
+	LayerClassMLDv2 = gopacket.NewLayerClass([]gopacket.LayerType{
+		LayerTypeMLDv1MulticastListenerReport,
+		LayerTypeMLDv1MulticastListenerDone,
+		LayerTypeMLDv2MulticastListenerReport,
+		LayerTypeMLDv1MulticastListenerQuery,
+		LayerTypeMLDv2MulticastListenerQuery,
+	})
+)
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/lcm.go b/go-controller/vendor/github.com/google/gopacket/layers/lcm.go
new file mode 100644
index 00000000000..58a4b828908
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/lcm.go
@@ -0,0 +1,218 @@
+// 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"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// LCMShortHeaderMagic is the LCM small message header magic number
+	LCMShortHeaderMagic uint32 = 0x4c433032
+	// LCMFragmentedHeaderMagic is the LCM fragmented message header magic number
+	LCMFragmentedHeaderMagic uint32 = 0x4c433033
+)
+
+// LCM (Lightweight Communications and Marshalling) is a set of libraries and
+// tools for message passing and data marshalling, targeted at real-time systems
+// where high-bandwidth and low latency are critical. It provides a
+// publish/subscribe message passing model and automatic
+// marshalling/unmarshalling code generation with bindings for applications in a
+// variety of programming languages.
+//
+// References
+//   https://lcm-proj.github.io/
+//   https://github.com/lcm-proj/lcm
+type LCM struct {
+	// Common (short & fragmented header) fields
+	Magic          uint32
+	SequenceNumber uint32
+	// Fragmented header only fields
+	PayloadSize    uint32
+	FragmentOffset uint32
+	FragmentNumber uint16
+	TotalFragments uint16
+	// Common field
+	ChannelName string
+	// Gopacket helper fields
+	Fragmented  bool
+	fingerprint LCMFingerprint
+	contents    []byte
+	payload     []byte
+}
+
+// LCMFingerprint is the type of a LCM fingerprint.
+type LCMFingerprint uint64
+
+var (
+	// lcmLayerTypes contains a map of all LCM fingerprints that we support and
+	// their LayerType
+	lcmLayerTypes  = map[LCMFingerprint]gopacket.LayerType{}
+	layerTypeIndex = 1001
+)
+
+// RegisterLCMLayerType allows users to register decoders for the underlying
+// LCM payload. This is done based on the fingerprint that every LCM message
+// contains and which identifies it uniquely. If num is not the zero value it
+// will be used when registering with RegisterLayerType towards gopacket,
+// otherwise an incremental value starting from 1001 will be used.
+func RegisterLCMLayerType(num int, name string, fingerprint LCMFingerprint,
+	decoder gopacket.Decoder) gopacket.LayerType {
+	metadata := gopacket.LayerTypeMetadata{Name: name, Decoder: decoder}
+
+	if num == 0 {
+		num = layerTypeIndex
+		layerTypeIndex++
+	}
+
+	lcmLayerTypes[fingerprint] = gopacket.RegisterLayerType(num, metadata)
+
+	return lcmLayerTypes[fingerprint]
+}
+
+// SupportedLCMFingerprints returns a slice of all LCM fingerprints that has
+// been registered so far.
+func SupportedLCMFingerprints() []LCMFingerprint {
+	fingerprints := make([]LCMFingerprint, 0, len(lcmLayerTypes))
+	for fp := range lcmLayerTypes {
+		fingerprints = append(fingerprints, fp)
+	}
+	return fingerprints
+}
+
+// GetLCMLayerType returns the underlying LCM message's LayerType.
+// This LayerType has to be registered by using RegisterLCMLayerType.
+func GetLCMLayerType(fingerprint LCMFingerprint) gopacket.LayerType {
+	layerType, ok := lcmLayerTypes[fingerprint]
+	if !ok {
+		return gopacket.LayerTypePayload
+	}
+
+	return layerType
+}
+
+func decodeLCM(data []byte, p gopacket.PacketBuilder) error {
+	lcm := &LCM{}
+
+	err := lcm.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+
+	p.AddLayer(lcm)
+	p.SetApplicationLayer(lcm)
+
+	return p.NextDecoder(lcm.NextLayerType())
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return errors.New("LCM < 8 bytes")
+	}
+	offset := 0
+
+	lcm.Magic = binary.BigEndian.Uint32(data[offset:4])
+	offset += 4
+
+	if lcm.Magic != LCMShortHeaderMagic && lcm.Magic != LCMFragmentedHeaderMagic {
+		return fmt.Errorf("Received LCM header magic %v does not match know "+
+			"LCM magic numbers. Dropping packet.", lcm.Magic)
+	}
+
+	lcm.SequenceNumber = binary.BigEndian.Uint32(data[offset:8])
+	offset += 4
+
+	if lcm.Magic == LCMFragmentedHeaderMagic {
+		lcm.Fragmented = true
+
+		lcm.PayloadSize = binary.BigEndian.Uint32(data[offset : offset+4])
+		offset += 4
+
+		lcm.FragmentOffset = binary.BigEndian.Uint32(data[offset : offset+4])
+		offset += 4
+
+		lcm.FragmentNumber = binary.BigEndian.Uint16(data[offset : offset+2])
+		offset += 2
+
+		lcm.TotalFragments = binary.BigEndian.Uint16(data[offset : offset+2])
+		offset += 2
+	} else {
+		lcm.Fragmented = false
+	}
+
+	if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
+		buffer := make([]byte, 0)
+		for _, b := range data[offset:] {
+			offset++
+
+			if b == 0 {
+				break
+			}
+
+			buffer = append(buffer, b)
+		}
+
+		lcm.ChannelName = string(buffer)
+	}
+
+	lcm.fingerprint = LCMFingerprint(
+		binary.BigEndian.Uint64(data[offset : offset+8]))
+
+	lcm.contents = data[:offset]
+	lcm.payload = data[offset:]
+
+	return nil
+}
+
+// CanDecode returns a set of layers that LCM objects can decode.
+// As LCM objects can only decode the LCM layer, we just return that layer.
+func (lcm LCM) CanDecode() gopacket.LayerClass {
+	return LayerTypeLCM
+}
+
+// NextLayerType specifies the LCM payload layer type following this header.
+// As LCM packets are serialized structs with uniq fingerprints for each uniq
+// combination of data types, lookup of correct layer type is based on that
+// fingerprint.
+func (lcm LCM) NextLayerType() gopacket.LayerType {
+	if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
+		return GetLCMLayerType(lcm.fingerprint)
+	}
+
+	return gopacket.LayerTypeFragment
+}
+
+// LayerType returns LayerTypeLCM
+func (lcm LCM) LayerType() gopacket.LayerType {
+	return LayerTypeLCM
+}
+
+// LayerContents returns the contents of the LCM header.
+func (lcm LCM) LayerContents() []byte {
+	return lcm.contents
+}
+
+// LayerPayload returns the payload following this LCM header.
+func (lcm LCM) LayerPayload() []byte {
+	return lcm.payload
+}
+
+// Payload returns the payload following this LCM header.
+func (lcm LCM) Payload() []byte {
+	return lcm.LayerPayload()
+}
+
+// Fingerprint returns the LCM fingerprint of the underlying message.
+func (lcm LCM) Fingerprint() LCMFingerprint {
+	return lcm.fingerprint
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/linux_sll.go b/go-controller/vendor/github.com/google/gopacket/layers/linux_sll.go
new file mode 100644
index 00000000000..85a4f8bdd0a
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/linux_sll.go
@@ -0,0 +1,98 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+type LinuxSLLPacketType uint16
+
+const (
+	LinuxSLLPacketTypeHost      LinuxSLLPacketType = 0 // To us
+	LinuxSLLPacketTypeBroadcast LinuxSLLPacketType = 1 // To all
+	LinuxSLLPacketTypeMulticast LinuxSLLPacketType = 2 // To group
+	LinuxSLLPacketTypeOtherhost LinuxSLLPacketType = 3 // To someone else
+	LinuxSLLPacketTypeOutgoing  LinuxSLLPacketType = 4 // Outgoing of any type
+	// These ones are invisible by user level
+	LinuxSLLPacketTypeLoopback  LinuxSLLPacketType = 5 // MC/BRD frame looped back
+	LinuxSLLPacketTypeFastroute LinuxSLLPacketType = 6 // Fastrouted frame
+)
+
+func (l LinuxSLLPacketType) String() string {
+	switch l {
+	case LinuxSLLPacketTypeHost:
+		return "host"
+	case LinuxSLLPacketTypeBroadcast:
+		return "broadcast"
+	case LinuxSLLPacketTypeMulticast:
+		return "multicast"
+	case LinuxSLLPacketTypeOtherhost:
+		return "otherhost"
+	case LinuxSLLPacketTypeOutgoing:
+		return "outgoing"
+	case LinuxSLLPacketTypeLoopback:
+		return "loopback"
+	case LinuxSLLPacketTypeFastroute:
+		return "fastroute"
+	}
+	return fmt.Sprintf("Unknown(%d)", int(l))
+}
+
+type LinuxSLL struct {
+	BaseLayer
+	PacketType   LinuxSLLPacketType
+	AddrLen      uint16
+	Addr         net.HardwareAddr
+	EthernetType EthernetType
+	AddrType     uint16
+}
+
+// LayerType returns LayerTypeLinuxSLL.
+func (sll *LinuxSLL) LayerType() gopacket.LayerType { return LayerTypeLinuxSLL }
+
+func (sll *LinuxSLL) CanDecode() gopacket.LayerClass {
+	return LayerTypeLinuxSLL
+}
+
+func (sll *LinuxSLL) LinkFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointMAC, sll.Addr, nil)
+}
+
+func (sll *LinuxSLL) NextLayerType() gopacket.LayerType {
+	return sll.EthernetType.LayerType()
+}
+
+func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 16 {
+		return errors.New("Linux SLL packet too small")
+	}
+	sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2]))
+	sll.AddrType = binary.BigEndian.Uint16(data[2:4])
+	sll.AddrLen = binary.BigEndian.Uint16(data[4:6])
+
+	sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6])
+	sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16]))
+	sll.BaseLayer = BaseLayer{data[:16], data[16:]}
+
+	return nil
+}
+
+func decodeLinuxSLL(data []byte, p gopacket.PacketBuilder) error {
+	sll := &LinuxSLL{}
+	if err := sll.DecodeFromBytes(data, p); err != nil {
+		return err
+	}
+	p.AddLayer(sll)
+	p.SetLinkLayer(sll)
+	return p.NextDecoder(sll.EthernetType)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/llc.go b/go-controller/vendor/github.com/google/gopacket/layers/llc.go
new file mode 100644
index 00000000000..cad6803671e
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/llc.go
@@ -0,0 +1,193 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+// LLC is the layer used for 802.2 Logical Link Control headers.
+// See http://standards.ieee.org/getieee802/download/802.2-1998.pdf
+type LLC struct {
+	BaseLayer
+	DSAP    uint8
+	IG      bool // true means group, false means individual
+	SSAP    uint8
+	CR      bool // true means response, false means command
+	Control uint16
+}
+
+// LayerType returns gopacket.LayerTypeLLC.
+func (l *LLC) LayerType() gopacket.LayerType { return LayerTypeLLC }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (l *LLC) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 3 {
+		return errors.New("LLC header too small")
+	}
+	l.DSAP = data[0] & 0xFE
+	l.IG = data[0]&0x1 != 0
+	l.SSAP = data[1] & 0xFE
+	l.CR = data[1]&0x1 != 0
+	l.Control = uint16(data[2])
+
+	if l.Control&0x1 == 0 || l.Control&0x3 == 0x1 {
+		if len(data) < 4 {
+			return errors.New("LLC header too small")
+		}
+		l.Control = l.Control<<8 | uint16(data[3])
+		l.Contents = data[:4]
+		l.Payload = data[4:]
+	} else {
+		l.Contents = data[:3]
+		l.Payload = data[3:]
+	}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (l *LLC) CanDecode() gopacket.LayerClass {
+	return LayerTypeLLC
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (l *LLC) NextLayerType() gopacket.LayerType {
+	switch {
+	case l.DSAP == 0xAA && l.SSAP == 0xAA:
+		return LayerTypeSNAP
+	case l.DSAP == 0x42 && l.SSAP == 0x42:
+		return LayerTypeSTP
+	}
+	return gopacket.LayerTypeZero // Not implemented
+}
+
+// SNAP is used inside LLC.  See
+// http://standards.ieee.org/getieee802/download/802-2001.pdf.
+// From http://en.wikipedia.org/wiki/Subnetwork_Access_Protocol:
+//  "[T]he Subnetwork Access Protocol (SNAP) is a mechanism for multiplexing,
+//  on networks using IEEE 802.2 LLC, more protocols than can be distinguished
+//  by the 8-bit 802.2 Service Access Point (SAP) fields."
+type SNAP struct {
+	BaseLayer
+	OrganizationalCode []byte
+	Type               EthernetType
+}
+
+// LayerType returns gopacket.LayerTypeSNAP.
+func (s *SNAP) LayerType() gopacket.LayerType { return LayerTypeSNAP }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (s *SNAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 5 {
+		return errors.New("SNAP header too small")
+	}
+	s.OrganizationalCode = data[:3]
+	s.Type = EthernetType(binary.BigEndian.Uint16(data[3:5]))
+	s.BaseLayer = BaseLayer{data[:5], data[5:]}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (s *SNAP) CanDecode() gopacket.LayerClass {
+	return LayerTypeSNAP
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (s *SNAP) NextLayerType() gopacket.LayerType {
+	// See BUG(gconnel) in decodeSNAP
+	return s.Type.LayerType()
+}
+
+func decodeLLC(data []byte, p gopacket.PacketBuilder) error {
+	l := &LLC{}
+	err := l.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(l)
+	return p.NextDecoder(l.NextLayerType())
+}
+
+func decodeSNAP(data []byte, p gopacket.PacketBuilder) error {
+	s := &SNAP{}
+	err := s.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(s)
+	// BUG(gconnell):  When decoding SNAP, we treat the SNAP type as an Ethernet
+	// type.  This may not actually be an ethernet type in all cases,
+	// depending on the organizational code.  Right now, we don't check.
+	return p.NextDecoder(s.Type)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (l *LLC) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var igFlag, crFlag byte
+	var length int
+
+	if l.Control&0xFF00 != 0 {
+		length = 4
+	} else {
+		length = 3
+	}
+
+	if l.DSAP&0x1 != 0 {
+		return errors.New("DSAP value invalid, should not include IG flag bit")
+	}
+
+	if l.SSAP&0x1 != 0 {
+		return errors.New("SSAP value invalid, should not include CR flag bit")
+	}
+
+	if buf, err := b.PrependBytes(length); err != nil {
+		return err
+	} else {
+		igFlag = 0
+		if l.IG {
+			igFlag = 0x1
+		}
+
+		crFlag = 0
+		if l.CR {
+			crFlag = 0x1
+		}
+
+		buf[0] = l.DSAP + igFlag
+		buf[1] = l.SSAP + crFlag
+
+		if length == 4 {
+			buf[2] = uint8(l.Control >> 8)
+			buf[3] = uint8(l.Control)
+		} else {
+			buf[2] = uint8(l.Control)
+		}
+	}
+
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (s *SNAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if buf, err := b.PrependBytes(5); err != nil {
+		return err
+	} else {
+		buf[0] = s.OrganizationalCode[0]
+		buf[1] = s.OrganizationalCode[1]
+		buf[2] = s.OrganizationalCode[2]
+		binary.BigEndian.PutUint16(buf[3:5], uint16(s.Type))
+	}
+
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/lldp.go b/go-controller/vendor/github.com/google/gopacket/layers/lldp.go
new file mode 100644
index 00000000000..16a5bbadad9
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/lldp.go
@@ -0,0 +1,1603 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// LLDPTLVType is the type of each TLV value in a LinkLayerDiscovery packet.
+type LLDPTLVType byte
+
+const (
+	LLDPTLVEnd             LLDPTLVType = 0
+	LLDPTLVChassisID       LLDPTLVType = 1
+	LLDPTLVPortID          LLDPTLVType = 2
+	LLDPTLVTTL             LLDPTLVType = 3
+	LLDPTLVPortDescription LLDPTLVType = 4
+	LLDPTLVSysName         LLDPTLVType = 5
+	LLDPTLVSysDescription  LLDPTLVType = 6
+	LLDPTLVSysCapabilities LLDPTLVType = 7
+	LLDPTLVMgmtAddress     LLDPTLVType = 8
+	LLDPTLVOrgSpecific     LLDPTLVType = 127
+)
+
+// LinkLayerDiscoveryValue is a TLV value inside a LinkLayerDiscovery packet layer.
+type LinkLayerDiscoveryValue struct {
+	Type   LLDPTLVType
+	Length uint16
+	Value  []byte
+}
+
+func (c *LinkLayerDiscoveryValue) len() int {
+	return 0
+}
+
+// LLDPChassisIDSubType specifies the value type for a single LLDPChassisID.ID
+type LLDPChassisIDSubType byte
+
+// LLDP Chassis Types
+const (
+	LLDPChassisIDSubTypeReserved    LLDPChassisIDSubType = 0
+	LLDPChassisIDSubTypeChassisComp LLDPChassisIDSubType = 1
+	LLDPChassisIDSubtypeIfaceAlias  LLDPChassisIDSubType = 2
+	LLDPChassisIDSubTypePortComp    LLDPChassisIDSubType = 3
+	LLDPChassisIDSubTypeMACAddr     LLDPChassisIDSubType = 4
+	LLDPChassisIDSubTypeNetworkAddr LLDPChassisIDSubType = 5
+	LLDPChassisIDSubtypeIfaceName   LLDPChassisIDSubType = 6
+	LLDPChassisIDSubTypeLocal       LLDPChassisIDSubType = 7
+)
+
+type LLDPChassisID struct {
+	Subtype LLDPChassisIDSubType
+	ID      []byte
+}
+
+func (c *LLDPChassisID) serialize() []byte {
+
+	var buf = make([]byte, c.serializedLen())
+	idLen := uint16(LLDPTLVChassisID)<<9 | uint16(len(c.ID)+1) //id should take 7 bits, length should take 9 bits, +1 for subtype
+	binary.BigEndian.PutUint16(buf[0:2], idLen)
+	buf[2] = byte(c.Subtype)
+	copy(buf[3:], c.ID)
+	return buf
+}
+
+func (c *LLDPChassisID) serializedLen() int {
+	return len(c.ID) + 3 // +2 for id and length, +1 for subtype
+}
+
+// LLDPPortIDSubType specifies the value type for a single LLDPPortID.ID
+type LLDPPortIDSubType byte
+
+// LLDP PortID types
+const (
+	LLDPPortIDSubtypeReserved       LLDPPortIDSubType = 0
+	LLDPPortIDSubtypeIfaceAlias     LLDPPortIDSubType = 1
+	LLDPPortIDSubtypePortComp       LLDPPortIDSubType = 2
+	LLDPPortIDSubtypeMACAddr        LLDPPortIDSubType = 3
+	LLDPPortIDSubtypeNetworkAddr    LLDPPortIDSubType = 4
+	LLDPPortIDSubtypeIfaceName      LLDPPortIDSubType = 5
+	LLDPPortIDSubtypeAgentCircuitID LLDPPortIDSubType = 6
+	LLDPPortIDSubtypeLocal          LLDPPortIDSubType = 7
+)
+
+type LLDPPortID struct {
+	Subtype LLDPPortIDSubType
+	ID      []byte
+}
+
+func (c *LLDPPortID) serialize() []byte {
+
+	var buf = make([]byte, c.serializedLen())
+	idLen := uint16(LLDPTLVPortID)<<9 | uint16(len(c.ID)+1) //id should take 7 bits, length should take 9 bits, +1 for subtype
+	binary.BigEndian.PutUint16(buf[0:2], idLen)
+	buf[2] = byte(c.Subtype)
+	copy(buf[3:], c.ID)
+	return buf
+}
+
+func (c *LLDPPortID) serializedLen() int {
+	return len(c.ID) + 3 // +2 for id and length, +1 for subtype
+}
+
+// LinkLayerDiscovery is a packet layer containing the LinkLayer Discovery Protocol.
+// See http:http://standards.ieee.org/getieee802/download/802.1AB-2009.pdf
+// ChassisID, PortID and TTL are mandatory TLV's. Other values can be decoded
+// with DecodeValues()
+type LinkLayerDiscovery struct {
+	BaseLayer
+	ChassisID LLDPChassisID
+	PortID    LLDPPortID
+	TTL       uint16
+	Values    []LinkLayerDiscoveryValue
+}
+
+type IEEEOUI uint32
+
+// http://standards.ieee.org/develop/regauth/oui/oui.txt
+const (
+	IEEEOUI8021     IEEEOUI = 0x0080c2
+	IEEEOUI8023     IEEEOUI = 0x00120f
+	IEEEOUI80211    IEEEOUI = 0x000fac
+	IEEEOUI8021Qbg  IEEEOUI = 0x0013BF
+	IEEEOUICisco2   IEEEOUI = 0x000142
+	IEEEOUIMedia    IEEEOUI = 0x0012bb // TR-41
+	IEEEOUIProfinet IEEEOUI = 0x000ecf
+	IEEEOUIDCBX     IEEEOUI = 0x001b21
+)
+
+// LLDPOrgSpecificTLV is an Organisation-specific TLV
+type LLDPOrgSpecificTLV struct {
+	OUI     IEEEOUI
+	SubType uint8
+	Info    []byte
+}
+
+// LLDPCapabilities Types
+const (
+	LLDPCapsOther       uint16 = 1 << 0
+	LLDPCapsRepeater    uint16 = 1 << 1
+	LLDPCapsBridge      uint16 = 1 << 2
+	LLDPCapsWLANAP      uint16 = 1 << 3
+	LLDPCapsRouter      uint16 = 1 << 4
+	LLDPCapsPhone       uint16 = 1 << 5
+	LLDPCapsDocSis      uint16 = 1 << 6
+	LLDPCapsStationOnly uint16 = 1 << 7
+	LLDPCapsCVLAN       uint16 = 1 << 8
+	LLDPCapsSVLAN       uint16 = 1 << 9
+	LLDPCapsTmpr        uint16 = 1 << 10
+)
+
+// LLDPCapabilities represents the capabilities of a device
+type LLDPCapabilities struct {
+	Other       bool
+	Repeater    bool
+	Bridge      bool
+	WLANAP      bool
+	Router      bool
+	Phone       bool
+	DocSis      bool
+	StationOnly bool
+	CVLAN       bool
+	SVLAN       bool
+	TMPR        bool
+}
+
+type LLDPSysCapabilities struct {
+	SystemCap  LLDPCapabilities
+	EnabledCap LLDPCapabilities
+}
+
+type IANAAddressFamily byte
+
+// LLDP Management Address Subtypes
+// http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml
+const (
+	IANAAddressFamilyReserved IANAAddressFamily = 0
+	IANAAddressFamilyIPV4     IANAAddressFamily = 1
+	IANAAddressFamilyIPV6     IANAAddressFamily = 2
+	IANAAddressFamilyNSAP     IANAAddressFamily = 3
+	IANAAddressFamilyHDLC     IANAAddressFamily = 4
+	IANAAddressFamilyBBN1822  IANAAddressFamily = 5
+	IANAAddressFamily802      IANAAddressFamily = 6
+	IANAAddressFamilyE163     IANAAddressFamily = 7
+	IANAAddressFamilyE164     IANAAddressFamily = 8
+	IANAAddressFamilyF69      IANAAddressFamily = 9
+	IANAAddressFamilyX121     IANAAddressFamily = 10
+	IANAAddressFamilyIPX      IANAAddressFamily = 11
+	IANAAddressFamilyAtalk    IANAAddressFamily = 12
+	IANAAddressFamilyDecnet   IANAAddressFamily = 13
+	IANAAddressFamilyBanyan   IANAAddressFamily = 14
+	IANAAddressFamilyE164NSAP IANAAddressFamily = 15
+	IANAAddressFamilyDNS      IANAAddressFamily = 16
+	IANAAddressFamilyDistname IANAAddressFamily = 17
+	IANAAddressFamilyASNumber IANAAddressFamily = 18
+	IANAAddressFamilyXTPIPV4  IANAAddressFamily = 19
+	IANAAddressFamilyXTPIPV6  IANAAddressFamily = 20
+	IANAAddressFamilyXTP      IANAAddressFamily = 21
+	IANAAddressFamilyFcWWPN   IANAAddressFamily = 22
+	IANAAddressFamilyFcWWNN   IANAAddressFamily = 23
+	IANAAddressFamilyGWID     IANAAddressFamily = 24
+	IANAAddressFamilyL2VPN    IANAAddressFamily = 25
+)
+
+type LLDPInterfaceSubtype byte
+
+// LLDP Interface Subtypes
+const (
+	LLDPInterfaceSubtypeUnknown LLDPInterfaceSubtype = 1
+	LLDPInterfaceSubtypeifIndex LLDPInterfaceSubtype = 2
+	LLDPInterfaceSubtypeSysPort LLDPInterfaceSubtype = 3
+)
+
+type LLDPMgmtAddress struct {
+	Subtype          IANAAddressFamily
+	Address          []byte
+	InterfaceSubtype LLDPInterfaceSubtype
+	InterfaceNumber  uint32
+	OID              string
+}
+
+// LinkLayerDiscoveryInfo represents the decoded details for a set of LinkLayerDiscoveryValues
+// Organisation-specific TLV's can be decoded using the various Decode() methods
+type LinkLayerDiscoveryInfo struct {
+	BaseLayer
+	PortDescription string
+	SysName         string
+	SysDescription  string
+	SysCapabilities LLDPSysCapabilities
+	MgmtAddress     LLDPMgmtAddress
+	OrgTLVs         []LLDPOrgSpecificTLV      // Private TLVs
+	Unknown         []LinkLayerDiscoveryValue // undecoded TLVs
+}
+
+/// IEEE 802.1 TLV Subtypes
+const (
+	LLDP8021SubtypePortVLANID       uint8 = 1
+	LLDP8021SubtypeProtocolVLANID   uint8 = 2
+	LLDP8021SubtypeVLANName         uint8 = 3
+	LLDP8021SubtypeProtocolIdentity uint8 = 4
+	LLDP8021SubtypeVDIUsageDigest   uint8 = 5
+	LLDP8021SubtypeManagementVID    uint8 = 6
+	LLDP8021SubtypeLinkAggregation  uint8 = 7
+)
+
+// VLAN Port Protocol ID options
+const (
+	LLDPProtocolVLANIDCapability byte = 1 << 1
+	LLDPProtocolVLANIDStatus     byte = 1 << 2
+)
+
+type PortProtocolVLANID struct {
+	Supported bool
+	Enabled   bool
+	ID        uint16
+}
+
+type VLANName struct {
+	ID   uint16
+	Name string
+}
+
+type ProtocolIdentity []byte
+
+// LACP options
+const (
+	LLDPAggregationCapability byte = 1 << 0
+	LLDPAggregationStatus     byte = 1 << 1
+)
+
+// IEEE 802 Link Aggregation parameters
+type LLDPLinkAggregation struct {
+	Supported bool
+	Enabled   bool
+	PortID    uint32
+}
+
+// LLDPInfo8021 represents the information carried in 802.1 Org-specific TLVs
+type LLDPInfo8021 struct {
+	PVID               uint16
+	PPVIDs             []PortProtocolVLANID
+	VLANNames          []VLANName
+	ProtocolIdentities []ProtocolIdentity
+	VIDUsageDigest     uint32
+	ManagementVID      uint16
+	LinkAggregation    LLDPLinkAggregation
+}
+
+// IEEE 802.3 TLV Subtypes
+const (
+	LLDP8023SubtypeMACPHY          uint8 = 1
+	LLDP8023SubtypeMDIPower        uint8 = 2
+	LLDP8023SubtypeLinkAggregation uint8 = 3
+	LLDP8023SubtypeMTU             uint8 = 4
+)
+
+// MACPHY options
+const (
+	LLDPMACPHYCapability byte = 1 << 0
+	LLDPMACPHYStatus     byte = 1 << 1
+)
+
+// From IANA-MAU-MIB (introduced by RFC 4836) - dot3MauType
+const (
+	LLDPMAUTypeUnknown         uint16 = 0
+	LLDPMAUTypeAUI             uint16 = 1
+	LLDPMAUType10Base5         uint16 = 2
+	LLDPMAUTypeFOIRL           uint16 = 3
+	LLDPMAUType10Base2         uint16 = 4
+	LLDPMAUType10BaseT         uint16 = 5
+	LLDPMAUType10BaseFP        uint16 = 6
+	LLDPMAUType10BaseFB        uint16 = 7
+	LLDPMAUType10BaseFL        uint16 = 8
+	LLDPMAUType10BROAD36       uint16 = 9
+	LLDPMAUType10BaseT_HD      uint16 = 10
+	LLDPMAUType10BaseT_FD      uint16 = 11
+	LLDPMAUType10BaseFL_HD     uint16 = 12
+	LLDPMAUType10BaseFL_FD     uint16 = 13
+	LLDPMAUType100BaseT4       uint16 = 14
+	LLDPMAUType100BaseTX_HD    uint16 = 15
+	LLDPMAUType100BaseTX_FD    uint16 = 16
+	LLDPMAUType100BaseFX_HD    uint16 = 17
+	LLDPMAUType100BaseFX_FD    uint16 = 18
+	LLDPMAUType100BaseT2_HD    uint16 = 19
+	LLDPMAUType100BaseT2_FD    uint16 = 20
+	LLDPMAUType1000BaseX_HD    uint16 = 21
+	LLDPMAUType1000BaseX_FD    uint16 = 22
+	LLDPMAUType1000BaseLX_HD   uint16 = 23
+	LLDPMAUType1000BaseLX_FD   uint16 = 24
+	LLDPMAUType1000BaseSX_HD   uint16 = 25
+	LLDPMAUType1000BaseSX_FD   uint16 = 26
+	LLDPMAUType1000BaseCX_HD   uint16 = 27
+	LLDPMAUType1000BaseCX_FD   uint16 = 28
+	LLDPMAUType1000BaseT_HD    uint16 = 29
+	LLDPMAUType1000BaseT_FD    uint16 = 30
+	LLDPMAUType10GBaseX        uint16 = 31
+	LLDPMAUType10GBaseLX4      uint16 = 32
+	LLDPMAUType10GBaseR        uint16 = 33
+	LLDPMAUType10GBaseER       uint16 = 34
+	LLDPMAUType10GBaseLR       uint16 = 35
+	LLDPMAUType10GBaseSR       uint16 = 36
+	LLDPMAUType10GBaseW        uint16 = 37
+	LLDPMAUType10GBaseEW       uint16 = 38
+	LLDPMAUType10GBaseLW       uint16 = 39
+	LLDPMAUType10GBaseSW       uint16 = 40
+	LLDPMAUType10GBaseCX4      uint16 = 41
+	LLDPMAUType2BaseTL         uint16 = 42
+	LLDPMAUType10PASS_TS       uint16 = 43
+	LLDPMAUType100BaseBX10D    uint16 = 44
+	LLDPMAUType100BaseBX10U    uint16 = 45
+	LLDPMAUType100BaseLX10     uint16 = 46
+	LLDPMAUType1000BaseBX10D   uint16 = 47
+	LLDPMAUType1000BaseBX10U   uint16 = 48
+	LLDPMAUType1000BaseLX10    uint16 = 49
+	LLDPMAUType1000BasePX10D   uint16 = 50
+	LLDPMAUType1000BasePX10U   uint16 = 51
+	LLDPMAUType1000BasePX20D   uint16 = 52
+	LLDPMAUType1000BasePX20U   uint16 = 53
+	LLDPMAUType10GBaseT        uint16 = 54
+	LLDPMAUType10GBaseLRM      uint16 = 55
+	LLDPMAUType1000BaseKX      uint16 = 56
+	LLDPMAUType10GBaseKX4      uint16 = 57
+	LLDPMAUType10GBaseKR       uint16 = 58
+	LLDPMAUType10_1GBasePRX_D1 uint16 = 59
+	LLDPMAUType10_1GBasePRX_D2 uint16 = 60
+	LLDPMAUType10_1GBasePRX_D3 uint16 = 61
+	LLDPMAUType10_1GBasePRX_U1 uint16 = 62
+	LLDPMAUType10_1GBasePRX_U2 uint16 = 63
+	LLDPMAUType10_1GBasePRX_U3 uint16 = 64
+	LLDPMAUType10GBasePR_D1    uint16 = 65
+	LLDPMAUType10GBasePR_D2    uint16 = 66
+	LLDPMAUType10GBasePR_D3    uint16 = 67
+	LLDPMAUType10GBasePR_U1    uint16 = 68
+	LLDPMAUType10GBasePR_U3    uint16 = 69
+)
+
+// From RFC 3636 - ifMauAutoNegCapAdvertisedBits
+const (
+	LLDPMAUPMDOther        uint16 = 1 << 15
+	LLDPMAUPMD10BaseT      uint16 = 1 << 14
+	LLDPMAUPMD10BaseT_FD   uint16 = 1 << 13
+	LLDPMAUPMD100BaseT4    uint16 = 1 << 12
+	LLDPMAUPMD100BaseTX    uint16 = 1 << 11
+	LLDPMAUPMD100BaseTX_FD uint16 = 1 << 10
+	LLDPMAUPMD100BaseT2    uint16 = 1 << 9
+	LLDPMAUPMD100BaseT2_FD uint16 = 1 << 8
+	LLDPMAUPMDFDXPAUSE     uint16 = 1 << 7
+	LLDPMAUPMDFDXAPAUSE    uint16 = 1 << 6
+	LLDPMAUPMDFDXSPAUSE    uint16 = 1 << 5
+	LLDPMAUPMDFDXBPAUSE    uint16 = 1 << 4
+	LLDPMAUPMD1000BaseX    uint16 = 1 << 3
+	LLDPMAUPMD1000BaseX_FD uint16 = 1 << 2
+	LLDPMAUPMD1000BaseT    uint16 = 1 << 1
+	LLDPMAUPMD1000BaseT_FD uint16 = 1 << 0
+)
+
+// Inverted ifMauAutoNegCapAdvertisedBits if required
+// (Some manufacturers misinterpreted the spec -
+// see https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1455)
+const (
+	LLDPMAUPMDOtherInv        uint16 = 1 << 0
+	LLDPMAUPMD10BaseTInv      uint16 = 1 << 1
+	LLDPMAUPMD10BaseT_FDInv   uint16 = 1 << 2
+	LLDPMAUPMD100BaseT4Inv    uint16 = 1 << 3
+	LLDPMAUPMD100BaseTXInv    uint16 = 1 << 4
+	LLDPMAUPMD100BaseTX_FDInv uint16 = 1 << 5
+	LLDPMAUPMD100BaseT2Inv    uint16 = 1 << 6
+	LLDPMAUPMD100BaseT2_FDInv uint16 = 1 << 7
+	LLDPMAUPMDFDXPAUSEInv     uint16 = 1 << 8
+	LLDPMAUPMDFDXAPAUSEInv    uint16 = 1 << 9
+	LLDPMAUPMDFDXSPAUSEInv    uint16 = 1 << 10
+	LLDPMAUPMDFDXBPAUSEInv    uint16 = 1 << 11
+	LLDPMAUPMD1000BaseXInv    uint16 = 1 << 12
+	LLDPMAUPMD1000BaseX_FDInv uint16 = 1 << 13
+	LLDPMAUPMD1000BaseTInv    uint16 = 1 << 14
+	LLDPMAUPMD1000BaseT_FDInv uint16 = 1 << 15
+)
+
+type LLDPMACPHYConfigStatus struct {
+	AutoNegSupported  bool
+	AutoNegEnabled    bool
+	AutoNegCapability uint16
+	MAUType           uint16
+}
+
+// MDI Power options
+const (
+	LLDPMDIPowerPortClass    byte = 1 << 0
+	LLDPMDIPowerCapability   byte = 1 << 1
+	LLDPMDIPowerStatus       byte = 1 << 2
+	LLDPMDIPowerPairsAbility byte = 1 << 3
+)
+
+type LLDPPowerType byte
+
+type LLDPPowerSource byte
+
+type LLDPPowerPriority byte
+
+const (
+	LLDPPowerPriorityUnknown LLDPPowerPriority = 0
+	LLDPPowerPriorityMedium  LLDPPowerPriority = 1
+	LLDPPowerPriorityHigh    LLDPPowerPriority = 2
+	LLDPPowerPriorityLow     LLDPPowerPriority = 3
+)
+
+type LLDPPowerViaMDI8023 struct {
+	PortClassPSE    bool // false = PD
+	PSESupported    bool
+	PSEEnabled      bool
+	PSEPairsAbility bool
+	PSEPowerPair    uint8
+	PSEClass        uint8
+	Type            LLDPPowerType
+	Source          LLDPPowerSource
+	Priority        LLDPPowerPriority
+	Requested       uint16 // 1-510 Watts
+	Allocated       uint16 // 1-510 Watts
+}
+
+// LLDPInfo8023 represents the information carried in 802.3 Org-specific TLVs
+type LLDPInfo8023 struct {
+	MACPHYConfigStatus LLDPMACPHYConfigStatus
+	PowerViaMDI        LLDPPowerViaMDI8023
+	LinkAggregation    LLDPLinkAggregation
+	MTU                uint16
+}
+
+// IEEE 802.1Qbg TLV Subtypes
+const (
+	LLDP8021QbgEVB   uint8 = 0
+	LLDP8021QbgCDCP  uint8 = 1
+	LLDP8021QbgVDP   uint8 = 2
+	LLDP8021QbgEVB22 uint8 = 13
+)
+
+// LLDPEVBCapabilities Types
+const (
+	LLDPEVBCapsSTD uint16 = 1 << 7
+	LLDPEVBCapsRR  uint16 = 1 << 6
+	LLDPEVBCapsRTE uint16 = 1 << 2
+	LLDPEVBCapsECP uint16 = 1 << 1
+	LLDPEVBCapsVDP uint16 = 1 << 0
+)
+
+// LLDPEVBCapabilities represents the EVB capabilities of a device
+type LLDPEVBCapabilities struct {
+	StandardBridging            bool
+	ReflectiveRelay             bool
+	RetransmissionTimerExponent bool
+	EdgeControlProtocol         bool
+	VSIDiscoveryProtocol        bool
+}
+
+type LLDPEVBSettings struct {
+	Supported      LLDPEVBCapabilities
+	Enabled        LLDPEVBCapabilities
+	SupportedVSIs  uint16
+	ConfiguredVSIs uint16
+	RTEExponent    uint8
+}
+
+// LLDPInfo8021Qbg represents the information carried in 802.1Qbg Org-specific TLVs
+type LLDPInfo8021Qbg struct {
+	EVBSettings LLDPEVBSettings
+}
+
+type LLDPMediaSubtype uint8
+
+// Media TLV Subtypes
+const (
+	LLDPMediaTypeCapabilities LLDPMediaSubtype = 1
+	LLDPMediaTypeNetwork      LLDPMediaSubtype = 2
+	LLDPMediaTypeLocation     LLDPMediaSubtype = 3
+	LLDPMediaTypePower        LLDPMediaSubtype = 4
+	LLDPMediaTypeHardware     LLDPMediaSubtype = 5
+	LLDPMediaTypeFirmware     LLDPMediaSubtype = 6
+	LLDPMediaTypeSoftware     LLDPMediaSubtype = 7
+	LLDPMediaTypeSerial       LLDPMediaSubtype = 8
+	LLDPMediaTypeManufacturer LLDPMediaSubtype = 9
+	LLDPMediaTypeModel        LLDPMediaSubtype = 10
+	LLDPMediaTypeAssetID      LLDPMediaSubtype = 11
+)
+
+type LLDPMediaClass uint8
+
+// Media Class Values
+const (
+	LLDPMediaClassUndefined   LLDPMediaClass = 0
+	LLDPMediaClassEndpointI   LLDPMediaClass = 1
+	LLDPMediaClassEndpointII  LLDPMediaClass = 2
+	LLDPMediaClassEndpointIII LLDPMediaClass = 3
+	LLDPMediaClassNetwork     LLDPMediaClass = 4
+)
+
+// LLDPMediaCapabilities Types
+const (
+	LLDPMediaCapsLLDP      uint16 = 1 << 0
+	LLDPMediaCapsNetwork   uint16 = 1 << 1
+	LLDPMediaCapsLocation  uint16 = 1 << 2
+	LLDPMediaCapsPowerPSE  uint16 = 1 << 3
+	LLDPMediaCapsPowerPD   uint16 = 1 << 4
+	LLDPMediaCapsInventory uint16 = 1 << 5
+)
+
+// LLDPMediaCapabilities represents the LLDP Media capabilities of a device
+type LLDPMediaCapabilities struct {
+	Capabilities  bool
+	NetworkPolicy bool
+	Location      bool
+	PowerPSE      bool
+	PowerPD       bool
+	Inventory     bool
+	Class         LLDPMediaClass
+}
+
+type LLDPApplicationType uint8
+
+const (
+	LLDPAppTypeReserved            LLDPApplicationType = 0
+	LLDPAppTypeVoice               LLDPApplicationType = 1
+	LLDPappTypeVoiceSignaling      LLDPApplicationType = 2
+	LLDPappTypeGuestVoice          LLDPApplicationType = 3
+	LLDPappTypeGuestVoiceSignaling LLDPApplicationType = 4
+	LLDPappTypeSoftphoneVoice      LLDPApplicationType = 5
+	LLDPappTypeVideoConferencing   LLDPApplicationType = 6
+	LLDPappTypeStreamingVideo      LLDPApplicationType = 7
+	LLDPappTypeVideoSignaling      LLDPApplicationType = 8
+)
+
+type LLDPNetworkPolicy struct {
+	ApplicationType LLDPApplicationType
+	Defined         bool
+	Tagged          bool
+	VLANId          uint16
+	L2Priority      uint16
+	DSCPValue       uint8
+}
+
+type LLDPLocationFormat uint8
+
+const (
+	LLDPLocationFormatInvalid    LLDPLocationFormat = 0
+	LLDPLocationFormatCoordinate LLDPLocationFormat = 1
+	LLDPLocationFormatAddress    LLDPLocationFormat = 2
+	LLDPLocationFormatECS        LLDPLocationFormat = 3
+)
+
+type LLDPLocationAddressWhat uint8
+
+const (
+	LLDPLocationAddressWhatDHCP    LLDPLocationAddressWhat = 0
+	LLDPLocationAddressWhatNetwork LLDPLocationAddressWhat = 1
+	LLDPLocationAddressWhatClient  LLDPLocationAddressWhat = 2
+)
+
+type LLDPLocationAddressType uint8
+
+const (
+	LLDPLocationAddressTypeLanguage       LLDPLocationAddressType = 0
+	LLDPLocationAddressTypeNational       LLDPLocationAddressType = 1
+	LLDPLocationAddressTypeCounty         LLDPLocationAddressType = 2
+	LLDPLocationAddressTypeCity           LLDPLocationAddressType = 3
+	LLDPLocationAddressTypeCityDivision   LLDPLocationAddressType = 4
+	LLDPLocationAddressTypeNeighborhood   LLDPLocationAddressType = 5
+	LLDPLocationAddressTypeStreet         LLDPLocationAddressType = 6
+	LLDPLocationAddressTypeLeadingStreet  LLDPLocationAddressType = 16
+	LLDPLocationAddressTypeTrailingStreet LLDPLocationAddressType = 17
+	LLDPLocationAddressTypeStreetSuffix   LLDPLocationAddressType = 18
+	LLDPLocationAddressTypeHouseNum       LLDPLocationAddressType = 19
+	LLDPLocationAddressTypeHouseSuffix    LLDPLocationAddressType = 20
+	LLDPLocationAddressTypeLandmark       LLDPLocationAddressType = 21
+	LLDPLocationAddressTypeAdditional     LLDPLocationAddressType = 22
+	LLDPLocationAddressTypeName           LLDPLocationAddressType = 23
+	LLDPLocationAddressTypePostal         LLDPLocationAddressType = 24
+	LLDPLocationAddressTypeBuilding       LLDPLocationAddressType = 25
+	LLDPLocationAddressTypeUnit           LLDPLocationAddressType = 26
+	LLDPLocationAddressTypeFloor          LLDPLocationAddressType = 27
+	LLDPLocationAddressTypeRoom           LLDPLocationAddressType = 28
+	LLDPLocationAddressTypePlace          LLDPLocationAddressType = 29
+	LLDPLocationAddressTypeScript         LLDPLocationAddressType = 128
+)
+
+type LLDPLocationCoordinate struct {
+	LatitudeResolution  uint8
+	Latitude            uint64
+	LongitudeResolution uint8
+	Longitude           uint64
+	AltitudeType        uint8
+	AltitudeResolution  uint16
+	Altitude            uint32
+	Datum               uint8
+}
+
+type LLDPLocationAddressLine struct {
+	Type  LLDPLocationAddressType
+	Value string
+}
+
+type LLDPLocationAddress struct {
+	What         LLDPLocationAddressWhat
+	CountryCode  string
+	AddressLines []LLDPLocationAddressLine
+}
+
+type LLDPLocationECS struct {
+	ELIN string
+}
+
+// LLDP represents a physical location.
+// Only one of the embedded types will contain values, depending on Format.
+type LLDPLocation struct {
+	Format     LLDPLocationFormat
+	Coordinate LLDPLocationCoordinate
+	Address    LLDPLocationAddress
+	ECS        LLDPLocationECS
+}
+
+type LLDPPowerViaMDI struct {
+	Type     LLDPPowerType
+	Source   LLDPPowerSource
+	Priority LLDPPowerPriority
+	Value    uint16
+}
+
+// LLDPInfoMedia represents the information carried in TR-41 Org-specific TLVs
+type LLDPInfoMedia struct {
+	MediaCapabilities LLDPMediaCapabilities
+	NetworkPolicy     LLDPNetworkPolicy
+	Location          LLDPLocation
+	PowerViaMDI       LLDPPowerViaMDI
+	HardwareRevision  string
+	FirmwareRevision  string
+	SoftwareRevision  string
+	SerialNumber      string
+	Manufacturer      string
+	Model             string
+	AssetID           string
+}
+
+type LLDPCisco2Subtype uint8
+
+// Cisco2 TLV Subtypes
+const (
+	LLDPCisco2PowerViaMDI LLDPCisco2Subtype = 1
+)
+
+const (
+	LLDPCiscoPSESupport   uint8 = 1 << 0
+	LLDPCiscoArchShared   uint8 = 1 << 1
+	LLDPCiscoPDSparePair  uint8 = 1 << 2
+	LLDPCiscoPSESparePair uint8 = 1 << 3
+)
+
+// LLDPInfoCisco2 represents the information carried in Cisco Org-specific TLVs
+type LLDPInfoCisco2 struct {
+	PSEFourWirePoESupported       bool
+	PDSparePairArchitectureShared bool
+	PDRequestSparePairPoEOn       bool
+	PSESparePairPoEOn             bool
+}
+
+// Profinet Subtypes
+type LLDPProfinetSubtype uint8
+
+const (
+	LLDPProfinetPNIODelay         LLDPProfinetSubtype = 1
+	LLDPProfinetPNIOPortStatus    LLDPProfinetSubtype = 2
+	LLDPProfinetPNIOMRPPortStatus LLDPProfinetSubtype = 4
+	LLDPProfinetPNIOChassisMAC    LLDPProfinetSubtype = 5
+	LLDPProfinetPNIOPTCPStatus    LLDPProfinetSubtype = 6
+)
+
+type LLDPPNIODelay struct {
+	RXLocal    uint32
+	RXRemote   uint32
+	TXLocal    uint32
+	TXRemote   uint32
+	CableLocal uint32
+}
+
+type LLDPPNIOPortStatus struct {
+	Class2 uint16
+	Class3 uint16
+}
+
+type LLDPPNIOMRPPortStatus struct {
+	UUID   []byte
+	Status uint16
+}
+
+type LLDPPNIOPTCPStatus struct {
+	MasterAddress     []byte
+	SubdomainUUID     []byte
+	IRDataUUID        []byte
+	PeriodValid       bool
+	PeriodLength      uint32
+	RedPeriodValid    bool
+	RedPeriodBegin    uint32
+	OrangePeriodValid bool
+	OrangePeriodBegin uint32
+	GreenPeriodValid  bool
+	GreenPeriodBegin  uint32
+}
+
+// LLDPInfoProfinet represents the information carried in Profinet Org-specific TLVs
+type LLDPInfoProfinet struct {
+	PNIODelay         LLDPPNIODelay
+	PNIOPortStatus    LLDPPNIOPortStatus
+	PNIOMRPPortStatus LLDPPNIOMRPPortStatus
+	ChassisMAC        []byte
+	PNIOPTCPStatus    LLDPPNIOPTCPStatus
+}
+
+// LayerType returns gopacket.LayerTypeLinkLayerDiscovery.
+func (c *LinkLayerDiscovery) LayerType() gopacket.LayerType {
+	return LayerTypeLinkLayerDiscovery
+}
+
+// SerializeTo serializes LLDP packet to bytes and writes on SerializeBuffer.
+func (c *LinkLayerDiscovery) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	chassIDLen := c.ChassisID.serializedLen()
+	portIDLen := c.PortID.serializedLen()
+	vb, err := b.AppendBytes(chassIDLen + portIDLen + 4) // +4 for TTL
+	if err != nil {
+		return err
+	}
+	copy(vb[:chassIDLen], c.ChassisID.serialize())
+	copy(vb[chassIDLen:], c.PortID.serialize())
+	ttlIDLen := uint16(LLDPTLVTTL)<<9 | uint16(2)
+	binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen:], ttlIDLen)
+	binary.BigEndian.PutUint16(vb[chassIDLen+portIDLen+2:], c.TTL)
+
+	for _, v := range c.Values {
+		vb, err := b.AppendBytes(int(v.Length) + 2) // +2 for TLV type and length; 1 byte for subtype is included in v.Value
+		if err != nil {
+			return err
+		}
+		idLen := ((uint16(v.Type) << 9) | v.Length)
+		binary.BigEndian.PutUint16(vb[0:2], idLen)
+		copy(vb[2:], v.Value)
+	}
+
+	vb, err = b.AppendBytes(2) // End Tlv, 2 bytes
+	if err != nil {
+		return err
+	}
+	binary.BigEndian.PutUint16(vb[len(vb)-2:], uint16(0)) //End tlv, 2 bytes, all zero
+	return nil
+
+}
+
+func decodeLinkLayerDiscovery(data []byte, p gopacket.PacketBuilder) error {
+	var vals []LinkLayerDiscoveryValue
+	vData := data[0:]
+	for len(vData) > 0 {
+		if len(vData) < 2 {
+			p.SetTruncated()
+			return errors.New("LLDP vdata < 2 bytes")
+		}
+		nbit := vData[0] & 0x01
+		t := LLDPTLVType(vData[0] >> 1)
+		val := LinkLayerDiscoveryValue{Type: t, Length: uint16(nbit)<<8 + uint16(vData[1])}
+		if val.Length > 0 {
+			if len(vData) < int(val.Length+2) {
+				p.SetTruncated()
+				return fmt.Errorf("LLDP VData < %d bytes", val.Length+2)
+			}
+			val.Value = vData[2 : val.Length+2]
+		}
+		vals = append(vals, val)
+		if t == LLDPTLVEnd {
+			break
+		}
+		if len(vData) < int(2+val.Length) {
+			return errors.New("Malformed LinkLayerDiscovery Header")
+		}
+		vData = vData[2+val.Length:]
+	}
+	if len(vals) < 4 {
+		return errors.New("Missing mandatory LinkLayerDiscovery TLV")
+	}
+	c := &LinkLayerDiscovery{}
+	gotEnd := false
+	for _, v := range vals {
+		switch v.Type {
+		case LLDPTLVEnd:
+			gotEnd = true
+		case LLDPTLVChassisID:
+			if len(v.Value) < 2 {
+				return errors.New("Malformed LinkLayerDiscovery ChassisID TLV")
+			}
+			c.ChassisID.Subtype = LLDPChassisIDSubType(v.Value[0])
+			c.ChassisID.ID = v.Value[1:]
+		case LLDPTLVPortID:
+			if len(v.Value) < 2 {
+				return errors.New("Malformed LinkLayerDiscovery PortID TLV")
+			}
+			c.PortID.Subtype = LLDPPortIDSubType(v.Value[0])
+			c.PortID.ID = v.Value[1:]
+		case LLDPTLVTTL:
+			if len(v.Value) < 2 {
+				return errors.New("Malformed LinkLayerDiscovery TTL TLV")
+			}
+			c.TTL = binary.BigEndian.Uint16(v.Value[0:2])
+		default:
+			c.Values = append(c.Values, v)
+		}
+	}
+	if c.ChassisID.Subtype == 0 || c.PortID.Subtype == 0 || !gotEnd {
+		return errors.New("Missing mandatory LinkLayerDiscovery TLV")
+	}
+	c.Contents = data
+	p.AddLayer(c)
+
+	info := &LinkLayerDiscoveryInfo{}
+	p.AddLayer(info)
+	for _, v := range c.Values {
+		switch v.Type {
+		case LLDPTLVPortDescription:
+			info.PortDescription = string(v.Value)
+		case LLDPTLVSysName:
+			info.SysName = string(v.Value)
+		case LLDPTLVSysDescription:
+			info.SysDescription = string(v.Value)
+		case LLDPTLVSysCapabilities:
+			if err := checkLLDPTLVLen(v, 4); err != nil {
+				return err
+			}
+			info.SysCapabilities.SystemCap = getCapabilities(binary.BigEndian.Uint16(v.Value[0:2]))
+			info.SysCapabilities.EnabledCap = getCapabilities(binary.BigEndian.Uint16(v.Value[2:4]))
+		case LLDPTLVMgmtAddress:
+			if err := checkLLDPTLVLen(v, 9); err != nil {
+				return err
+			}
+			mlen := v.Value[0]
+			if err := checkLLDPTLVLen(v, int(mlen+7)); err != nil {
+				return err
+			}
+			info.MgmtAddress.Subtype = IANAAddressFamily(v.Value[1])
+			info.MgmtAddress.Address = v.Value[2 : mlen+1]
+			info.MgmtAddress.InterfaceSubtype = LLDPInterfaceSubtype(v.Value[mlen+1])
+			info.MgmtAddress.InterfaceNumber = binary.BigEndian.Uint32(v.Value[mlen+2 : mlen+6])
+			olen := v.Value[mlen+6]
+			if err := checkLLDPTLVLen(v, int(mlen+7+olen)); err != nil {
+				return err
+			}
+			info.MgmtAddress.OID = string(v.Value[mlen+7 : mlen+7+olen])
+		case LLDPTLVOrgSpecific:
+			if err := checkLLDPTLVLen(v, 4); err != nil {
+				return err
+			}
+			info.OrgTLVs = append(info.OrgTLVs, LLDPOrgSpecificTLV{IEEEOUI(binary.BigEndian.Uint32(append([]byte{byte(0)}, v.Value[0:3]...))), uint8(v.Value[3]), v.Value[4:]})
+		}
+	}
+	return nil
+}
+
+func (l *LinkLayerDiscoveryInfo) Decode8021() (info LLDPInfo8021, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUI8021 {
+			continue
+		}
+		switch o.SubType {
+		case LLDP8021SubtypePortVLANID:
+			if err = checkLLDPOrgSpecificLen(o, 2); err != nil {
+				return
+			}
+			info.PVID = binary.BigEndian.Uint16(o.Info[0:2])
+		case LLDP8021SubtypeProtocolVLANID:
+			if err = checkLLDPOrgSpecificLen(o, 3); err != nil {
+				return
+			}
+			sup := (o.Info[0]&LLDPProtocolVLANIDCapability > 0)
+			en := (o.Info[0]&LLDPProtocolVLANIDStatus > 0)
+			id := binary.BigEndian.Uint16(o.Info[1:3])
+			info.PPVIDs = append(info.PPVIDs, PortProtocolVLANID{sup, en, id})
+		case LLDP8021SubtypeVLANName:
+			if err = checkLLDPOrgSpecificLen(o, 2); err != nil {
+				return
+			}
+			id := binary.BigEndian.Uint16(o.Info[0:2])
+			info.VLANNames = append(info.VLANNames, VLANName{id, string(o.Info[3:])})
+		case LLDP8021SubtypeProtocolIdentity:
+			if err = checkLLDPOrgSpecificLen(o, 1); err != nil {
+				return
+			}
+			l := int(o.Info[0])
+			if l > 0 {
+				info.ProtocolIdentities = append(info.ProtocolIdentities, o.Info[1:1+l])
+			}
+		case LLDP8021SubtypeVDIUsageDigest:
+			if err = checkLLDPOrgSpecificLen(o, 4); err != nil {
+				return
+			}
+			info.VIDUsageDigest = binary.BigEndian.Uint32(o.Info[0:4])
+		case LLDP8021SubtypeManagementVID:
+			if err = checkLLDPOrgSpecificLen(o, 2); err != nil {
+				return
+			}
+			info.ManagementVID = binary.BigEndian.Uint16(o.Info[0:2])
+		case LLDP8021SubtypeLinkAggregation:
+			if err = checkLLDPOrgSpecificLen(o, 5); err != nil {
+				return
+			}
+			sup := (o.Info[0]&LLDPAggregationCapability > 0)
+			en := (o.Info[0]&LLDPAggregationStatus > 0)
+			info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])}
+		}
+	}
+	return
+}
+
+func (l *LinkLayerDiscoveryInfo) Decode8023() (info LLDPInfo8023, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUI8023 {
+			continue
+		}
+		switch o.SubType {
+		case LLDP8023SubtypeMACPHY:
+			if err = checkLLDPOrgSpecificLen(o, 5); err != nil {
+				return
+			}
+			sup := (o.Info[0]&LLDPMACPHYCapability > 0)
+			en := (o.Info[0]&LLDPMACPHYStatus > 0)
+			ca := binary.BigEndian.Uint16(o.Info[1:3])
+			mau := binary.BigEndian.Uint16(o.Info[3:5])
+			info.MACPHYConfigStatus = LLDPMACPHYConfigStatus{sup, en, ca, mau}
+		case LLDP8023SubtypeMDIPower:
+			if err = checkLLDPOrgSpecificLen(o, 3); err != nil {
+				return
+			}
+			info.PowerViaMDI.PortClassPSE = (o.Info[0]&LLDPMDIPowerPortClass > 0)
+			info.PowerViaMDI.PSESupported = (o.Info[0]&LLDPMDIPowerCapability > 0)
+			info.PowerViaMDI.PSEEnabled = (o.Info[0]&LLDPMDIPowerStatus > 0)
+			info.PowerViaMDI.PSEPairsAbility = (o.Info[0]&LLDPMDIPowerPairsAbility > 0)
+			info.PowerViaMDI.PSEPowerPair = uint8(o.Info[1])
+			info.PowerViaMDI.PSEClass = uint8(o.Info[2])
+			if len(o.Info) >= 7 {
+				info.PowerViaMDI.Type = LLDPPowerType((o.Info[3] & 0xc0) >> 6)
+				info.PowerViaMDI.Source = LLDPPowerSource((o.Info[3] & 0x30) >> 4)
+				if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 {
+					info.PowerViaMDI.Source += 128 // For Stringify purposes
+				}
+				info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[3] & 0x0f)
+				info.PowerViaMDI.Requested = binary.BigEndian.Uint16(o.Info[4:6])
+				info.PowerViaMDI.Allocated = binary.BigEndian.Uint16(o.Info[6:8])
+			}
+		case LLDP8023SubtypeLinkAggregation:
+			if err = checkLLDPOrgSpecificLen(o, 5); err != nil {
+				return
+			}
+			sup := (o.Info[0]&LLDPAggregationCapability > 0)
+			en := (o.Info[0]&LLDPAggregationStatus > 0)
+			info.LinkAggregation = LLDPLinkAggregation{sup, en, binary.BigEndian.Uint32(o.Info[1:5])}
+		case LLDP8023SubtypeMTU:
+			if err = checkLLDPOrgSpecificLen(o, 2); err != nil {
+				return
+			}
+			info.MTU = binary.BigEndian.Uint16(o.Info[0:2])
+		}
+	}
+	return
+}
+
+func (l *LinkLayerDiscoveryInfo) Decode8021Qbg() (info LLDPInfo8021Qbg, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUI8021Qbg {
+			continue
+		}
+		switch o.SubType {
+		case LLDP8021QbgEVB:
+			if err = checkLLDPOrgSpecificLen(o, 9); err != nil {
+				return
+			}
+			info.EVBSettings.Supported = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[0:2]))
+			info.EVBSettings.Enabled = getEVBCapabilities(binary.BigEndian.Uint16(o.Info[2:4]))
+			info.EVBSettings.SupportedVSIs = binary.BigEndian.Uint16(o.Info[4:6])
+			info.EVBSettings.ConfiguredVSIs = binary.BigEndian.Uint16(o.Info[6:8])
+			info.EVBSettings.RTEExponent = uint8(o.Info[8])
+		}
+	}
+	return
+}
+
+func (l *LinkLayerDiscoveryInfo) DecodeMedia() (info LLDPInfoMedia, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUIMedia {
+			continue
+		}
+		switch LLDPMediaSubtype(o.SubType) {
+		case LLDPMediaTypeCapabilities:
+			if err = checkLLDPOrgSpecificLen(o, 3); err != nil {
+				return
+			}
+			b := binary.BigEndian.Uint16(o.Info[0:2])
+			info.MediaCapabilities.Capabilities = (b & LLDPMediaCapsLLDP) > 0
+			info.MediaCapabilities.NetworkPolicy = (b & LLDPMediaCapsNetwork) > 0
+			info.MediaCapabilities.Location = (b & LLDPMediaCapsLocation) > 0
+			info.MediaCapabilities.PowerPSE = (b & LLDPMediaCapsPowerPSE) > 0
+			info.MediaCapabilities.PowerPD = (b & LLDPMediaCapsPowerPD) > 0
+			info.MediaCapabilities.Inventory = (b & LLDPMediaCapsInventory) > 0
+			info.MediaCapabilities.Class = LLDPMediaClass(o.Info[2])
+		case LLDPMediaTypeNetwork:
+			if err = checkLLDPOrgSpecificLen(o, 4); err != nil {
+				return
+			}
+			info.NetworkPolicy.ApplicationType = LLDPApplicationType(o.Info[0])
+			b := binary.BigEndian.Uint16(o.Info[1:3])
+			info.NetworkPolicy.Defined = (b & 0x8000) == 0
+			info.NetworkPolicy.Tagged = (b & 0x4000) > 0
+			info.NetworkPolicy.VLANId = (b & 0x1ffe) >> 1
+			b = binary.BigEndian.Uint16(o.Info[2:4])
+			info.NetworkPolicy.L2Priority = (b & 0x01c0) >> 6
+			info.NetworkPolicy.DSCPValue = uint8(o.Info[3] & 0x3f)
+		case LLDPMediaTypeLocation:
+			if err = checkLLDPOrgSpecificLen(o, 1); err != nil {
+				return
+			}
+			info.Location.Format = LLDPLocationFormat(o.Info[0])
+			o.Info = o.Info[1:]
+			switch info.Location.Format {
+			case LLDPLocationFormatCoordinate:
+				if err = checkLLDPOrgSpecificLen(o, 16); err != nil {
+					return
+				}
+				info.Location.Coordinate.LatitudeResolution = uint8(o.Info[0]&0xfc) >> 2
+				b := binary.BigEndian.Uint64(o.Info[0:8])
+				info.Location.Coordinate.Latitude = (b & 0x03ffffffff000000) >> 24
+				info.Location.Coordinate.LongitudeResolution = uint8(o.Info[5]&0xfc) >> 2
+				b = binary.BigEndian.Uint64(o.Info[5:13])
+				info.Location.Coordinate.Longitude = (b & 0x03ffffffff000000) >> 24
+				info.Location.Coordinate.AltitudeType = uint8((o.Info[10] & 0x30) >> 4)
+				b1 := binary.BigEndian.Uint16(o.Info[10:12])
+				info.Location.Coordinate.AltitudeResolution = (b1 & 0xfc0) >> 6
+				b2 := binary.BigEndian.Uint32(o.Info[11:15])
+				info.Location.Coordinate.Altitude = b2 & 0x3fffffff
+				info.Location.Coordinate.Datum = uint8(o.Info[15])
+			case LLDPLocationFormatAddress:
+				if err = checkLLDPOrgSpecificLen(o, 3); err != nil {
+					return
+				}
+				//ll := uint8(o.Info[0])
+				info.Location.Address.What = LLDPLocationAddressWhat(o.Info[1])
+				info.Location.Address.CountryCode = string(o.Info[2:4])
+				data := o.Info[4:]
+				for len(data) > 1 {
+					aType := LLDPLocationAddressType(data[0])
+					aLen := int(data[1])
+					if len(data) >= aLen+2 {
+						info.Location.Address.AddressLines = append(info.Location.Address.AddressLines, LLDPLocationAddressLine{aType, string(data[2 : aLen+2])})
+						data = data[aLen+2:]
+					} else {
+						break
+					}
+				}
+			case LLDPLocationFormatECS:
+				info.Location.ECS.ELIN = string(o.Info)
+			}
+		case LLDPMediaTypePower:
+			if err = checkLLDPOrgSpecificLen(o, 3); err != nil {
+				return
+			}
+			info.PowerViaMDI.Type = LLDPPowerType((o.Info[0] & 0xc0) >> 6)
+			info.PowerViaMDI.Source = LLDPPowerSource((o.Info[0] & 0x30) >> 4)
+			if info.PowerViaMDI.Type == 1 || info.PowerViaMDI.Type == 3 {
+				info.PowerViaMDI.Source += 128 // For Stringify purposes
+			}
+			info.PowerViaMDI.Priority = LLDPPowerPriority(o.Info[0] & 0x0f)
+			info.PowerViaMDI.Value = binary.BigEndian.Uint16(o.Info[1:3]) * 100 // 0 to 102.3 w, 0.1W increments
+		case LLDPMediaTypeHardware:
+			info.HardwareRevision = string(o.Info)
+		case LLDPMediaTypeFirmware:
+			info.FirmwareRevision = string(o.Info)
+		case LLDPMediaTypeSoftware:
+			info.SoftwareRevision = string(o.Info)
+		case LLDPMediaTypeSerial:
+			info.SerialNumber = string(o.Info)
+		case LLDPMediaTypeManufacturer:
+			info.Manufacturer = string(o.Info)
+		case LLDPMediaTypeModel:
+			info.Model = string(o.Info)
+		case LLDPMediaTypeAssetID:
+			info.AssetID = string(o.Info)
+		}
+	}
+	return
+}
+
+func (l *LinkLayerDiscoveryInfo) DecodeCisco2() (info LLDPInfoCisco2, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUICisco2 {
+			continue
+		}
+		switch LLDPCisco2Subtype(o.SubType) {
+		case LLDPCisco2PowerViaMDI:
+			if err = checkLLDPOrgSpecificLen(o, 1); err != nil {
+				return
+			}
+			info.PSEFourWirePoESupported = (o.Info[0] & LLDPCiscoPSESupport) > 0
+			info.PDSparePairArchitectureShared = (o.Info[0] & LLDPCiscoArchShared) > 0
+			info.PDRequestSparePairPoEOn = (o.Info[0] & LLDPCiscoPDSparePair) > 0
+			info.PSESparePairPoEOn = (o.Info[0] & LLDPCiscoPSESparePair) > 0
+		}
+	}
+	return
+}
+
+func (l *LinkLayerDiscoveryInfo) DecodeProfinet() (info LLDPInfoProfinet, err error) {
+	for _, o := range l.OrgTLVs {
+		if o.OUI != IEEEOUIProfinet {
+			continue
+		}
+		switch LLDPProfinetSubtype(o.SubType) {
+		case LLDPProfinetPNIODelay:
+			if err = checkLLDPOrgSpecificLen(o, 20); err != nil {
+				return
+			}
+			info.PNIODelay.RXLocal = binary.BigEndian.Uint32(o.Info[0:4])
+			info.PNIODelay.RXRemote = binary.BigEndian.Uint32(o.Info[4:8])
+			info.PNIODelay.TXLocal = binary.BigEndian.Uint32(o.Info[8:12])
+			info.PNIODelay.TXRemote = binary.BigEndian.Uint32(o.Info[12:16])
+			info.PNIODelay.CableLocal = binary.BigEndian.Uint32(o.Info[16:20])
+		case LLDPProfinetPNIOPortStatus:
+			if err = checkLLDPOrgSpecificLen(o, 4); err != nil {
+				return
+			}
+			info.PNIOPortStatus.Class2 = binary.BigEndian.Uint16(o.Info[0:2])
+			info.PNIOPortStatus.Class3 = binary.BigEndian.Uint16(o.Info[2:4])
+		case LLDPProfinetPNIOMRPPortStatus:
+			if err = checkLLDPOrgSpecificLen(o, 18); err != nil {
+				return
+			}
+			info.PNIOMRPPortStatus.UUID = o.Info[0:16]
+			info.PNIOMRPPortStatus.Status = binary.BigEndian.Uint16(o.Info[16:18])
+		case LLDPProfinetPNIOChassisMAC:
+			if err = checkLLDPOrgSpecificLen(o, 6); err != nil {
+				return
+			}
+			info.ChassisMAC = o.Info[0:6]
+		case LLDPProfinetPNIOPTCPStatus:
+			if err = checkLLDPOrgSpecificLen(o, 54); err != nil {
+				return
+			}
+			info.PNIOPTCPStatus.MasterAddress = o.Info[0:6]
+			info.PNIOPTCPStatus.SubdomainUUID = o.Info[6:22]
+			info.PNIOPTCPStatus.IRDataUUID = o.Info[22:38]
+			b := binary.BigEndian.Uint32(o.Info[38:42])
+			info.PNIOPTCPStatus.PeriodValid = (b & 0x80000000) > 0
+			info.PNIOPTCPStatus.PeriodLength = b & 0x7fffffff
+			b = binary.BigEndian.Uint32(o.Info[42:46])
+			info.PNIOPTCPStatus.RedPeriodValid = (b & 0x80000000) > 0
+			info.PNIOPTCPStatus.RedPeriodBegin = b & 0x7fffffff
+			b = binary.BigEndian.Uint32(o.Info[46:50])
+			info.PNIOPTCPStatus.OrangePeriodValid = (b & 0x80000000) > 0
+			info.PNIOPTCPStatus.OrangePeriodBegin = b & 0x7fffffff
+			b = binary.BigEndian.Uint32(o.Info[50:54])
+			info.PNIOPTCPStatus.GreenPeriodValid = (b & 0x80000000) > 0
+			info.PNIOPTCPStatus.GreenPeriodBegin = b & 0x7fffffff
+		}
+	}
+	return
+}
+
+// LayerType returns gopacket.LayerTypeLinkLayerDiscoveryInfo.
+func (c *LinkLayerDiscoveryInfo) LayerType() gopacket.LayerType {
+	return LayerTypeLinkLayerDiscoveryInfo
+}
+
+func getCapabilities(v uint16) (c LLDPCapabilities) {
+	c.Other = (v&LLDPCapsOther > 0)
+	c.Repeater = (v&LLDPCapsRepeater > 0)
+	c.Bridge = (v&LLDPCapsBridge > 0)
+	c.WLANAP = (v&LLDPCapsWLANAP > 0)
+	c.Router = (v&LLDPCapsRouter > 0)
+	c.Phone = (v&LLDPCapsPhone > 0)
+	c.DocSis = (v&LLDPCapsDocSis > 0)
+	c.StationOnly = (v&LLDPCapsStationOnly > 0)
+	c.CVLAN = (v&LLDPCapsCVLAN > 0)
+	c.SVLAN = (v&LLDPCapsSVLAN > 0)
+	c.TMPR = (v&LLDPCapsTmpr > 0)
+	return
+}
+
+func getEVBCapabilities(v uint16) (c LLDPEVBCapabilities) {
+	c.StandardBridging = (v & LLDPEVBCapsSTD) > 0
+	c.StandardBridging = (v & LLDPEVBCapsSTD) > 0
+	c.ReflectiveRelay = (v & LLDPEVBCapsRR) > 0
+	c.RetransmissionTimerExponent = (v & LLDPEVBCapsRTE) > 0
+	c.EdgeControlProtocol = (v & LLDPEVBCapsECP) > 0
+	c.VSIDiscoveryProtocol = (v & LLDPEVBCapsVDP) > 0
+	return
+}
+
+func (t LLDPTLVType) String() (s string) {
+	switch t {
+	case LLDPTLVEnd:
+		s = "TLV End"
+	case LLDPTLVChassisID:
+		s = "Chassis ID"
+	case LLDPTLVPortID:
+		s = "Port ID"
+	case LLDPTLVTTL:
+		s = "TTL"
+	case LLDPTLVPortDescription:
+		s = "Port Description"
+	case LLDPTLVSysName:
+		s = "System Name"
+	case LLDPTLVSysDescription:
+		s = "System Description"
+	case LLDPTLVSysCapabilities:
+		s = "System Capabilities"
+	case LLDPTLVMgmtAddress:
+		s = "Management Address"
+	case LLDPTLVOrgSpecific:
+		s = "Organisation Specific"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPChassisIDSubType) String() (s string) {
+	switch t {
+	case LLDPChassisIDSubTypeReserved:
+		s = "Reserved"
+	case LLDPChassisIDSubTypeChassisComp:
+		s = "Chassis Component"
+	case LLDPChassisIDSubtypeIfaceAlias:
+		s = "Interface Alias"
+	case LLDPChassisIDSubTypePortComp:
+		s = "Port Component"
+	case LLDPChassisIDSubTypeMACAddr:
+		s = "MAC Address"
+	case LLDPChassisIDSubTypeNetworkAddr:
+		s = "Network Address"
+	case LLDPChassisIDSubtypeIfaceName:
+		s = "Interface Name"
+	case LLDPChassisIDSubTypeLocal:
+		s = "Local"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPPortIDSubType) String() (s string) {
+	switch t {
+	case LLDPPortIDSubtypeReserved:
+		s = "Reserved"
+	case LLDPPortIDSubtypeIfaceAlias:
+		s = "Interface Alias"
+	case LLDPPortIDSubtypePortComp:
+		s = "Port Component"
+	case LLDPPortIDSubtypeMACAddr:
+		s = "MAC Address"
+	case LLDPPortIDSubtypeNetworkAddr:
+		s = "Network Address"
+	case LLDPPortIDSubtypeIfaceName:
+		s = "Interface Name"
+	case LLDPPortIDSubtypeAgentCircuitID:
+		s = "Agent Circuit ID"
+	case LLDPPortIDSubtypeLocal:
+		s = "Local"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t IANAAddressFamily) String() (s string) {
+	switch t {
+	case IANAAddressFamilyReserved:
+		s = "Reserved"
+	case IANAAddressFamilyIPV4:
+		s = "IPv4"
+	case IANAAddressFamilyIPV6:
+		s = "IPv6"
+	case IANAAddressFamilyNSAP:
+		s = "NSAP"
+	case IANAAddressFamilyHDLC:
+		s = "HDLC"
+	case IANAAddressFamilyBBN1822:
+		s = "BBN 1822"
+	case IANAAddressFamily802:
+		s = "802 media plus Ethernet 'canonical format'"
+	case IANAAddressFamilyE163:
+		s = "E.163"
+	case IANAAddressFamilyE164:
+		s = "E.164 (SMDS, Frame Relay, ATM)"
+	case IANAAddressFamilyF69:
+		s = "F.69 (Telex)"
+	case IANAAddressFamilyX121:
+		s = "X.121, X.25, Frame Relay"
+	case IANAAddressFamilyIPX:
+		s = "IPX"
+	case IANAAddressFamilyAtalk:
+		s = "Appletalk"
+	case IANAAddressFamilyDecnet:
+		s = "Decnet IV"
+	case IANAAddressFamilyBanyan:
+		s = "Banyan Vines"
+	case IANAAddressFamilyE164NSAP:
+		s = "E.164 with NSAP format subaddress"
+	case IANAAddressFamilyDNS:
+		s = "DNS"
+	case IANAAddressFamilyDistname:
+		s = "Distinguished Name"
+	case IANAAddressFamilyASNumber:
+		s = "AS Number"
+	case IANAAddressFamilyXTPIPV4:
+		s = "XTP over IP version 4"
+	case IANAAddressFamilyXTPIPV6:
+		s = "XTP over IP version 6"
+	case IANAAddressFamilyXTP:
+		s = "XTP native mode XTP"
+	case IANAAddressFamilyFcWWPN:
+		s = "Fibre Channel World-Wide Port Name"
+	case IANAAddressFamilyFcWWNN:
+		s = "Fibre Channel World-Wide Node Name"
+	case IANAAddressFamilyGWID:
+		s = "GWID"
+	case IANAAddressFamilyL2VPN:
+		s = "AFI for Layer 2 VPN"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPInterfaceSubtype) String() (s string) {
+	switch t {
+	case LLDPInterfaceSubtypeUnknown:
+		s = "Unknown"
+	case LLDPInterfaceSubtypeifIndex:
+		s = "IfIndex"
+	case LLDPInterfaceSubtypeSysPort:
+		s = "System Port Number"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPPowerType) String() (s string) {
+	switch t {
+	case 0:
+		s = "Type 2 PSE Device"
+	case 1:
+		s = "Type 2 PD Device"
+	case 2:
+		s = "Type 1 PSE Device"
+	case 3:
+		s = "Type 1 PD Device"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPPowerSource) String() (s string) {
+	switch t {
+	// PD Device
+	case 0:
+		s = "Unknown"
+	case 1:
+		s = "PSE"
+	case 2:
+		s = "Local"
+	case 3:
+		s = "PSE and Local"
+	// PSE Device  (Actual value  + 128)
+	case 128:
+		s = "Unknown"
+	case 129:
+		s = "Primary Power Source"
+	case 130:
+		s = "Backup Power Source"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPPowerPriority) String() (s string) {
+	switch t {
+	case 0:
+		s = "Unknown"
+	case 1:
+		s = "Critical"
+	case 2:
+		s = "High"
+	case 3:
+		s = "Low"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPMediaSubtype) String() (s string) {
+	switch t {
+	case LLDPMediaTypeCapabilities:
+		s = "Media Capabilities "
+	case LLDPMediaTypeNetwork:
+		s = "Network Policy"
+	case LLDPMediaTypeLocation:
+		s = "Location Identification"
+	case LLDPMediaTypePower:
+		s = "Extended Power-via-MDI"
+	case LLDPMediaTypeHardware:
+		s = "Hardware Revision"
+	case LLDPMediaTypeFirmware:
+		s = "Firmware Revision"
+	case LLDPMediaTypeSoftware:
+		s = "Software Revision"
+	case LLDPMediaTypeSerial:
+		s = "Serial Number"
+	case LLDPMediaTypeManufacturer:
+		s = "Manufacturer"
+	case LLDPMediaTypeModel:
+		s = "Model"
+	case LLDPMediaTypeAssetID:
+		s = "Asset ID"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPMediaClass) String() (s string) {
+	switch t {
+	case LLDPMediaClassUndefined:
+		s = "Undefined"
+	case LLDPMediaClassEndpointI:
+		s = "Endpoint Class I"
+	case LLDPMediaClassEndpointII:
+		s = "Endpoint Class II"
+	case LLDPMediaClassEndpointIII:
+		s = "Endpoint Class III"
+	case LLDPMediaClassNetwork:
+		s = "Network connectivity "
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPApplicationType) String() (s string) {
+	switch t {
+	case LLDPAppTypeReserved:
+		s = "Reserved"
+	case LLDPAppTypeVoice:
+		s = "Voice"
+	case LLDPappTypeVoiceSignaling:
+		s = "Voice Signaling"
+	case LLDPappTypeGuestVoice:
+		s = "Guest Voice"
+	case LLDPappTypeGuestVoiceSignaling:
+		s = "Guest Voice Signaling"
+	case LLDPappTypeSoftphoneVoice:
+		s = "Softphone Voice"
+	case LLDPappTypeVideoConferencing:
+		s = "Video Conferencing"
+	case LLDPappTypeStreamingVideo:
+		s = "Streaming Video"
+	case LLDPappTypeVideoSignaling:
+		s = "Video Signaling"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPLocationFormat) String() (s string) {
+	switch t {
+	case LLDPLocationFormatInvalid:
+		s = "Invalid"
+	case LLDPLocationFormatCoordinate:
+		s = "Coordinate-based LCI"
+	case LLDPLocationFormatAddress:
+		s = "Address-based LCO"
+	case LLDPLocationFormatECS:
+		s = "ECS ELIN"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t LLDPLocationAddressType) String() (s string) {
+	switch t {
+	case LLDPLocationAddressTypeLanguage:
+		s = "Language"
+	case LLDPLocationAddressTypeNational:
+		s = "National subdivisions (province, state, etc)"
+	case LLDPLocationAddressTypeCounty:
+		s = "County, parish, district"
+	case LLDPLocationAddressTypeCity:
+		s = "City, township"
+	case LLDPLocationAddressTypeCityDivision:
+		s = "City division, borough, ward"
+	case LLDPLocationAddressTypeNeighborhood:
+		s = "Neighborhood, block"
+	case LLDPLocationAddressTypeStreet:
+		s = "Street"
+	case LLDPLocationAddressTypeLeadingStreet:
+		s = "Leading street direction"
+	case LLDPLocationAddressTypeTrailingStreet:
+		s = "Trailing street suffix"
+	case LLDPLocationAddressTypeStreetSuffix:
+		s = "Street suffix"
+	case LLDPLocationAddressTypeHouseNum:
+		s = "House number"
+	case LLDPLocationAddressTypeHouseSuffix:
+		s = "House number suffix"
+	case LLDPLocationAddressTypeLandmark:
+		s = "Landmark or vanity address"
+	case LLDPLocationAddressTypeAdditional:
+		s = "Additional location information"
+	case LLDPLocationAddressTypeName:
+		s = "Name"
+	case LLDPLocationAddressTypePostal:
+		s = "Postal/ZIP code"
+	case LLDPLocationAddressTypeBuilding:
+		s = "Building"
+	case LLDPLocationAddressTypeUnit:
+		s = "Unit"
+	case LLDPLocationAddressTypeFloor:
+		s = "Floor"
+	case LLDPLocationAddressTypeRoom:
+		s = "Room number"
+	case LLDPLocationAddressTypePlace:
+		s = "Place type"
+	case LLDPLocationAddressTypeScript:
+		s = "Script"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func checkLLDPTLVLen(v LinkLayerDiscoveryValue, l int) (err error) {
+	if len(v.Value) < l {
+		err = fmt.Errorf("Invalid TLV %v length %d (wanted mimimum %v", v.Type, len(v.Value), l)
+	}
+	return
+}
+
+func checkLLDPOrgSpecificLen(o LLDPOrgSpecificTLV, l int) (err error) {
+	if len(o.Info) < l {
+		err = fmt.Errorf("Invalid Org Specific TLV %v length %d (wanted minimum %v)", o.SubType, len(o.Info), l)
+	}
+	return
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/loopback.go b/go-controller/vendor/github.com/google/gopacket/layers/loopback.go
new file mode 100644
index 00000000000..839f760739b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/loopback.go
@@ -0,0 +1,80 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// Loopback contains the header for loopback encapsulation.  This header is
+// used by both BSD and OpenBSD style loopback decoding (pcap's DLT_NULL
+// and DLT_LOOP, respectively).
+type Loopback struct {
+	BaseLayer
+	Family ProtocolFamily
+}
+
+// LayerType returns LayerTypeLoopback.
+func (l *Loopback) LayerType() gopacket.LayerType { return LayerTypeLoopback }
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (l *Loopback) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		return errors.New("Loopback packet too small")
+	}
+
+	// The protocol could be either big-endian or little-endian, we're
+	// not sure.  But we're PRETTY sure that the value is less than
+	// 256, so we can check the first two bytes.
+	var prot uint32
+	if data[0] == 0 && data[1] == 0 {
+		prot = binary.BigEndian.Uint32(data[:4])
+	} else {
+		prot = binary.LittleEndian.Uint32(data[:4])
+	}
+	if prot > 0xFF {
+		return fmt.Errorf("Invalid loopback protocol %q", data[:4])
+	}
+
+	l.Family = ProtocolFamily(prot)
+	l.BaseLayer = BaseLayer{data[:4], data[4:]}
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (l *Loopback) CanDecode() gopacket.LayerClass {
+	return LayerTypeLoopback
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (l *Loopback) NextLayerType() gopacket.LayerType {
+	return l.Family.LayerType()
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+func (l *Loopback) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	binary.LittleEndian.PutUint32(bytes, uint32(l.Family))
+	return nil
+}
+
+func decodeLoopback(data []byte, p gopacket.PacketBuilder) error {
+	l := Loopback{}
+	if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil {
+		return err
+	}
+	p.AddLayer(&l)
+	return p.NextDecoder(l.Family)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/mldv1.go b/go-controller/vendor/github.com/google/gopacket/layers/mldv1.go
new file mode 100644
index 00000000000..e1bb1dc00fe
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/mldv1.go
@@ -0,0 +1,182 @@
+// Copyright 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"
+	"math"
+	"net"
+	"time"
+
+	"github.com/google/gopacket"
+)
+
+// MLDv1Message represents the common structure of all MLDv1 messages
+type MLDv1Message struct {
+	BaseLayer
+	// 3.4. Maximum Response Delay
+	MaximumResponseDelay time.Duration
+	// 3.6. Multicast Address
+	// Zero in general query
+	// Specific IPv6 multicast address otherwise
+	MulticastAddress net.IP
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (m *MLDv1Message) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less than 20 bytes for Multicast Listener Query Message V1")
+	}
+
+	m.MaximumResponseDelay = time.Duration(binary.BigEndian.Uint16(data[0:2])) * time.Millisecond
+	// data[2:4] is reserved and not used in mldv1
+	m.MulticastAddress = data[4:20]
+
+	return nil
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (*MLDv1Message) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (m *MLDv1Message) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf, err := b.PrependBytes(20)
+	if err != nil {
+		return err
+	}
+
+	if m.MaximumResponseDelay < 0 {
+		return errors.New("maximum response delay must not be negative")
+	}
+	dms := m.MaximumResponseDelay / time.Millisecond
+	if dms > math.MaxUint16 {
+		return fmt.Errorf("maximum response delay %dms is more than the allowed 65535ms", dms)
+	}
+	binary.BigEndian.PutUint16(buf[0:2], uint16(dms))
+
+	copy(buf[2:4], []byte{0x0, 0x0})
+
+	ma16 := m.MulticastAddress.To16()
+	if ma16 == nil {
+		return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
+	}
+	copy(buf[4:20], ma16)
+
+	return nil
+}
+
+// Sums this layer up nicely formatted
+func (m *MLDv1Message) String() string {
+	return fmt.Sprintf(
+		"Maximum Response Delay: %dms, Multicast Address: %s",
+		m.MaximumResponseDelay/time.Millisecond,
+		m.MulticastAddress)
+}
+
+// MLDv1MulticastListenerQueryMessage are sent by the router to determine
+// whether there are multicast listeners on the link.
+// https://tools.ietf.org/html/rfc2710 Page 5
+type MLDv1MulticastListenerQueryMessage struct {
+	MLDv1Message
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (m *MLDv1MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	err := m.MLDv1Message.DecodeFromBytes(data, df)
+	if err != nil {
+		return err
+	}
+
+	if len(data) > 20 {
+		m.Payload = data[20:]
+	}
+
+	return nil
+}
+
+// LayerType returns LayerTypeMLDv1MulticastListenerQuery.
+func (*MLDv1MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
+	return LayerTypeMLDv1MulticastListenerQuery
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (*MLDv1MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
+	return LayerTypeMLDv1MulticastListenerQuery
+}
+
+// IsGeneralQuery is true when this is a general query.
+// In a Query message, the Multicast Address field is set to zero when
+// sending a General Query.
+// https://tools.ietf.org/html/rfc2710#section-3.6
+func (m *MLDv1MulticastListenerQueryMessage) IsGeneralQuery() bool {
+	return net.IPv6zero.Equal(m.MulticastAddress)
+}
+
+// IsSpecificQuery is true when this is not a general query.
+// In a Query message, the Multicast Address field is set to a specific
+// IPv6 multicast address when sending a Multicast-Address-Specific Query.
+// https://tools.ietf.org/html/rfc2710#section-3.6
+func (m *MLDv1MulticastListenerQueryMessage) IsSpecificQuery() bool {
+	return !m.IsGeneralQuery()
+}
+
+// MLDv1MulticastListenerReportMessage is sent by a client listening on
+// a specific multicast address to indicate that it is (still) listening
+// on the specific multicast address.
+// https://tools.ietf.org/html/rfc2710 Page 6
+type MLDv1MulticastListenerReportMessage struct {
+	MLDv1Message
+}
+
+// LayerType returns LayerTypeMLDv1MulticastListenerReport.
+func (*MLDv1MulticastListenerReportMessage) LayerType() gopacket.LayerType {
+	return LayerTypeMLDv1MulticastListenerReport
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (*MLDv1MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
+	return LayerTypeMLDv1MulticastListenerReport
+}
+
+// MLDv1MulticastListenerDoneMessage should be sent by a client when it ceases
+// to listen to a multicast address on an interface.
+// https://tools.ietf.org/html/rfc2710 Page 7
+type MLDv1MulticastListenerDoneMessage struct {
+	MLDv1Message
+}
+
+// LayerType returns LayerTypeMLDv1MulticastListenerDone.
+func (*MLDv1MulticastListenerDoneMessage) LayerType() gopacket.LayerType {
+	return LayerTypeMLDv1MulticastListenerDone
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (*MLDv1MulticastListenerDoneMessage) CanDecode() gopacket.LayerClass {
+	return LayerTypeMLDv1MulticastListenerDone
+}
+
+func decodeMLDv1MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
+	m := &MLDv1MulticastListenerReportMessage{}
+	return decodingLayerDecoder(m, data, p)
+}
+
+func decodeMLDv1MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
+	m := &MLDv1MulticastListenerQueryMessage{}
+	return decodingLayerDecoder(m, data, p)
+}
+
+func decodeMLDv1MulticastListenerDone(data []byte, p gopacket.PacketBuilder) error {
+	m := &MLDv1MulticastListenerDoneMessage{}
+	return decodingLayerDecoder(m, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/mldv2.go b/go-controller/vendor/github.com/google/gopacket/layers/mldv2.go
new file mode 100644
index 00000000000..05100a52d14
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/mldv2.go
@@ -0,0 +1,619 @@
+// Copyright 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"
+	"math"
+	"net"
+	"time"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// S Flag bit is 1
+	mldv2STrue uint8 = 0x8
+
+	// S Flag value mask
+	//   mldv2STrue & mldv2SMask == mldv2STrue  // true
+	//          0x1 & mldv2SMask == mldv2STrue  // true
+	//          0x0 & mldv2SMask == mldv2STrue  // false
+	mldv2SMask uint8 = 0x8
+
+	// QRV value mask
+	mldv2QRVMask uint8 = 0x7
+)
+
+// MLDv2MulticastListenerQueryMessage are sent by multicast routers to query the
+// multicast listening state of neighboring interfaces.
+// https://tools.ietf.org/html/rfc3810#section-5.1
+//
+// Some information, like Maximum Response Code and Multicast Address are in the
+// previous layer LayerTypeMLDv1MulticastListenerQuery
+type MLDv2MulticastListenerQueryMessage struct {
+	BaseLayer
+	// 5.1.3. Maximum Response Delay COde
+	MaximumResponseCode uint16
+	// 5.1.5. Multicast Address
+	// Zero in general query
+	// Specific IPv6 multicast address otherwise
+	MulticastAddress net.IP
+	// 5.1.7. S Flag (Suppress Router-Side Processing)
+	SuppressRoutersideProcessing bool
+	// 5.1.8. QRV (Querier's Robustness Variable)
+	QueriersRobustnessVariable uint8
+	// 5.1.9. QQIC (Querier's Query Interval Code)
+	QueriersQueryIntervalCode uint8
+	// 5.1.10. Number of Sources (N)
+	NumberOfSources uint16
+	// 5.1.11 Source Address [i]
+	SourceAddresses []net.IP
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (m *MLDv2MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 24 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less than 24 bytes for Multicast Listener Query Message V2")
+	}
+
+	m.MaximumResponseCode = binary.BigEndian.Uint16(data[0:2])
+	// ignore data[2:4] as per https://tools.ietf.org/html/rfc3810#section-5.1.4
+	m.MulticastAddress = data[4:20]
+	m.SuppressRoutersideProcessing = (data[20] & mldv2SMask) == mldv2STrue
+	m.QueriersRobustnessVariable = data[20] & mldv2QRVMask
+	m.QueriersQueryIntervalCode = data[21]
+
+	m.NumberOfSources = binary.BigEndian.Uint16(data[22:24])
+
+	var end int
+	for i := uint16(0); i < m.NumberOfSources; i++ {
+		begin := 24 + (int(i) * 16)
+		end = begin + 16
+
+		if end > len(data) {
+			df.SetTruncated()
+			return fmt.Errorf("ICMP layer less than %d bytes for Multicast Listener Query Message V2", end)
+		}
+
+		m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
+	}
+
+	return nil
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (*MLDv2MulticastListenerQueryMessage) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (m *MLDv2MulticastListenerQueryMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := m.serializeSourceAddressesTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(24)
+	if err != nil {
+		return err
+	}
+
+	binary.BigEndian.PutUint16(buf[0:2], m.MaximumResponseCode)
+	copy(buf[2:4], []byte{0x00, 0x00}) // set reserved bytes to zero
+
+	ma16 := m.MulticastAddress.To16()
+	if ma16 == nil {
+		return fmt.Errorf("invalid MulticastAddress '%s'", m.MulticastAddress)
+	}
+	copy(buf[4:20], ma16)
+
+	byte20 := m.QueriersRobustnessVariable & mldv2QRVMask
+	if m.SuppressRoutersideProcessing {
+		byte20 |= mldv2STrue
+	} else {
+		byte20 &= ^mldv2STrue // the complement of mldv2STrue
+	}
+	byte20 &= 0x0F // set reserved bits to zero
+	buf[20] = byte20
+
+	binary.BigEndian.PutUint16(buf[22:24], m.NumberOfSources)
+	buf[21] = m.QueriersQueryIntervalCode
+
+	return nil
+}
+
+// writes each source address to the buffer preserving the order
+func (m *MLDv2MulticastListenerQueryMessage) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	numberOfSourceAddresses := len(m.SourceAddresses)
+	if numberOfSourceAddresses > math.MaxUint16 {
+		return fmt.Errorf(
+			"there are more than %d source addresses, but 65535 is the maximum number of supported addresses",
+			numberOfSourceAddresses)
+	}
+
+	if opts.FixLengths {
+		m.NumberOfSources = uint16(numberOfSourceAddresses)
+	}
+
+	lastSAIdx := numberOfSourceAddresses - 1
+	for k := range m.SourceAddresses {
+		i := lastSAIdx - k // reverse order
+
+		buf, err := b.PrependBytes(16)
+		if err != nil {
+			return err
+		}
+
+		sa16 := m.SourceAddresses[i].To16()
+		if sa16 == nil {
+			return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
+		}
+		copy(buf[0:16], sa16)
+	}
+
+	return nil
+}
+
+// String sums this layer up nicely formatted
+func (m *MLDv2MulticastListenerQueryMessage) String() string {
+	return fmt.Sprintf(
+		"Maximum Response Code: %#x (%dms), Multicast Address: %s, Suppress Routerside Processing: %t, QRV: %#x, QQIC: %#x (%ds), Number of Source Address: %d (actual: %d), Source Addresses: %s",
+		m.MaximumResponseCode,
+		m.MaximumResponseDelay(),
+		m.MulticastAddress,
+		m.SuppressRoutersideProcessing,
+		m.QueriersRobustnessVariable,
+		m.QueriersQueryIntervalCode,
+		m.QQI()/time.Second,
+		m.NumberOfSources,
+		len(m.SourceAddresses),
+		m.SourceAddresses)
+}
+
+// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
+func (*MLDv2MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
+	return LayerTypeMLDv2MulticastListenerQuery
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (*MLDv2MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
+	return LayerTypeMLDv2MulticastListenerQuery
+}
+
+// QQI calculates the Querier's Query Interval based on the QQIC
+// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
+func (m *MLDv2MulticastListenerQueryMessage) QQI() time.Duration {
+	data := m.QueriersQueryIntervalCode
+	if data < 128 {
+		return time.Second * time.Duration(data)
+	}
+
+	exp := uint16(data) & 0x70 >> 4
+	mant := uint16(data) & 0x0F
+	return time.Second * time.Duration(mant|0x1000<<(exp+3))
+}
+
+// SetQQI calculates and updates the Querier's Query Interval Code (QQIC)
+// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
+func (m *MLDv2MulticastListenerQueryMessage) SetQQI(d time.Duration) error {
+	if d < 0 {
+		m.QueriersQueryIntervalCode = 0
+		return errors.New("QQI duration is negative")
+	}
+
+	if d == 0 {
+		m.QueriersQueryIntervalCode = 0
+		return nil
+	}
+
+	dms := d / time.Second
+	if dms < 128 {
+		m.QueriersQueryIntervalCode = uint8(dms)
+	}
+
+	if dms > 31744 { // mant=0xF, exp=0x7
+		m.QueriersQueryIntervalCode = 0xFF
+		return fmt.Errorf("QQI duration %ds is, maximum allowed is 31744s", dms)
+	}
+
+	value := uint16(dms) // ok, because 31744 < math.MaxUint16
+	exp := uint8(7)
+	for mask := uint16(0x4000); exp > 0; exp-- {
+		if mask&value != 0 {
+			break
+		}
+
+		mask >>= 1
+	}
+
+	mant := uint8(0x000F & (value >> (exp + 3)))
+	sig := uint8(0x10)
+	m.QueriersQueryIntervalCode = sig | exp<<4 | mant
+
+	return nil
+}
+
+// MaximumResponseDelay returns the Maximum Response Delay based on the
+// Maximum Response Code according to
+// https://tools.ietf.org/html/rfc3810#section-5.1.3
+func (m *MLDv2MulticastListenerQueryMessage) MaximumResponseDelay() time.Duration {
+	if m.MaximumResponseCode < 0x8000 {
+		return time.Duration(m.MaximumResponseCode)
+	}
+
+	exp := m.MaximumResponseCode & 0x7000 >> 12
+	mant := m.MaximumResponseCode & 0x0FFF
+
+	return time.Millisecond * time.Duration(mant|0x1000<<(exp+3))
+}
+
+// SetMLDv2MaximumResponseDelay updates the Maximum Response Code according to
+// https://tools.ietf.org/html/rfc3810#section-5.1.3
+func (m *MLDv2MulticastListenerQueryMessage) SetMLDv2MaximumResponseDelay(d time.Duration) error {
+	if d == 0 {
+		m.MaximumResponseCode = 0
+		return nil
+	}
+
+	if d < 0 {
+		return errors.New("maximum response delay must not be negative")
+	}
+
+	dms := d / time.Millisecond
+
+	if dms < 32768 {
+		m.MaximumResponseCode = uint16(dms)
+	}
+
+	if dms > 4193280 { // mant=0xFFF, exp=0x7
+		return fmt.Errorf("maximum response delay %dms is bigger the than maximum of 4193280ms", dms)
+	}
+
+	value := uint32(dms) // ok, because 4193280 < math.MaxUint32
+	exp := uint8(7)
+	for mask := uint32(0x40000000); exp > 0; exp-- {
+		if mask&value != 0 {
+			break
+		}
+
+		mask >>= 1
+	}
+
+	mant := uint16(0x00000FFF & (value >> (exp + 3)))
+	sig := uint16(0x1000)
+	m.MaximumResponseCode = sig | uint16(exp)<<12 | mant
+	return nil
+}
+
+// MLDv2MulticastListenerReportMessage is sent by an IP node to report the
+// current multicast listening state, or changes therein.
+// https://tools.ietf.org/html/rfc3810#section-5.2
+type MLDv2MulticastListenerReportMessage struct {
+	BaseLayer
+	// 5.2.3. Nr of Mcast Address Records
+	NumberOfMulticastAddressRecords uint16
+	// 5.2.4. Multicast Address Record [i]
+	MulticastAddressRecords []MLDv2MulticastAddressRecord
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (m *MLDv2MulticastListenerReportMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return errors.New("ICMP layer less than 4 bytes for Multicast Listener Report Message V2")
+	}
+
+	// ignore data[0:2] as per RFC
+	// https://tools.ietf.org/html/rfc3810#section-5.2.1
+	m.NumberOfMulticastAddressRecords = binary.BigEndian.Uint16(data[2:4])
+
+	begin := 4
+	for i := uint16(0); i < m.NumberOfMulticastAddressRecords; i++ {
+		mar := MLDv2MulticastAddressRecord{}
+		read, err := mar.decode(data[begin:], df)
+		if err != nil {
+			return err
+		}
+
+		m.MulticastAddressRecords = append(m.MulticastAddressRecords, mar)
+
+		begin += read
+	}
+
+	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 (m *MLDv2MulticastListenerReportMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	lastItemIdx := len(m.MulticastAddressRecords) - 1
+	for k := range m.MulticastAddressRecords {
+		i := lastItemIdx - k // reverse order
+
+		err := m.MulticastAddressRecords[i].serializeTo(b, opts)
+		if err != nil {
+			return err
+		}
+	}
+
+	if opts.FixLengths {
+		numberOfMAR := len(m.MulticastAddressRecords)
+		if numberOfMAR > math.MaxUint16 {
+			return fmt.Errorf(
+				"%d multicast address records added, but the maximum is 65535",
+				numberOfMAR)
+		}
+
+		m.NumberOfMulticastAddressRecords = uint16(numberOfMAR)
+	}
+
+	buf, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+
+	copy(buf[0:2], []byte{0x0, 0x0})
+	binary.BigEndian.PutUint16(buf[2:4], m.NumberOfMulticastAddressRecords)
+	return nil
+}
+
+// Sums this layer up nicely formatted
+func (m *MLDv2MulticastListenerReportMessage) String() string {
+	return fmt.Sprintf(
+		"Number of Mcast Addr Records: %d (actual %d), Multicast Address Records: %+v",
+		m.NumberOfMulticastAddressRecords,
+		len(m.MulticastAddressRecords),
+		m.MulticastAddressRecords)
+}
+
+// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
+func (*MLDv2MulticastListenerReportMessage) LayerType() gopacket.LayerType {
+	return LayerTypeMLDv2MulticastListenerReport
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (*MLDv2MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
+	return LayerTypeMLDv2MulticastListenerReport
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (*MLDv2MulticastListenerReportMessage) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// MLDv2MulticastAddressRecordType holds the type of a
+// Multicast Address Record, according to
+// https://tools.ietf.org/html/rfc3810#section-5.2.5 and
+// https://tools.ietf.org/html/rfc3810#section-5.2.12
+type MLDv2MulticastAddressRecordType uint8
+
+const (
+	// MLDv2MulticastAddressRecordTypeModeIsIncluded stands for
+	// MODE_IS_INCLUDE - indicates that the interface has a filter
+	// mode of INCLUDE for the specified multicast address.
+	MLDv2MulticastAddressRecordTypeModeIsIncluded MLDv2MulticastAddressRecordType = 1
+	// MLDv2MulticastAddressRecordTypeModeIsExcluded stands for
+	// MODE_IS_EXCLUDE - indicates that the interface has a filter
+	// mode of EXCLUDE for the specified multicast address.
+	MLDv2MulticastAddressRecordTypeModeIsExcluded MLDv2MulticastAddressRecordType = 2
+	// MLDv2MulticastAddressRecordTypeChangeToIncludeMode stands for
+	// CHANGE_TO_INCLUDE_MODE - indicates that the interface has
+	// changed to INCLUDE filter mode for the specified multicast
+	// address.
+	MLDv2MulticastAddressRecordTypeChangeToIncludeMode MLDv2MulticastAddressRecordType = 3
+	// MLDv2MulticastAddressRecordTypeChangeToExcludeMode stands for
+	// CHANGE_TO_EXCLUDE_MODE - indicates that the interface has
+	// changed to EXCLUDE filter mode for the specified multicast
+	// address
+	MLDv2MulticastAddressRecordTypeChangeToExcludeMode MLDv2MulticastAddressRecordType = 4
+	// MLDv2MulticastAddressRecordTypeAllowNewSources stands for
+	// ALLOW_NEW_SOURCES - indicates that the Source Address [i]
+	// fields in this Multicast Address Record contain a list of
+	// the additional sources that the node wishes to listen to,
+	// for packets sent to the specified multicast address.
+	MLDv2MulticastAddressRecordTypeAllowNewSources MLDv2MulticastAddressRecordType = 5
+	// MLDv2MulticastAddressRecordTypeBlockOldSources stands for
+	// BLOCK_OLD_SOURCES - indicates that the Source Address [i]
+	// fields in this Multicast Address Record contain a list of
+	// the sources that the node no longer wishes to listen to,
+	// for packets sent to the specified multicast address.
+	MLDv2MulticastAddressRecordTypeBlockOldSources MLDv2MulticastAddressRecordType = 6
+)
+
+// Human readable record types
+// Naming follows https://tools.ietf.org/html/rfc3810#section-5.2.12
+func (m MLDv2MulticastAddressRecordType) String() string {
+	switch m {
+	case MLDv2MulticastAddressRecordTypeModeIsIncluded:
+		return "MODE_IS_INCLUDE"
+	case MLDv2MulticastAddressRecordTypeModeIsExcluded:
+		return "MODE_IS_EXCLUDE"
+	case MLDv2MulticastAddressRecordTypeChangeToIncludeMode:
+		return "CHANGE_TO_INCLUDE_MODE"
+	case MLDv2MulticastAddressRecordTypeChangeToExcludeMode:
+		return "CHANGE_TO_EXCLUDE_MODE"
+	case MLDv2MulticastAddressRecordTypeAllowNewSources:
+		return "ALLOW_NEW_SOURCES"
+	case MLDv2MulticastAddressRecordTypeBlockOldSources:
+		return "BLOCK_OLD_SOURCES"
+	default:
+		return fmt.Sprintf("UNKNOWN(%d)", m)
+	}
+}
+
+// MLDv2MulticastAddressRecord contains information on the sender listening to a
+// single multicast address on the interface the report is sent.
+// https://tools.ietf.org/html/rfc3810#section-5.2.4
+type MLDv2MulticastAddressRecord struct {
+	// 5.2.5. Record Type
+	RecordType MLDv2MulticastAddressRecordType
+	// 5.2.6. Auxiliary Data Length (number of 32-bit words)
+	AuxDataLen uint8
+	// 5.2.7. Number Of Sources (N)
+	N uint16
+	// 5.2.8. Multicast Address
+	MulticastAddress net.IP
+	// 5.2.9 Source Address [i]
+	SourceAddresses []net.IP
+	// 5.2.10 Auxiliary Data
+	AuxiliaryData []byte
+}
+
+// decodes a multicast address record from bytes
+func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return 0, errors.New(
+			"Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
+	}
+
+	m.RecordType = MLDv2MulticastAddressRecordType(data[0])
+	m.AuxDataLen = data[1]
+	m.N = binary.BigEndian.Uint16(data[2:4])
+	m.MulticastAddress = data[4:20]
+
+	for i := uint16(0); i < m.N; i++ {
+		begin := 20 + (int(i) * 16)
+		end := begin + 16
+
+		if len(data) < end {
+			df.SetTruncated()
+			return begin, fmt.Errorf(
+				"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", end)
+		}
+
+		m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
+	}
+
+	expectedLengthWithouAuxData := 20 + (int(m.N) * 16)
+	expectedTotalLength := (int(m.AuxDataLen) * 4) + expectedLengthWithouAuxData // *4 because AuxDataLen are 32bit words
+	if len(data) < expectedTotalLength {
+		return expectedLengthWithouAuxData, fmt.Errorf(
+			"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record",
+			expectedLengthWithouAuxData)
+	}
+
+	m.AuxiliaryData = data[expectedLengthWithouAuxData:expectedTotalLength]
+
+	return expectedTotalLength, nil
+}
+
+// String sums this layer up nicely formatted
+func (m *MLDv2MulticastAddressRecord) String() string {
+	return fmt.Sprintf(
+		"RecordType: %d (%s), AuxDataLen: %d [32-bit words], N: %d, Multicast Address: %s, SourceAddresses: %s, Auxiliary Data: %#x",
+		m.RecordType,
+		m.RecordType.String(),
+		m.AuxDataLen,
+		m.N,
+		m.MulticastAddress.To16(),
+		m.SourceAddresses,
+		m.AuxiliaryData)
+}
+
+// serializes a multicast address record
+func (m *MLDv2MulticastAddressRecord) serializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if err := m.serializeAuxiliaryDataTo(b, opts); err != nil {
+		return err
+	}
+
+	if err := m.serializeSourceAddressesTo(b, opts); err != nil {
+		return err
+	}
+
+	buf, err := b.PrependBytes(20)
+	if err != nil {
+		return err
+	}
+
+	buf[0] = uint8(m.RecordType)
+	buf[1] = m.AuxDataLen
+	binary.BigEndian.PutUint16(buf[2:4], m.N)
+
+	ma16 := m.MulticastAddress.To16()
+	if ma16 == nil {
+		return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
+	}
+	copy(buf[4:20], ma16)
+
+	return nil
+}
+
+// serializes the auxiliary data of a multicast address record
+func (m *MLDv2MulticastAddressRecord) serializeAuxiliaryDataTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if remainder := len(m.AuxiliaryData) % 4; remainder != 0 {
+		zeroWord := []byte{0x0, 0x0, 0x0, 0x0}
+		m.AuxiliaryData = append(m.AuxiliaryData, zeroWord[:remainder]...)
+	}
+
+	if opts.FixLengths {
+		auxDataLen := len(m.AuxiliaryData) / 4
+
+		if auxDataLen > math.MaxUint8 {
+			return fmt.Errorf("auxilary data is %d 32-bit words, but the maximum is 255 32-bit words", auxDataLen)
+		}
+
+		m.AuxDataLen = uint8(auxDataLen)
+	}
+
+	buf, err := b.PrependBytes(len(m.AuxiliaryData))
+	if err != nil {
+		return err
+	}
+
+	copy(buf, m.AuxiliaryData)
+	return nil
+}
+
+// serializes the source addresses of a multicast address record preserving the order
+func (m *MLDv2MulticastAddressRecord) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if opts.FixLengths {
+		numberOfSourceAddresses := len(m.SourceAddresses)
+
+		if numberOfSourceAddresses > math.MaxUint16 {
+			return fmt.Errorf(
+				"%d source addresses added, but the maximum is 65535",
+				numberOfSourceAddresses)
+		}
+
+		m.N = uint16(numberOfSourceAddresses)
+	}
+
+	lastItemIdx := len(m.SourceAddresses) - 1
+	for k := range m.SourceAddresses {
+		i := lastItemIdx - k // reverse order
+
+		buf, err := b.PrependBytes(16)
+		if err != nil {
+			return err
+		}
+
+		sa16 := m.SourceAddresses[i].To16()
+		if sa16 == nil {
+			return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
+		}
+		copy(buf, sa16)
+	}
+
+	return nil
+}
+
+func decodeMLDv2MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
+	m := &MLDv2MulticastListenerReportMessage{}
+	return decodingLayerDecoder(m, data, p)
+}
+
+func decodeMLDv2MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
+	m := &MLDv2MulticastListenerQueryMessage{}
+	return decodingLayerDecoder(m, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/modbustcp.go b/go-controller/vendor/github.com/google/gopacket/layers/modbustcp.go
new file mode 100644
index 00000000000..bafbd7436cd
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/modbustcp.go
@@ -0,0 +1,150 @@
+// 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 (
+	"encoding/binary"
+	"errors"
+	"github.com/google/gopacket"
+)
+
+//******************************************************************************
+//
+// ModbusTCP Decoding Layer
+// ------------------------------------------
+// This file provides a GoPacket decoding layer for ModbusTCP.
+//
+//******************************************************************************
+
+const mbapRecordSizeInBytes int = 7
+const modbusPDUMinimumRecordSizeInBytes int = 2
+const modbusPDUMaximumRecordSizeInBytes int = 253
+
+// ModbusProtocol type
+type ModbusProtocol uint16
+
+// ModbusProtocol known values.
+const (
+	ModbusProtocolModbus ModbusProtocol = 0
+)
+
+func (mp ModbusProtocol) String() string {
+	switch mp {
+	default:
+		return "Unknown"
+	case ModbusProtocolModbus:
+		return "Modbus"
+	}
+}
+
+//******************************************************************************
+
+// ModbusTCP Type
+// --------
+// Type ModbusTCP implements the DecodingLayer interface. Each ModbusTCP object
+// represents in a structured form the MODBUS Application Protocol header (MBAP) record present as the TCP
+// payload in an ModbusTCP TCP packet.
+//
+type ModbusTCP struct {
+	BaseLayer // Stores the packet bytes and payload (Modbus PDU) bytes .
+
+	TransactionIdentifier uint16         // Identification of a MODBUS Request/Response transaction
+	ProtocolIdentifier    ModbusProtocol // It is used for intra-system multiplexing
+	Length                uint16         // Number of following bytes (includes 1 byte for UnitIdentifier + Modbus data length
+	UnitIdentifier        uint8          // Identification of a remote slave connected on a serial line or on other buses
+}
+
+//******************************************************************************
+
+// LayerType returns the layer type of the ModbusTCP object, which is LayerTypeModbusTCP.
+func (d *ModbusTCP) LayerType() gopacket.LayerType {
+	return LayerTypeModbusTCP
+}
+
+//******************************************************************************
+
+// decodeModbusTCP analyses a byte slice and attempts to decode it as an ModbusTCP
+// record of a TCP 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 ModbusTCP layer.
+func decodeModbusTCP(data []byte, p gopacket.PacketBuilder) error {
+
+	// Attempt to decode the byte slice.
+	d := &ModbusTCP{}
+	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 p.NextDecoder(d.NextLayerType())
+
+}
+
+//******************************************************************************
+
+// DecodeFromBytes analyses a byte slice and attempts to decode it as an ModbusTCP
+// record of a TCP packet.
+//
+// Upon succeeds, it loads the ModbusTCP object with information about the packet
+// and returns nil.
+// Upon failure, it returns an error (non nil).
+func (d *ModbusTCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+
+	// If the data block is too short to be a MBAP record, then return an error.
+	if len(data) < mbapRecordSizeInBytes+modbusPDUMinimumRecordSizeInBytes {
+		df.SetTruncated()
+		return errors.New("ModbusTCP packet too short")
+	}
+
+	if len(data) > mbapRecordSizeInBytes+modbusPDUMaximumRecordSizeInBytes {
+		df.SetTruncated()
+		return errors.New("ModbusTCP packet too long")
+	}
+
+	// ModbusTCP type embeds type BaseLayer which contains two fields:
+	//    Contents is supposed to contain the bytes of the data at this level (MPBA).
+	//    Payload is supposed to contain the payload of this level (PDU).
+	d.BaseLayer = BaseLayer{Contents: data[:mbapRecordSizeInBytes], Payload: data[mbapRecordSizeInBytes:len(data)]}
+
+	// Extract the fields from the block of bytes.
+	// The fields can just be copied in big endian order.
+	d.TransactionIdentifier = binary.BigEndian.Uint16(data[:2])
+	d.ProtocolIdentifier = ModbusProtocol(binary.BigEndian.Uint16(data[2:4]))
+	d.Length = binary.BigEndian.Uint16(data[4:6])
+
+	// Length should have the size of the payload plus one byte (size of UnitIdentifier)
+	if d.Length != uint16(len(d.BaseLayer.Payload)+1) {
+		df.SetTruncated()
+		return errors.New("ModbusTCP packet with wrong field value (Length)")
+	}
+	d.UnitIdentifier = uint8(data[6])
+
+	return nil
+}
+
+//******************************************************************************
+
+// NextLayerType returns the layer type of the ModbusTCP payload, which is LayerTypePayload.
+func (d *ModbusTCP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+//******************************************************************************
+
+// Payload returns Modbus Protocol Data Unit (PDU) composed by Function Code and Data, it is carried within ModbusTCP packets
+func (d *ModbusTCP) Payload() []byte {
+	return d.BaseLayer.Payload
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/mpls.go b/go-controller/vendor/github.com/google/gopacket/layers/mpls.go
new file mode 100644
index 00000000000..83079a09b72
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/mpls.go
@@ -0,0 +1,87 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"github.com/google/gopacket"
+)
+
+// MPLS is the MPLS packet header.
+type MPLS struct {
+	BaseLayer
+	Label        uint32
+	TrafficClass uint8
+	StackBottom  bool
+	TTL          uint8
+}
+
+// LayerType returns gopacket.LayerTypeMPLS.
+func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS }
+
+// ProtocolGuessingDecoder attempts to guess the protocol of the bytes it's
+// given, then decode the packet accordingly.  Its algorithm for guessing is:
+//  If the packet starts with byte 0x45-0x4F: IPv4
+//  If the packet starts with byte 0x60-0x6F: IPv6
+//  Otherwise:  Error
+// See draft-hsmit-isis-aal5mux-00.txt for more detail on this approach.
+type ProtocolGuessingDecoder struct{}
+
+func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error {
+	switch data[0] {
+	// 0x40 | header_len, where header_len is at least 5.
+	case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f:
+		return decodeIPv4(data, p)
+		// IPv6 can start with any byte whose first 4 bits are 0x6.
+	case 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f:
+		return decodeIPv6(data, p)
+	}
+	return errors.New("Unable to guess protocol of packet data")
+}
+
+// MPLSPayloadDecoder is the decoder used to data encapsulated by each MPLS
+// layer.  MPLS contains no type information, so we have to explicitly decide
+// which decoder to use.  This is initially set to ProtocolGuessingDecoder, our
+// simple attempt at guessing protocols based on the first few bytes of data
+// available to us.  However, if you know that in your environment MPLS always
+// encapsulates a specific protocol, you may reset this.
+var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{}
+
+func decodeMPLS(data []byte, p gopacket.PacketBuilder) error {
+	decoded := binary.BigEndian.Uint32(data[:4])
+	mpls := &MPLS{
+		Label:        decoded >> 12,
+		TrafficClass: uint8(decoded>>9) & 0x7,
+		StackBottom:  decoded&0x100 != 0,
+		TTL:          uint8(decoded),
+		BaseLayer:    BaseLayer{data[:4], data[4:]},
+	}
+	p.AddLayer(mpls)
+	if mpls.StackBottom {
+		return p.NextDecoder(MPLSPayloadDecoder)
+	}
+	return p.NextDecoder(gopacket.DecodeFunc(decodeMPLS))
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (m *MPLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	encoded := m.Label << 12
+	encoded |= uint32(m.TrafficClass) << 9
+	encoded |= uint32(m.TTL)
+	if m.StackBottom {
+		encoded |= 0x100
+	}
+	binary.BigEndian.PutUint32(bytes, encoded)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ndp.go b/go-controller/vendor/github.com/google/gopacket/layers/ndp.go
new file mode 100644
index 00000000000..f7ca1b26b78
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ndp.go
@@ -0,0 +1,611 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// Enum types courtesy of...
+// http://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-ndp.c
+
+package layers
+
+import (
+	"fmt"
+	"github.com/google/gopacket"
+	"net"
+)
+
+type NDPChassisType uint8
+
+// Nortel Chassis Types
+const (
+	NDPChassisother                                       NDPChassisType = 1
+	NDPChassis3000                                        NDPChassisType = 2
+	NDPChassis3030                                        NDPChassisType = 3
+	NDPChassis2310                                        NDPChassisType = 4
+	NDPChassis2810                                        NDPChassisType = 5
+	NDPChassis2912                                        NDPChassisType = 6
+	NDPChassis2914                                        NDPChassisType = 7
+	NDPChassis271x                                        NDPChassisType = 8
+	NDPChassis2813                                        NDPChassisType = 9
+	NDPChassis2814                                        NDPChassisType = 10
+	NDPChassis2915                                        NDPChassisType = 11
+	NDPChassis5000                                        NDPChassisType = 12
+	NDPChassis2813SA                                      NDPChassisType = 13
+	NDPChassis2814SA                                      NDPChassisType = 14
+	NDPChassis810M                                        NDPChassisType = 15
+	NDPChassisEthercell                                   NDPChassisType = 16
+	NDPChassis5005                                        NDPChassisType = 17
+	NDPChassisAlcatelEWC                                  NDPChassisType = 18
+	NDPChassis2715SA                                      NDPChassisType = 20
+	NDPChassis2486                                        NDPChassisType = 21
+	NDPChassis28000series                                 NDPChassisType = 22
+	NDPChassis23000series                                 NDPChassisType = 23
+	NDPChassis5DN00xseries                                NDPChassisType = 24
+	NDPChassisBayStackEthernet                            NDPChassisType = 25
+	NDPChassis23100series                                 NDPChassisType = 26
+	NDPChassis100BaseTHub                                 NDPChassisType = 27
+	NDPChassis3000FastEthernet                            NDPChassisType = 28
+	NDPChassisOrionSwitch                                 NDPChassisType = 29
+	NDPChassisDDS                                         NDPChassisType = 31
+	NDPChassisCentillion6slot                             NDPChassisType = 32
+	NDPChassisCentillion12slot                            NDPChassisType = 33
+	NDPChassisCentillion1slot                             NDPChassisType = 34
+	NDPChassisBayStack301                                 NDPChassisType = 35
+	NDPChassisBayStackTokenRingHub                        NDPChassisType = 36
+	NDPChassisFVCMultimediaSwitch                         NDPChassisType = 37
+	NDPChassisSwitchNode                                  NDPChassisType = 38
+	NDPChassisBayStack302Switch                           NDPChassisType = 39
+	NDPChassisBayStack350Switch                           NDPChassisType = 40
+	NDPChassisBayStack150EthernetHub                      NDPChassisType = 41
+	NDPChassisCentillion50NSwitch                         NDPChassisType = 42
+	NDPChassisCentillion50TSwitch                         NDPChassisType = 43
+	NDPChassisBayStack303304Switches                      NDPChassisType = 44
+	NDPChassisBayStack200EthernetHub                      NDPChassisType = 45
+	NDPChassisBayStack25010100EthernetHub                 NDPChassisType = 46
+	NDPChassisBayStack450101001000Switches                NDPChassisType = 48
+	NDPChassisBayStack41010100Switches                    NDPChassisType = 49
+	NDPChassisPassport1200L3Switch                        NDPChassisType = 50
+	NDPChassisPassport1250L3Switch                        NDPChassisType = 51
+	NDPChassisPassport1100L3Switch                        NDPChassisType = 52
+	NDPChassisPassport1150L3Switch                        NDPChassisType = 53
+	NDPChassisPassport1050L3Switch                        NDPChassisType = 54
+	NDPChassisPassport1051L3Switch                        NDPChassisType = 55
+	NDPChassisPassport8610L3Switch                        NDPChassisType = 56
+	NDPChassisPassport8606L3Switch                        NDPChassisType = 57
+	NDPChassisPassport8010                                NDPChassisType = 58
+	NDPChassisPassport8006                                NDPChassisType = 59
+	NDPChassisBayStack670wirelessaccesspoint              NDPChassisType = 60
+	NDPChassisPassport740                                 NDPChassisType = 61
+	NDPChassisPassport750                                 NDPChassisType = 62
+	NDPChassisPassport790                                 NDPChassisType = 63
+	NDPChassisBusinessPolicySwitch200010100Switches       NDPChassisType = 64
+	NDPChassisPassport8110L2Switch                        NDPChassisType = 65
+	NDPChassisPassport8106L2Switch                        NDPChassisType = 66
+	NDPChassisBayStack3580GigSwitch                       NDPChassisType = 67
+	NDPChassisBayStack10PowerSupplyUnit                   NDPChassisType = 68
+	NDPChassisBayStack42010100Switch                      NDPChassisType = 69
+	NDPChassisOPTeraMetro1200EthernetServiceModule        NDPChassisType = 70
+	NDPChassisOPTera8010co                                NDPChassisType = 71
+	NDPChassisOPTera8610coL3Switch                        NDPChassisType = 72
+	NDPChassisOPTera8110coL2Switch                        NDPChassisType = 73
+	NDPChassisOPTera8003                                  NDPChassisType = 74
+	NDPChassisOPTera8603L3Switch                          NDPChassisType = 75
+	NDPChassisOPTera8103L2Switch                          NDPChassisType = 76
+	NDPChassisBayStack380101001000Switch                  NDPChassisType = 77
+	NDPChassisEthernetSwitch47048T                        NDPChassisType = 78
+	NDPChassisOPTeraMetro1450EthernetServiceModule        NDPChassisType = 79
+	NDPChassisOPTeraMetro1400EthernetServiceModule        NDPChassisType = 80
+	NDPChassisAlteonSwitchFamily                          NDPChassisType = 81
+	NDPChassisEthernetSwitch46024TPWR                     NDPChassisType = 82
+	NDPChassisOPTeraMetro8010OPML2Switch                  NDPChassisType = 83
+	NDPChassisOPTeraMetro8010coOPML2Switch                NDPChassisType = 84
+	NDPChassisOPTeraMetro8006OPML2Switch                  NDPChassisType = 85
+	NDPChassisOPTeraMetro8003OPML2Switch                  NDPChassisType = 86
+	NDPChassisAlteon180e                                  NDPChassisType = 87
+	NDPChassisAlteonAD3                                   NDPChassisType = 88
+	NDPChassisAlteon184                                   NDPChassisType = 89
+	NDPChassisAlteonAD4                                   NDPChassisType = 90
+	NDPChassisPassport1424L3Switch                        NDPChassisType = 91
+	NDPChassisPassport1648L3Switch                        NDPChassisType = 92
+	NDPChassisPassport1612L3Switch                        NDPChassisType = 93
+	NDPChassisPassport1624L3Switch                        NDPChassisType = 94
+	NDPChassisBayStack38024FFiber1000Switch               NDPChassisType = 95
+	NDPChassisEthernetRoutingSwitch551024T                NDPChassisType = 96
+	NDPChassisEthernetRoutingSwitch551048T                NDPChassisType = 97
+	NDPChassisEthernetSwitch47024T                        NDPChassisType = 98
+	NDPChassisNortelNetworksWirelessLANAccessPoint2220    NDPChassisType = 99
+	NDPChassisPassportRBS2402L3Switch                     NDPChassisType = 100
+	NDPChassisAlteonApplicationSwitch2424                 NDPChassisType = 101
+	NDPChassisAlteonApplicationSwitch2224                 NDPChassisType = 102
+	NDPChassisAlteonApplicationSwitch2208                 NDPChassisType = 103
+	NDPChassisAlteonApplicationSwitch2216                 NDPChassisType = 104
+	NDPChassisAlteonApplicationSwitch3408                 NDPChassisType = 105
+	NDPChassisAlteonApplicationSwitch3416                 NDPChassisType = 106
+	NDPChassisNortelNetworksWirelessLANSecuritySwitch2250 NDPChassisType = 107
+	NDPChassisEthernetSwitch42548T                        NDPChassisType = 108
+	NDPChassisEthernetSwitch42524T                        NDPChassisType = 109
+	NDPChassisNortelNetworksWirelessLANAccessPoint2221    NDPChassisType = 110
+	NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch  NDPChassisType = 111
+	NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch NDPChassisType = 112
+	NDPChassisPassport830010slotchassis                   NDPChassisType = 113
+	NDPChassisPassport83006slotchassis                    NDPChassisType = 114
+	NDPChassisEthernetRoutingSwitch552024TPWR             NDPChassisType = 115
+	NDPChassisEthernetRoutingSwitch552048TPWR             NDPChassisType = 116
+	NDPChassisNortelNetworksVPNGateway3050                NDPChassisType = 117
+	NDPChassisAlteonSSL31010100                           NDPChassisType = 118
+	NDPChassisAlteonSSL31010100Fiber                      NDPChassisType = 119
+	NDPChassisAlteonSSL31010100FIPS                       NDPChassisType = 120
+	NDPChassisAlteonSSL410101001000                       NDPChassisType = 121
+	NDPChassisAlteonSSL410101001000Fiber                  NDPChassisType = 122
+	NDPChassisAlteonApplicationSwitch2424SSL              NDPChassisType = 123
+	NDPChassisEthernetSwitch32524T                        NDPChassisType = 124
+	NDPChassisEthernetSwitch32524G                        NDPChassisType = 125
+	NDPChassisNortelNetworksWirelessLANAccessPoint2225    NDPChassisType = 126
+	NDPChassisNortelNetworksWirelessLANSecuritySwitch2270 NDPChassisType = 127
+	NDPChassis24portEthernetSwitch47024TPWR               NDPChassisType = 128
+	NDPChassis48portEthernetSwitch47048TPWR               NDPChassisType = 129
+	NDPChassisEthernetRoutingSwitch553024TFD              NDPChassisType = 130
+	NDPChassisEthernetSwitch351024T                       NDPChassisType = 131
+	NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch NDPChassisType = 132
+	NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch NDPChassisType = 133
+	NDPChassisNortelSecureAccessSwitch                    NDPChassisType = 134
+	NDPChassisNortelNetworksVPNGateway3070                NDPChassisType = 135
+	NDPChassisOPTeraMetro3500                             NDPChassisType = 136
+	NDPChassisSMBBES101024T                               NDPChassisType = 137
+	NDPChassisSMBBES101048T                               NDPChassisType = 138
+	NDPChassisSMBBES102024TPWR                            NDPChassisType = 139
+	NDPChassisSMBBES102048TPWR                            NDPChassisType = 140
+	NDPChassisSMBBES201024T                               NDPChassisType = 141
+	NDPChassisSMBBES201048T                               NDPChassisType = 142
+	NDPChassisSMBBES202024TPWR                            NDPChassisType = 143
+	NDPChassisSMBBES202048TPWR                            NDPChassisType = 144
+	NDPChassisSMBBES11024T                                NDPChassisType = 145
+	NDPChassisSMBBES11048T                                NDPChassisType = 146
+	NDPChassisSMBBES12024TPWR                             NDPChassisType = 147
+	NDPChassisSMBBES12048TPWR                             NDPChassisType = 148
+	NDPChassisSMBBES21024T                                NDPChassisType = 149
+	NDPChassisSMBBES21048T                                NDPChassisType = 150
+	NDPChassisSMBBES22024TPWR                             NDPChassisType = 151
+	NDPChassisSMBBES22048TPWR                             NDPChassisType = 152
+	NDPChassisOME6500                                     NDPChassisType = 153
+	NDPChassisEthernetRoutingSwitch4548GT                 NDPChassisType = 154
+	NDPChassisEthernetRoutingSwitch4548GTPWR              NDPChassisType = 155
+	NDPChassisEthernetRoutingSwitch4550T                  NDPChassisType = 156
+	NDPChassisEthernetRoutingSwitch4550TPWR               NDPChassisType = 157
+	NDPChassisEthernetRoutingSwitch4526FX                 NDPChassisType = 158
+	NDPChassisEthernetRoutingSwitch250026T                NDPChassisType = 159
+	NDPChassisEthernetRoutingSwitch250026TPWR             NDPChassisType = 160
+	NDPChassisEthernetRoutingSwitch250050T                NDPChassisType = 161
+	NDPChassisEthernetRoutingSwitch250050TPWR             NDPChassisType = 162
+)
+
+type NDPBackplaneType uint8
+
+// Nortel Backplane Types
+const (
+	NDPBackplaneOther                                       NDPBackplaneType = 1
+	NDPBackplaneEthernet                                    NDPBackplaneType = 2
+	NDPBackplaneEthernetTokenring                           NDPBackplaneType = 3
+	NDPBackplaneEthernetFDDI                                NDPBackplaneType = 4
+	NDPBackplaneEthernetTokenringFDDI                       NDPBackplaneType = 5
+	NDPBackplaneEthernetTokenringRedundantPower             NDPBackplaneType = 6
+	NDPBackplaneEthernetTokenringFDDIRedundantPower         NDPBackplaneType = 7
+	NDPBackplaneTokenRing                                   NDPBackplaneType = 8
+	NDPBackplaneEthernetTokenringFastEthernet               NDPBackplaneType = 9
+	NDPBackplaneEthernetFastEthernet                        NDPBackplaneType = 10
+	NDPBackplaneEthernetTokenringFastEthernetRedundantPower NDPBackplaneType = 11
+	NDPBackplaneEthernetFastEthernetGigabitEthernet         NDPBackplaneType = 12
+)
+
+type NDPState uint8
+
+// Device State
+const (
+	NDPStateTopology  NDPState = 1
+	NDPStateHeartbeat NDPState = 2
+	NDPStateNew       NDPState = 3
+)
+
+// NortelDiscovery is a packet layer containing the Nortel Discovery Protocol.
+type NortelDiscovery struct {
+	BaseLayer
+	IPAddress net.IP
+	SegmentID []byte
+	Chassis   NDPChassisType
+	Backplane NDPBackplaneType
+	State     NDPState
+	NumLinks  uint8
+}
+
+// LayerType returns gopacket.LayerTypeNortelDiscovery.
+func (c *NortelDiscovery) LayerType() gopacket.LayerType {
+	return LayerTypeNortelDiscovery
+}
+
+func decodeNortelDiscovery(data []byte, p gopacket.PacketBuilder) error {
+	c := &NortelDiscovery{}
+	if len(data) < 11 {
+		return fmt.Errorf("Invalid NortelDiscovery packet length %d", len(data))
+	}
+	c.IPAddress = data[0:4]
+	c.SegmentID = data[4:7]
+	c.Chassis = NDPChassisType(data[7])
+	c.Backplane = NDPBackplaneType(data[8])
+	c.State = NDPState(data[9])
+	c.NumLinks = uint8(data[10])
+	p.AddLayer(c)
+	return nil
+}
+
+func (t NDPChassisType) String() (s string) {
+	switch t {
+	case NDPChassisother:
+		s = "other"
+	case NDPChassis3000:
+		s = "3000"
+	case NDPChassis3030:
+		s = "3030"
+	case NDPChassis2310:
+		s = "2310"
+	case NDPChassis2810:
+		s = "2810"
+	case NDPChassis2912:
+		s = "2912"
+	case NDPChassis2914:
+		s = "2914"
+	case NDPChassis271x:
+		s = "271x"
+	case NDPChassis2813:
+		s = "2813"
+	case NDPChassis2814:
+		s = "2814"
+	case NDPChassis2915:
+		s = "2915"
+	case NDPChassis5000:
+		s = "5000"
+	case NDPChassis2813SA:
+		s = "2813SA"
+	case NDPChassis2814SA:
+		s = "2814SA"
+	case NDPChassis810M:
+		s = "810M"
+	case NDPChassisEthercell:
+		s = "Ethercell"
+	case NDPChassis5005:
+		s = "5005"
+	case NDPChassisAlcatelEWC:
+		s = "Alcatel Ethernet workgroup conc."
+	case NDPChassis2715SA:
+		s = "2715SA"
+	case NDPChassis2486:
+		s = "2486"
+	case NDPChassis28000series:
+		s = "28000 series"
+	case NDPChassis23000series:
+		s = "23000 series"
+	case NDPChassis5DN00xseries:
+		s = "5DN00x series"
+	case NDPChassisBayStackEthernet:
+		s = "BayStack Ethernet"
+	case NDPChassis23100series:
+		s = "23100 series"
+	case NDPChassis100BaseTHub:
+		s = "100Base-T Hub"
+	case NDPChassis3000FastEthernet:
+		s = "3000 Fast Ethernet"
+	case NDPChassisOrionSwitch:
+		s = "Orion switch"
+	case NDPChassisDDS:
+		s = "DDS"
+	case NDPChassisCentillion6slot:
+		s = "Centillion (6 slot)"
+	case NDPChassisCentillion12slot:
+		s = "Centillion (12 slot)"
+	case NDPChassisCentillion1slot:
+		s = "Centillion (1 slot)"
+	case NDPChassisBayStack301:
+		s = "BayStack 301"
+	case NDPChassisBayStackTokenRingHub:
+		s = "BayStack TokenRing Hub"
+	case NDPChassisFVCMultimediaSwitch:
+		s = "FVC Multimedia Switch"
+	case NDPChassisSwitchNode:
+		s = "Switch Node"
+	case NDPChassisBayStack302Switch:
+		s = "BayStack 302 Switch"
+	case NDPChassisBayStack350Switch:
+		s = "BayStack 350 Switch"
+	case NDPChassisBayStack150EthernetHub:
+		s = "BayStack 150 Ethernet Hub"
+	case NDPChassisCentillion50NSwitch:
+		s = "Centillion 50N switch"
+	case NDPChassisCentillion50TSwitch:
+		s = "Centillion 50T switch"
+	case NDPChassisBayStack303304Switches:
+		s = "BayStack 303 and 304 Switches"
+	case NDPChassisBayStack200EthernetHub:
+		s = "BayStack 200 Ethernet Hub"
+	case NDPChassisBayStack25010100EthernetHub:
+		s = "BayStack 250 10/100 Ethernet Hub"
+	case NDPChassisBayStack450101001000Switches:
+		s = "BayStack 450 10/100/1000 Switches"
+	case NDPChassisBayStack41010100Switches:
+		s = "BayStack 410 10/100 Switches"
+	case NDPChassisPassport1200L3Switch:
+		s = "Passport 1200 L3 Switch"
+	case NDPChassisPassport1250L3Switch:
+		s = "Passport 1250 L3 Switch"
+	case NDPChassisPassport1100L3Switch:
+		s = "Passport 1100 L3 Switch"
+	case NDPChassisPassport1150L3Switch:
+		s = "Passport 1150 L3 Switch"
+	case NDPChassisPassport1050L3Switch:
+		s = "Passport 1050 L3 Switch"
+	case NDPChassisPassport1051L3Switch:
+		s = "Passport 1051 L3 Switch"
+	case NDPChassisPassport8610L3Switch:
+		s = "Passport 8610 L3 Switch"
+	case NDPChassisPassport8606L3Switch:
+		s = "Passport 8606 L3 Switch"
+	case NDPChassisPassport8010:
+		s = "Passport 8010"
+	case NDPChassisPassport8006:
+		s = "Passport 8006"
+	case NDPChassisBayStack670wirelessaccesspoint:
+		s = "BayStack 670 wireless access point"
+	case NDPChassisPassport740:
+		s = "Passport 740"
+	case NDPChassisPassport750:
+		s = "Passport 750"
+	case NDPChassisPassport790:
+		s = "Passport 790"
+	case NDPChassisBusinessPolicySwitch200010100Switches:
+		s = "Business Policy Switch 2000 10/100 Switches"
+	case NDPChassisPassport8110L2Switch:
+		s = "Passport 8110 L2 Switch"
+	case NDPChassisPassport8106L2Switch:
+		s = "Passport 8106 L2 Switch"
+	case NDPChassisBayStack3580GigSwitch:
+		s = "BayStack 3580 Gig Switch"
+	case NDPChassisBayStack10PowerSupplyUnit:
+		s = "BayStack 10 Power Supply Unit"
+	case NDPChassisBayStack42010100Switch:
+		s = "BayStack 420 10/100 Switch"
+	case NDPChassisOPTeraMetro1200EthernetServiceModule:
+		s = "OPTera Metro 1200 Ethernet Service Module"
+	case NDPChassisOPTera8010co:
+		s = "OPTera 8010co"
+	case NDPChassisOPTera8610coL3Switch:
+		s = "OPTera 8610co L3 switch"
+	case NDPChassisOPTera8110coL2Switch:
+		s = "OPTera 8110co L2 switch"
+	case NDPChassisOPTera8003:
+		s = "OPTera 8003"
+	case NDPChassisOPTera8603L3Switch:
+		s = "OPTera 8603 L3 switch"
+	case NDPChassisOPTera8103L2Switch:
+		s = "OPTera 8103 L2 switch"
+	case NDPChassisBayStack380101001000Switch:
+		s = "BayStack 380 10/100/1000 Switch"
+	case NDPChassisEthernetSwitch47048T:
+		s = "Ethernet Switch 470-48T"
+	case NDPChassisOPTeraMetro1450EthernetServiceModule:
+		s = "OPTera Metro 1450 Ethernet Service Module"
+	case NDPChassisOPTeraMetro1400EthernetServiceModule:
+		s = "OPTera Metro 1400 Ethernet Service Module"
+	case NDPChassisAlteonSwitchFamily:
+		s = "Alteon Switch Family"
+	case NDPChassisEthernetSwitch46024TPWR:
+		s = "Ethernet Switch 460-24T-PWR"
+	case NDPChassisOPTeraMetro8010OPML2Switch:
+		s = "OPTera Metro 8010 OPM L2 Switch"
+	case NDPChassisOPTeraMetro8010coOPML2Switch:
+		s = "OPTera Metro 8010co OPM L2 Switch"
+	case NDPChassisOPTeraMetro8006OPML2Switch:
+		s = "OPTera Metro 8006 OPM L2 Switch"
+	case NDPChassisOPTeraMetro8003OPML2Switch:
+		s = "OPTera Metro 8003 OPM L2 Switch"
+	case NDPChassisAlteon180e:
+		s = "Alteon 180e"
+	case NDPChassisAlteonAD3:
+		s = "Alteon AD3"
+	case NDPChassisAlteon184:
+		s = "Alteon 184"
+	case NDPChassisAlteonAD4:
+		s = "Alteon AD4"
+	case NDPChassisPassport1424L3Switch:
+		s = "Passport 1424 L3 switch"
+	case NDPChassisPassport1648L3Switch:
+		s = "Passport 1648 L3 switch"
+	case NDPChassisPassport1612L3Switch:
+		s = "Passport 1612 L3 switch"
+	case NDPChassisPassport1624L3Switch:
+		s = "Passport 1624 L3 switch"
+	case NDPChassisBayStack38024FFiber1000Switch:
+		s = "BayStack 380-24F Fiber 1000 Switch"
+	case NDPChassisEthernetRoutingSwitch551024T:
+		s = "Ethernet Routing Switch 5510-24T"
+	case NDPChassisEthernetRoutingSwitch551048T:
+		s = "Ethernet Routing Switch 5510-48T"
+	case NDPChassisEthernetSwitch47024T:
+		s = "Ethernet Switch 470-24T"
+	case NDPChassisNortelNetworksWirelessLANAccessPoint2220:
+		s = "Nortel Networks Wireless LAN Access Point 2220"
+	case NDPChassisPassportRBS2402L3Switch:
+		s = "Passport RBS 2402 L3 switch"
+	case NDPChassisAlteonApplicationSwitch2424:
+		s = "Alteon Application Switch 2424"
+	case NDPChassisAlteonApplicationSwitch2224:
+		s = "Alteon Application Switch 2224"
+	case NDPChassisAlteonApplicationSwitch2208:
+		s = "Alteon Application Switch 2208"
+	case NDPChassisAlteonApplicationSwitch2216:
+		s = "Alteon Application Switch 2216"
+	case NDPChassisAlteonApplicationSwitch3408:
+		s = "Alteon Application Switch 3408"
+	case NDPChassisAlteonApplicationSwitch3416:
+		s = "Alteon Application Switch 3416"
+	case NDPChassisNortelNetworksWirelessLANSecuritySwitch2250:
+		s = "Nortel Networks Wireless LAN SecuritySwitch 2250"
+	case NDPChassisEthernetSwitch42548T:
+		s = "Ethernet Switch 425-48T"
+	case NDPChassisEthernetSwitch42524T:
+		s = "Ethernet Switch 425-24T"
+	case NDPChassisNortelNetworksWirelessLANAccessPoint2221:
+		s = "Nortel Networks Wireless LAN Access Point 2221"
+	case NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch:
+		s = "Nortel Metro Ethernet Service Unit 24-T SPF switch"
+	case NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch:
+		s = " Nortel Metro Ethernet Service Unit 24-T LX DC switch"
+	case NDPChassisPassport830010slotchassis:
+		s = "Passport 8300 10-slot chassis"
+	case NDPChassisPassport83006slotchassis:
+		s = "Passport 8300 6-slot chassis"
+	case NDPChassisEthernetRoutingSwitch552024TPWR:
+		s = "Ethernet Routing Switch 5520-24T-PWR"
+	case NDPChassisEthernetRoutingSwitch552048TPWR:
+		s = "Ethernet Routing Switch 5520-48T-PWR"
+	case NDPChassisNortelNetworksVPNGateway3050:
+		s = "Nortel Networks VPN Gateway 3050"
+	case NDPChassisAlteonSSL31010100:
+		s = "Alteon SSL 310 10/100"
+	case NDPChassisAlteonSSL31010100Fiber:
+		s = "Alteon SSL 310 10/100 Fiber"
+	case NDPChassisAlteonSSL31010100FIPS:
+		s = "Alteon SSL 310 10/100 FIPS"
+	case NDPChassisAlteonSSL410101001000:
+		s = "Alteon SSL 410 10/100/1000"
+	case NDPChassisAlteonSSL410101001000Fiber:
+		s = "Alteon SSL 410 10/100/1000 Fiber"
+	case NDPChassisAlteonApplicationSwitch2424SSL:
+		s = "Alteon Application Switch 2424-SSL"
+	case NDPChassisEthernetSwitch32524T:
+		s = "Ethernet Switch 325-24T"
+	case NDPChassisEthernetSwitch32524G:
+		s = "Ethernet Switch 325-24G"
+	case NDPChassisNortelNetworksWirelessLANAccessPoint2225:
+		s = "Nortel Networks Wireless LAN Access Point 2225"
+	case NDPChassisNortelNetworksWirelessLANSecuritySwitch2270:
+		s = "Nortel Networks Wireless LAN SecuritySwitch 2270"
+	case NDPChassis24portEthernetSwitch47024TPWR:
+		s = "24-port Ethernet Switch 470-24T-PWR"
+	case NDPChassis48portEthernetSwitch47048TPWR:
+		s = "48-port Ethernet Switch 470-48T-PWR"
+	case NDPChassisEthernetRoutingSwitch553024TFD:
+		s = "Ethernet Routing Switch 5530-24TFD"
+	case NDPChassisEthernetSwitch351024T:
+		s = "Ethernet Switch 3510-24T"
+	case NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch:
+		s = "Nortel Metro Ethernet Service Unit 12G AC L3 switch"
+	case NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch:
+		s = "Nortel Metro Ethernet Service Unit 12G DC L3 switch"
+	case NDPChassisNortelSecureAccessSwitch:
+		s = "Nortel Secure Access Switch"
+	case NDPChassisNortelNetworksVPNGateway3070:
+		s = "Nortel Networks VPN Gateway 3070"
+	case NDPChassisOPTeraMetro3500:
+		s = "OPTera Metro 3500"
+	case NDPChassisSMBBES101024T:
+		s = "SMB BES 1010 24T"
+	case NDPChassisSMBBES101048T:
+		s = "SMB BES 1010 48T"
+	case NDPChassisSMBBES102024TPWR:
+		s = "SMB BES 1020 24T PWR"
+	case NDPChassisSMBBES102048TPWR:
+		s = "SMB BES 1020 48T PWR"
+	case NDPChassisSMBBES201024T:
+		s = "SMB BES 2010 24T"
+	case NDPChassisSMBBES201048T:
+		s = "SMB BES 2010 48T"
+	case NDPChassisSMBBES202024TPWR:
+		s = "SMB BES 2020 24T PWR"
+	case NDPChassisSMBBES202048TPWR:
+		s = "SMB BES 2020 48T PWR"
+	case NDPChassisSMBBES11024T:
+		s = "SMB BES 110 24T"
+	case NDPChassisSMBBES11048T:
+		s = "SMB BES 110 48T"
+	case NDPChassisSMBBES12024TPWR:
+		s = "SMB BES 120 24T PWR"
+	case NDPChassisSMBBES12048TPWR:
+		s = "SMB BES 120 48T PWR"
+	case NDPChassisSMBBES21024T:
+		s = "SMB BES 210 24T"
+	case NDPChassisSMBBES21048T:
+		s = "SMB BES 210 48T"
+	case NDPChassisSMBBES22024TPWR:
+		s = "SMB BES 220 24T PWR"
+	case NDPChassisSMBBES22048TPWR:
+		s = "SMB BES 220 48T PWR"
+	case NDPChassisOME6500:
+		s = "OME 6500"
+	case NDPChassisEthernetRoutingSwitch4548GT:
+		s = "Ethernet Routing Switch 4548GT"
+	case NDPChassisEthernetRoutingSwitch4548GTPWR:
+		s = "Ethernet Routing Switch 4548GT-PWR"
+	case NDPChassisEthernetRoutingSwitch4550T:
+		s = "Ethernet Routing Switch 4550T"
+	case NDPChassisEthernetRoutingSwitch4550TPWR:
+		s = "Ethernet Routing Switch 4550T-PWR"
+	case NDPChassisEthernetRoutingSwitch4526FX:
+		s = "Ethernet Routing Switch 4526FX"
+	case NDPChassisEthernetRoutingSwitch250026T:
+		s = "Ethernet Routing Switch 2500-26T"
+	case NDPChassisEthernetRoutingSwitch250026TPWR:
+		s = "Ethernet Routing Switch 2500-26T-PWR"
+	case NDPChassisEthernetRoutingSwitch250050T:
+		s = "Ethernet Routing Switch 2500-50T"
+	case NDPChassisEthernetRoutingSwitch250050TPWR:
+		s = "Ethernet Routing Switch 2500-50T-PWR"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t NDPBackplaneType) String() (s string) {
+	switch t {
+	case NDPBackplaneOther:
+		s = "Other"
+	case NDPBackplaneEthernet:
+		s = "Ethernet"
+	case NDPBackplaneEthernetTokenring:
+		s = "Ethernet and Tokenring"
+	case NDPBackplaneEthernetFDDI:
+		s = "Ethernet and FDDI"
+	case NDPBackplaneEthernetTokenringFDDI:
+		s = "Ethernet, Tokenring and FDDI"
+	case NDPBackplaneEthernetTokenringRedundantPower:
+		s = "Ethernet and Tokenring with redundant power"
+	case NDPBackplaneEthernetTokenringFDDIRedundantPower:
+		s = "Ethernet, Tokenring, FDDI with redundant power"
+	case NDPBackplaneTokenRing:
+		s = "Token Ring"
+	case NDPBackplaneEthernetTokenringFastEthernet:
+		s = "Ethernet, Tokenring and Fast Ethernet"
+	case NDPBackplaneEthernetFastEthernet:
+		s = "Ethernet and Fast Ethernet"
+	case NDPBackplaneEthernetTokenringFastEthernetRedundantPower:
+		s = "Ethernet, Tokenring, Fast Ethernet with redundant power"
+	case NDPBackplaneEthernetFastEthernetGigabitEthernet:
+		s = "Ethernet, Fast Ethernet and Gigabit Ethernet"
+	default:
+		s = "Unknown"
+	}
+	return
+}
+
+func (t NDPState) String() (s string) {
+	switch t {
+	case NDPStateTopology:
+		s = "Topology Change"
+	case NDPStateHeartbeat:
+		s = "Heartbeat"
+	case NDPStateNew:
+		s = "New"
+	default:
+		s = "Unknown"
+	}
+	return
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ntp.go b/go-controller/vendor/github.com/google/gopacket/layers/ntp.go
new file mode 100644
index 00000000000..33c15b3b39f
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ntp.go
@@ -0,0 +1,416 @@
+// Copyright 2016 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+//
+//******************************************************************************
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+//******************************************************************************
+//
+// Network Time Protocol (NTP) Decoding Layer
+// ------------------------------------------
+// This file provides a GoPacket decoding layer for NTP.
+//
+//******************************************************************************
+//
+// About The Network Time Protocol (NTP)
+// -------------------------------------
+// NTP is a protocol that enables computers on the internet to set their
+// clocks to the correct time (or to a time that is acceptably close to the
+// correct time). NTP runs on top of UDP.
+//
+// There have been a series of versions of the NTP protocol. The latest
+// version is V4 and is specified in RFC 5905:
+//     http://www.ietf.org/rfc/rfc5905.txt
+//
+//******************************************************************************
+//
+// References
+// ----------
+//
+// Wikipedia's NTP entry:
+//     https://en.wikipedia.org/wiki/Network_Time_Protocol
+//     This is the best place to get an overview of NTP.
+//
+// Network Time Protocol Home Website:
+//     http://www.ntp.org/
+//     This appears to be the official website of NTP.
+//
+// List of current NTP Protocol RFCs:
+//     http://www.ntp.org/rfc.html
+//
+// RFC 958: "Network Time Protocol (NTP)" (1985)
+//     https://tools.ietf.org/html/rfc958
+//     This is the original NTP specification.
+//
+// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992)
+//     https://tools.ietf.org/html/rfc1305
+//     The protocol was updated in 1992 yielding NTP V3.
+//
+// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010)
+//     https://www.ietf.org/rfc/rfc5905.txt
+//     The protocol was updated in 2010 yielding NTP V4.
+//     V4 is backwards compatible with all previous versions of NTP.
+//
+// RFC 5906: "Network Time Protocol Version 4: Autokey Specification"
+//     https://tools.ietf.org/html/rfc5906
+//     This document addresses the security of the NTP protocol
+//     and is probably not relevant to this package.
+//
+// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)"
+//     https://tools.ietf.org/html/rfc5907
+//     This document addresses the management of NTP servers and
+//     is probably not relevant to this package.
+//
+// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6"
+//     https://tools.ietf.org/html/rfc5908
+//     This document addresses the use of NTP in DHCPv6 and is
+//     probably not relevant to this package.
+//
+// "Let's make a NTP Client in C"
+//     https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html
+//     This web page contains useful information about the details of NTP,
+//     including an NTP record struture in C, and C code.
+//
+// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)"
+//     http://what-when-how.com/computer-network-time-synchronization/
+//        ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/
+//     This web page contains useful information on the details of NTP.
+//
+// "Technical information - NTP Data Packet"
+//     https://www.meinbergglobal.com/english/info/ntp-packet.htm
+//     This page has a helpful diagram of an NTP V4 packet.
+//
+//******************************************************************************
+//
+// Obsolete References
+// -------------------
+//
+// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989)
+//     https://tools.ietf.org/html/rfc1119
+//     Version 2 was drafted in 1989.
+//     It is unclear whether V2 was ever implememented or whether the
+//     ideas ended up in V3 (which was implemented in 1992).
+//
+// RFC 1361: "Simple Network Time Protocol (SNTP)"
+//     https://tools.ietf.org/html/rfc1361
+//     This document is obsoleted by RFC 1769 and is included only for completeness.
+//
+// RFC 1769: "Simple Network Time Protocol (SNTP)"
+//     https://tools.ietf.org/html/rfc1769
+//     This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness.
+//
+// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
+//     https://tools.ietf.org/html/rfc2030
+//     This document is obsoleted by RFC 4330 and is included only for completeness.
+//
+// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
+//     https://tools.ietf.org/html/rfc4330
+//     This document is obsoleted by RFC 5905 and is included only for completeness.
+//
+//******************************************************************************
+//
+// Endian And Bit Numbering Issues
+// -------------------------------
+//
+// Endian and bit numbering issues can be confusing. Here is some
+// clarification:
+//
+//    ENDIAN: Values are sent big endian.
+//    https://en.wikipedia.org/wiki/Endianness
+//
+//    BIT NUMBERING: Bits are numbered 0 upwards from the most significant
+//    bit to the least significant bit. This means that if there is a 32-bit
+//    value, the most significant bit is called bit 0 and the least
+//    significant bit is called bit 31.
+//
+// See RFC 791 Appendix B for more discussion.
+//
+//******************************************************************************
+//
+// NTP V3 and V4 Packet Format
+// ---------------------------
+// NTP packets are UDP packets whose payload contains an NTP record.
+//
+// The NTP RFC defines the format of the NTP record.
+//
+// There have been four versions of the protocol:
+//
+//    V1 in 1985
+//    V2 in 1989
+//    V3 in 1992
+//    V4 in 2010
+//
+// It is clear that V1 and V2 are obsolete, and there is no need to
+// cater for these formats.
+//
+// V3 and V4 essentially use the same format, with V4 adding some optional
+// fields on the end. So this package supports the V3 and V4 formats.
+//
+// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains
+// the following diagram for the NTP record format:
+
+//      0                   1                   2                   3
+//      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                         Root Delay                            |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                         Root Dispersion                       |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                          Reference ID                         |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     +                     Reference Timestamp (64)                  +
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     +                      Origin Timestamp (64)                    +
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     +                      Receive Timestamp (64)                   +
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     +                      Transmit Timestamp (64)                  +
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     .                                                               .
+//     .                    Extension Field 1 (variable)               .
+//     .                                                               .
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     .                                                               .
+//     .                    Extension Field 2 (variable)               .
+//     .                                                               .
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                          Key Identifier                       |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     |                                                               |
+//     |                            dgst (128)                         |
+//     |                                                               |
+//     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//     From http://www.ietf.org/rfc/rfc5905.txt
+//
+// The fields "Extension Field 1 (variable)" and later are optional fields,
+// and so we can set a minimum NTP record size of 48 bytes.
+//
+const ntpMinimumRecordSizeInBytes int = 48
+
+//******************************************************************************
+
+// NTP Type
+// --------
+// Type NTP implements the DecodingLayer interface. Each NTP object
+// represents in a structured form the NTP record present as the UDP
+// payload in an NTP UDP packet.
+//
+
+type NTPLeapIndicator uint8
+type NTPVersion uint8
+type NTPMode uint8
+type NTPStratum uint8
+type NTPLog2Seconds int8
+type NTPFixed16Seconds uint32
+type NTPReferenceID uint32
+type NTPTimestamp uint64
+
+type NTP struct {
+	BaseLayer // Stores the packet bytes and payload bytes.
+
+	LeapIndicator      NTPLeapIndicator  // [0,3]. Indicates whether leap second(s) is to be added.
+	Version            NTPVersion        // [0,7]. Version of the NTP protocol.
+	Mode               NTPMode           // [0,7]. Mode.
+	Stratum            NTPStratum        // [0,255]. Stratum of time server in the server tree.
+	Poll               NTPLog2Seconds    // [-128,127]. The maximum interval between successive messages, in log2 seconds.
+	Precision          NTPLog2Seconds    // [-128,127]. The precision of the system clock, in log2 seconds.
+	RootDelay          NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16.
+	RootDispersion     NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16.
+	ReferenceID        NTPReferenceID    // ID code of reference clock [0,2^32-1].
+	ReferenceTimestamp NTPTimestamp      // Most recent timestamp from the reference clock.
+	OriginTimestamp    NTPTimestamp      // Local time when request was sent from local host.
+	ReceiveTimestamp   NTPTimestamp      // Local time (on server) that request arrived at server host.
+	TransmitTimestamp  NTPTimestamp      // Local time (on server) that request departed server host.
+
+	// FIX: This package should analyse the extension fields and represent the extension fields too.
+	ExtensionBytes []byte // Just put extensions in a byte slice.
+}
+
+//******************************************************************************
+
+// LayerType returns the layer type of the NTP object, which is LayerTypeNTP.
+func (d *NTP) LayerType() gopacket.LayerType {
+	return LayerTypeNTP
+}
+
+//******************************************************************************
+
+// decodeNTP analyses a byte slice and attempts to decode it as an NTP
+// record of a UDP packet.
+//
+// If it succeeds, it loads p with information about the packet and returns nil.
+// If it fails, it returns an error (non nil).
+//
+// This function is employed in layertypes.go to register the NTP layer.
+func decodeNTP(data []byte, p gopacket.PacketBuilder) error {
+
+	// Attempt to decode the byte slice.
+	d := &NTP{}
+	err := d.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+
+	// If the decoding worked, add the layer to the packet and set it
+	// as the application layer too, if there isn't already one.
+	p.AddLayer(d)
+	p.SetApplicationLayer(d)
+
+	return nil
+}
+
+//******************************************************************************
+
+// DecodeFromBytes analyses a byte slice and attempts to decode it as an NTP
+// record of a UDP packet.
+//
+// Upon succeeds, it loads the NTP object with information about the packet
+// and returns nil.
+// Upon failure, it returns an error (non nil).
+func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+
+	// If the data block is too short to be a NTP record, then return an error.
+	if len(data) < ntpMinimumRecordSizeInBytes {
+		df.SetTruncated()
+		return errors.New("NTP packet too short")
+	}
+
+	// RFC 5905 does not appear to define a maximum NTP record length.
+	// The protocol allows "extension fields" to be included in the record,
+	// and states about these fields:"
+	//
+	//     "While the minimum field length containing required fields is
+	//      four words (16 octets), a maximum field length remains to be
+	//      established."
+	//
+	// For this reason, the packet length is not checked here for being too long.
+
+	// NTP type embeds type BaseLayer which contains two fields:
+	//    Contents is supposed to contain the bytes of the data at this level.
+	//    Payload is supposed to contain the payload of this level.
+	// Here we set the baselayer to be the bytes of the NTP record.
+	d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
+
+	// Extract the fields from the block of bytes.
+	// To make sense of this, refer to the packet diagram
+	// above and the section on endian conventions.
+
+	// The first few fields are all packed into the first 32 bits. Unpack them.
+	f := data[0]
+	d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6)
+	d.Version = NTPVersion((f & 0x38) >> 3)
+	d.Mode = NTPMode(f & 0x07)
+	d.Stratum = NTPStratum(data[1])
+	d.Poll = NTPLog2Seconds(data[2])
+	d.Precision = NTPLog2Seconds(data[3])
+
+	// The remaining fields can just be copied in big endian order.
+	d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8]))
+	d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12]))
+	d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16]))
+	d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24]))
+	d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32]))
+	d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40]))
+	d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48]))
+
+	// This layer does not attempt to analyse the extension bytes.
+	// But if there are any, we'd like the user to know. So we just
+	// place them all in an ExtensionBytes field.
+	d.ExtensionBytes = data[48:]
+
+	// Return no error.
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (d *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes)
+	if err != nil {
+		return err
+	}
+
+	// Pack the first few fields into the first 32 bits.
+	h := uint8(0)
+	h |= (uint8(d.LeapIndicator) << 6) & 0xC0
+	h |= (uint8(d.Version) << 3) & 0x38
+	h |= (uint8(d.Mode)) & 0x07
+	data[0] = byte(h)
+	data[1] = byte(d.Stratum)
+	data[2] = byte(d.Poll)
+	data[3] = byte(d.Precision)
+
+	// The remaining fields can just be copied in big endian order.
+	binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay))
+	binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion))
+	binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID))
+	binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp))
+	binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp))
+	binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp))
+	binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp))
+
+	ex, err := b.AppendBytes(len(d.ExtensionBytes))
+	if err != nil {
+		return err
+	}
+	copy(ex, d.ExtensionBytes)
+
+	return nil
+}
+
+//******************************************************************************
+
+// CanDecode returns a set of layers that NTP objects can decode.
+// As NTP objects can only decide the NTP layer, we can return just that layer.
+// Apparently a single layer type implements LayerClass.
+func (d *NTP) CanDecode() gopacket.LayerClass {
+	return LayerTypeNTP
+}
+
+//******************************************************************************
+
+// NextLayerType specifies the next layer that GoPacket should attempt to
+// analyse after this (NTP) layer. As NTP packets do not contain any payload
+// bytes, there are no further layers to analyse.
+func (d *NTP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+//******************************************************************************
+
+// NTP packets do not carry any data payload, so the empty byte slice is retured.
+// In Go, a nil slice is functionally identical to an empty slice, so we
+// return nil to avoid a heap allocation.
+func (d *NTP) Payload() []byte {
+	return nil
+}
+
+//******************************************************************************
+//*                            End Of NTP File                                 *
+//******************************************************************************
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ospf.go b/go-controller/vendor/github.com/google/gopacket/layers/ospf.go
new file mode 100644
index 00000000000..4f5473d065b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ospf.go
@@ -0,0 +1,715 @@
+// 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"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// OSPFType denotes what kind of OSPF type it is
+type OSPFType uint8
+
+// Potential values for OSPF.Type.
+const (
+	OSPFHello                   OSPFType = 1
+	OSPFDatabaseDescription     OSPFType = 2
+	OSPFLinkStateRequest        OSPFType = 3
+	OSPFLinkStateUpdate         OSPFType = 4
+	OSPFLinkStateAcknowledgment OSPFType = 5
+)
+
+// LSA Function Codes for LSAheader.LSType
+const (
+	RouterLSAtypeV2         = 0x1
+	RouterLSAtype           = 0x2001
+	NetworkLSAtypeV2        = 0x2
+	NetworkLSAtype          = 0x2002
+	SummaryLSANetworktypeV2 = 0x3
+	InterAreaPrefixLSAtype  = 0x2003
+	SummaryLSAASBRtypeV2    = 0x4
+	InterAreaRouterLSAtype  = 0x2004
+	ASExternalLSAtypeV2     = 0x5
+	ASExternalLSAtype       = 0x4005
+	NSSALSAtype             = 0x2007
+	NSSALSAtypeV2           = 0x7
+	LinkLSAtype             = 0x0008
+	IntraAreaPrefixLSAtype  = 0x2009
+)
+
+// String conversions for OSPFType
+func (i OSPFType) String() string {
+	switch i {
+	case OSPFHello:
+		return "Hello"
+	case OSPFDatabaseDescription:
+		return "Database Description"
+	case OSPFLinkStateRequest:
+		return "Link State Request"
+	case OSPFLinkStateUpdate:
+		return "Link State Update"
+	case OSPFLinkStateAcknowledgment:
+		return "Link State Acknowledgment"
+	default:
+		return ""
+	}
+}
+
+// Prefix extends IntraAreaPrefixLSA
+type Prefix struct {
+	PrefixLength  uint8
+	PrefixOptions uint8
+	Metric        uint16
+	AddressPrefix []byte
+}
+
+// IntraAreaPrefixLSA is the struct from RFC 5340  A.4.10.
+type IntraAreaPrefixLSA struct {
+	NumOfPrefixes  uint16
+	RefLSType      uint16
+	RefLinkStateID uint32
+	RefAdvRouter   uint32
+	Prefixes       []Prefix
+}
+
+// LinkLSA is the struct from RFC 5340  A.4.9.
+type LinkLSA struct {
+	RtrPriority      uint8
+	Options          uint32
+	LinkLocalAddress []byte
+	NumOfPrefixes    uint32
+	Prefixes         []Prefix
+}
+
+// ASExternalLSAV2 is the struct from RFC 2328  A.4.5.
+type ASExternalLSAV2 struct {
+	NetworkMask       uint32
+	ExternalBit       uint8
+	Metric            uint32
+	ForwardingAddress uint32
+	ExternalRouteTag  uint32
+}
+
+// ASExternalLSA is the struct from RFC 5340  A.4.7.
+type ASExternalLSA struct {
+	Flags             uint8
+	Metric            uint32
+	PrefixLength      uint8
+	PrefixOptions     uint8
+	RefLSType         uint16
+	AddressPrefix     []byte
+	ForwardingAddress []byte
+	ExternalRouteTag  uint32
+	RefLinkStateID    uint32
+}
+
+// InterAreaRouterLSA is the struct from RFC 5340  A.4.6.
+type InterAreaRouterLSA struct {
+	Options             uint32
+	Metric              uint32
+	DestinationRouterID uint32
+}
+
+// InterAreaPrefixLSA is the struct from RFC 5340  A.4.5.
+type InterAreaPrefixLSA struct {
+	Metric        uint32
+	PrefixLength  uint8
+	PrefixOptions uint8
+	AddressPrefix []byte
+}
+
+// NetworkLSA is the struct from RFC 5340  A.4.4.
+type NetworkLSA struct {
+	Options        uint32
+	AttachedRouter []uint32
+}
+
+// NetworkLSAV2 is the struct from RFC 2328  A.4.3.
+type NetworkLSAV2 struct {
+	NetworkMask    uint32
+	AttachedRouter []uint32
+}
+
+// RouterV2 extends RouterLSAV2
+type RouterV2 struct {
+	Type     uint8
+	LinkID   uint32
+	LinkData uint32
+	Metric   uint16
+}
+
+// RouterLSAV2 is the struct from RFC 2328  A.4.2.
+type RouterLSAV2 struct {
+	Flags   uint8
+	Links   uint16
+	Routers []RouterV2
+}
+
+// Router extends RouterLSA
+type Router struct {
+	Type                uint8
+	Metric              uint16
+	InterfaceID         uint32
+	NeighborInterfaceID uint32
+	NeighborRouterID    uint32
+}
+
+// RouterLSA is the struct from RFC 5340  A.4.3.
+type RouterLSA struct {
+	Flags   uint8
+	Options uint32
+	Routers []Router
+}
+
+// LSAheader is the struct from RFC 5340  A.4.2 and RFC 2328 A.4.1.
+type LSAheader struct {
+	LSAge       uint16
+	LSType      uint16
+	LinkStateID uint32
+	AdvRouter   uint32
+	LSSeqNumber uint32
+	LSChecksum  uint16
+	Length      uint16
+	LSOptions   uint8
+}
+
+// LSA links LSAheader with the structs from RFC 5340  A.4.
+type LSA struct {
+	LSAheader
+	Content interface{}
+}
+
+// LSUpdate is the struct from RFC 5340  A.3.5.
+type LSUpdate struct {
+	NumOfLSAs uint32
+	LSAs      []LSA
+}
+
+// LSReq is the struct from RFC 5340  A.3.4.
+type LSReq struct {
+	LSType    uint16
+	LSID      uint32
+	AdvRouter uint32
+}
+
+// DbDescPkg is the struct from RFC 5340  A.3.3.
+type DbDescPkg struct {
+	Options      uint32
+	InterfaceMTU uint16
+	Flags        uint16
+	DDSeqNumber  uint32
+	LSAinfo      []LSAheader
+}
+
+// HelloPkg  is the struct from RFC 5340  A.3.2.
+type HelloPkg struct {
+	InterfaceID              uint32
+	RtrPriority              uint8
+	Options                  uint32
+	HelloInterval            uint16
+	RouterDeadInterval       uint32
+	DesignatedRouterID       uint32
+	BackupDesignatedRouterID uint32
+	NeighborID               []uint32
+}
+
+// HelloPkgV2 extends the HelloPkg struct with OSPFv2 information
+type HelloPkgV2 struct {
+	HelloPkg
+	NetworkMask uint32
+}
+
+// OSPF is a basic OSPF packet header with common fields of Version 2 and Version 3.
+type OSPF struct {
+	Version      uint8
+	Type         OSPFType
+	PacketLength uint16
+	RouterID     uint32
+	AreaID       uint32
+	Checksum     uint16
+	Content      interface{}
+}
+
+//OSPFv2 extend the OSPF head with version 2 specific fields
+type OSPFv2 struct {
+	BaseLayer
+	OSPF
+	AuType         uint16
+	Authentication uint64
+}
+
+// OSPFv3 extend the OSPF head with version 3 specific fields
+type OSPFv3 struct {
+	BaseLayer
+	OSPF
+	Instance uint8
+	Reserved uint8
+}
+
+// getLSAsv2 parses the LSA information from the packet for OSPFv2
+func getLSAsv2(num uint32, data []byte) ([]LSA, error) {
+	var lsas []LSA
+	var i uint32 = 0
+	var offset uint32 = 0
+	for ; i < num; i++ {
+		lstype := uint16(data[offset+3])
+		lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
+		content, err := extractLSAInformation(lstype, lsalength, data[offset:])
+		if err != nil {
+			return nil, fmt.Errorf("Could not extract Link State type.")
+		}
+		lsa := LSA{
+			LSAheader: LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[offset : offset+2]),
+				LSOptions:   data[offset+2],
+				LSType:      lstype,
+				LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[offset+8 : offset+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[offset+16 : offset+18]),
+				Length:      lsalength,
+			},
+			Content: content,
+		}
+		lsas = append(lsas, lsa)
+		offset += uint32(lsalength)
+	}
+	return lsas, nil
+}
+
+// extractLSAInformation extracts all the LSA information
+func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{}, error) {
+	if lsalength < 20 {
+		return nil, fmt.Errorf("Link State header length %v too short, %v required", lsalength, 20)
+	}
+	if len(data) < int(lsalength) {
+		return nil, fmt.Errorf("Link State header length %v too short, %v required", len(data), lsalength)
+	}
+	var content interface{}
+	switch lstype {
+	case RouterLSAtypeV2:
+		var routers []RouterV2
+		var j uint32
+		for j = 24; j < uint32(lsalength); j += 12 {
+			if len(data) < int(j+12) {
+				return nil, errors.New("LSAtypeV2 too small")
+			}
+			router := RouterV2{
+				LinkID:   binary.BigEndian.Uint32(data[j : j+4]),
+				LinkData: binary.BigEndian.Uint32(data[j+4 : j+8]),
+				Type:     uint8(data[j+8]),
+				Metric:   binary.BigEndian.Uint16(data[j+10 : j+12]),
+			}
+			routers = append(routers, router)
+		}
+		if len(data) < 24 {
+			return nil, errors.New("LSAtypeV2 too small")
+		}
+		links := binary.BigEndian.Uint16(data[22:24])
+		content = RouterLSAV2{
+			Flags:   data[20],
+			Links:   links,
+			Routers: routers,
+		}
+	case NSSALSAtypeV2:
+		fallthrough
+	case ASExternalLSAtypeV2:
+		content = ASExternalLSAV2{
+			NetworkMask:       binary.BigEndian.Uint32(data[20:24]),
+			ExternalBit:       data[24] & 0x80,
+			Metric:            binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
+			ForwardingAddress: binary.BigEndian.Uint32(data[28:32]),
+			ExternalRouteTag:  binary.BigEndian.Uint32(data[32:36]),
+		}
+	case NetworkLSAtypeV2:
+		var routers []uint32
+		var j uint32
+		for j = 24; j < uint32(lsalength); j += 4 {
+			routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
+		}
+		content = NetworkLSAV2{
+			NetworkMask:    binary.BigEndian.Uint32(data[20:24]),
+			AttachedRouter: routers,
+		}
+	case RouterLSAtype:
+		var routers []Router
+		var j uint32
+		for j = 24; j < uint32(lsalength); j += 16 {
+			router := Router{
+				Type:                uint8(data[j]),
+				Metric:              binary.BigEndian.Uint16(data[j+2 : j+4]),
+				InterfaceID:         binary.BigEndian.Uint32(data[j+4 : j+8]),
+				NeighborInterfaceID: binary.BigEndian.Uint32(data[j+8 : j+12]),
+				NeighborRouterID:    binary.BigEndian.Uint32(data[j+12 : j+16]),
+			}
+			routers = append(routers, router)
+		}
+		content = RouterLSA{
+			Flags:   uint8(data[20]),
+			Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			Routers: routers,
+		}
+	case NetworkLSAtype:
+		var routers []uint32
+		var j uint32
+		for j = 24; j < uint32(lsalength); j += 4 {
+			routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
+		}
+		content = NetworkLSA{
+			Options:        binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			AttachedRouter: routers,
+		}
+	case InterAreaPrefixLSAtype:
+		content = InterAreaPrefixLSA{
+			Metric:        binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			PrefixLength:  uint8(data[24]),
+			PrefixOptions: uint8(data[25]),
+			AddressPrefix: data[28:uint32(lsalength)],
+		}
+	case InterAreaRouterLSAtype:
+		content = InterAreaRouterLSA{
+			Options:             binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			Metric:              binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
+			DestinationRouterID: binary.BigEndian.Uint32(data[28:32]),
+		}
+	case ASExternalLSAtype:
+		fallthrough
+	case NSSALSAtype:
+		flags := uint8(data[20])
+		prefixLen := uint8(data[24]) / 8
+		var forwardingAddress []byte
+		if (flags & 0x02) == 0x02 {
+			forwardingAddress = data[28+uint32(prefixLen) : 28+uint32(prefixLen)+16]
+		}
+		content = ASExternalLSA{
+			Flags:             flags,
+			Metric:            binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			PrefixLength:      prefixLen,
+			PrefixOptions:     uint8(data[25]),
+			RefLSType:         binary.BigEndian.Uint16(data[26:28]),
+			AddressPrefix:     data[28 : 28+uint32(prefixLen)],
+			ForwardingAddress: forwardingAddress,
+		}
+	case LinkLSAtype:
+		var prefixes []Prefix
+		var prefixOffset uint32 = 44
+		var j uint32
+		numOfPrefixes := binary.BigEndian.Uint32(data[40:44])
+		for j = 0; j < numOfPrefixes; j++ {
+			prefixLen := uint8(data[prefixOffset])
+			prefix := Prefix{
+				PrefixLength:  prefixLen,
+				PrefixOptions: uint8(data[prefixOffset+1]),
+				AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
+			}
+			prefixes = append(prefixes, prefix)
+			prefixOffset = prefixOffset + 4 + uint32(prefixLen)/8
+		}
+		content = LinkLSA{
+			RtrPriority:      uint8(data[20]),
+			Options:          binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
+			LinkLocalAddress: data[24:40],
+			NumOfPrefixes:    numOfPrefixes,
+			Prefixes:         prefixes,
+		}
+	case IntraAreaPrefixLSAtype:
+		var prefixes []Prefix
+		var prefixOffset uint32 = 32
+		var j uint16
+		numOfPrefixes := binary.BigEndian.Uint16(data[20:22])
+		for j = 0; j < numOfPrefixes; j++ {
+			prefixLen := uint8(data[prefixOffset])
+			prefix := Prefix{
+				PrefixLength:  prefixLen,
+				PrefixOptions: uint8(data[prefixOffset+1]),
+				Metric:        binary.BigEndian.Uint16(data[prefixOffset+2 : prefixOffset+4]),
+				AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
+			}
+			prefixes = append(prefixes, prefix)
+			prefixOffset = prefixOffset + 4 + uint32(prefixLen)
+		}
+		content = IntraAreaPrefixLSA{
+			NumOfPrefixes:  numOfPrefixes,
+			RefLSType:      binary.BigEndian.Uint16(data[22:24]),
+			RefLinkStateID: binary.BigEndian.Uint32(data[24:28]),
+			RefAdvRouter:   binary.BigEndian.Uint32(data[28:32]),
+			Prefixes:       prefixes,
+		}
+	default:
+		return nil, fmt.Errorf("Unknown Link State type.")
+	}
+	return content, nil
+}
+
+// getLSAs parses the LSA information from the packet for OSPFv3
+func getLSAs(num uint32, data []byte) ([]LSA, error) {
+	var lsas []LSA
+	var i uint32 = 0
+	var offset uint32 = 0
+	for ; i < num; i++ {
+		var content interface{}
+		lstype := binary.BigEndian.Uint16(data[offset+2 : offset+4])
+		lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
+
+		content, err := extractLSAInformation(lstype, lsalength, data[offset:])
+		if err != nil {
+			return nil, fmt.Errorf("Could not extract Link State type.")
+		}
+		lsa := LSA{
+			LSAheader: LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[offset : offset+2]),
+				LSType:      lstype,
+				LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[offset+8 : offset+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[offset+16 : offset+18]),
+				Length:      lsalength,
+			},
+			Content: content,
+		}
+		lsas = append(lsas, lsa)
+		offset += uint32(lsalength)
+	}
+	return lsas, nil
+}
+
+// DecodeFromBytes decodes the given bytes into the OSPF layer.
+func (ospf *OSPFv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 24 {
+		return fmt.Errorf("Packet too smal for OSPF Version 2")
+	}
+
+	ospf.Version = uint8(data[0])
+	ospf.Type = OSPFType(data[1])
+	ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
+	ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
+	ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
+	ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
+	ospf.AuType = binary.BigEndian.Uint16(data[14:16])
+	ospf.Authentication = binary.BigEndian.Uint64(data[16:24])
+
+	switch ospf.Type {
+	case OSPFHello:
+		var neighbors []uint32
+		for i := 44; uint16(i+4) <= ospf.PacketLength; i += 4 {
+			neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
+		}
+		ospf.Content = HelloPkgV2{
+			NetworkMask: binary.BigEndian.Uint32(data[24:28]),
+			HelloPkg: HelloPkg{
+				HelloInterval:            binary.BigEndian.Uint16(data[28:30]),
+				Options:                  uint32(data[30]),
+				RtrPriority:              uint8(data[31]),
+				RouterDeadInterval:       binary.BigEndian.Uint32(data[32:36]),
+				DesignatedRouterID:       binary.BigEndian.Uint32(data[36:40]),
+				BackupDesignatedRouterID: binary.BigEndian.Uint32(data[40:44]),
+				NeighborID:               neighbors,
+			},
+		}
+	case OSPFDatabaseDescription:
+		var lsas []LSAheader
+		for i := 32; uint16(i+20) <= ospf.PacketLength; i += 20 {
+			lsa := LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[i : i+2]),
+				LSType:      binary.BigEndian.Uint16(data[i+2 : i+4]),
+				LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[i+8 : i+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[i+16 : i+18]),
+				Length:      binary.BigEndian.Uint16(data[i+18 : i+20]),
+			}
+			lsas = append(lsas, lsa)
+		}
+		ospf.Content = DbDescPkg{
+			InterfaceMTU: binary.BigEndian.Uint16(data[24:26]),
+			Options:      uint32(data[26]),
+			Flags:        uint16(data[27]),
+			DDSeqNumber:  binary.BigEndian.Uint32(data[28:32]),
+			LSAinfo:      lsas,
+		}
+	case OSPFLinkStateRequest:
+		var lsrs []LSReq
+		for i := 24; uint16(i+12) <= ospf.PacketLength; i += 12 {
+			lsr := LSReq{
+				LSType:    binary.BigEndian.Uint16(data[i+2 : i+4]),
+				LSID:      binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
+			}
+			lsrs = append(lsrs, lsr)
+		}
+		ospf.Content = lsrs
+	case OSPFLinkStateUpdate:
+		num := binary.BigEndian.Uint32(data[24:28])
+
+		lsas, err := getLSAsv2(num, data[28:])
+		if err != nil {
+			return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
+		}
+		ospf.Content = LSUpdate{
+			NumOfLSAs: num,
+			LSAs:      lsas,
+		}
+	case OSPFLinkStateAcknowledgment:
+		var lsas []LSAheader
+		for i := 24; uint16(i+20) <= ospf.PacketLength; i += 20 {
+			lsa := LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[i : i+2]),
+				LSOptions:   data[i+2],
+				LSType:      uint16(data[i+3]),
+				LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[i+8 : i+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[i+16 : i+18]),
+				Length:      binary.BigEndian.Uint16(data[i+18 : i+20]),
+			}
+			lsas = append(lsas, lsa)
+		}
+		ospf.Content = lsas
+	}
+	return nil
+}
+
+// DecodeFromBytes decodes the given bytes into the OSPF layer.
+func (ospf *OSPFv3) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+
+	if len(data) < 16 {
+		return fmt.Errorf("Packet too smal for OSPF Version 3")
+	}
+
+	ospf.Version = uint8(data[0])
+	ospf.Type = OSPFType(data[1])
+	ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
+	ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
+	ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
+	ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
+	ospf.Instance = uint8(data[14])
+	ospf.Reserved = uint8(data[15])
+
+	switch ospf.Type {
+	case OSPFHello:
+		var neighbors []uint32
+		for i := 36; uint16(i+4) <= ospf.PacketLength; i += 4 {
+			neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
+		}
+		ospf.Content = HelloPkg{
+			InterfaceID:              binary.BigEndian.Uint32(data[16:20]),
+			RtrPriority:              uint8(data[20]),
+			Options:                  binary.BigEndian.Uint32(data[21:25]) >> 8,
+			HelloInterval:            binary.BigEndian.Uint16(data[24:26]),
+			RouterDeadInterval:       uint32(binary.BigEndian.Uint16(data[26:28])),
+			DesignatedRouterID:       binary.BigEndian.Uint32(data[28:32]),
+			BackupDesignatedRouterID: binary.BigEndian.Uint32(data[32:36]),
+			NeighborID:               neighbors,
+		}
+	case OSPFDatabaseDescription:
+		var lsas []LSAheader
+		for i := 28; uint16(i+20) <= ospf.PacketLength; i += 20 {
+			lsa := LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[i : i+2]),
+				LSType:      binary.BigEndian.Uint16(data[i+2 : i+4]),
+				LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[i+8 : i+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[i+16 : i+18]),
+				Length:      binary.BigEndian.Uint16(data[i+18 : i+20]),
+			}
+			lsas = append(lsas, lsa)
+		}
+		ospf.Content = DbDescPkg{
+			Options:      binary.BigEndian.Uint32(data[16:20]) & 0x00FFFFFF,
+			InterfaceMTU: binary.BigEndian.Uint16(data[20:22]),
+			Flags:        binary.BigEndian.Uint16(data[22:24]),
+			DDSeqNumber:  binary.BigEndian.Uint32(data[24:28]),
+			LSAinfo:      lsas,
+		}
+	case OSPFLinkStateRequest:
+		var lsrs []LSReq
+		for i := 16; uint16(i+12) <= ospf.PacketLength; i += 12 {
+			lsr := LSReq{
+				LSType:    binary.BigEndian.Uint16(data[i+2 : i+4]),
+				LSID:      binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
+			}
+			lsrs = append(lsrs, lsr)
+		}
+		ospf.Content = lsrs
+	case OSPFLinkStateUpdate:
+		num := binary.BigEndian.Uint32(data[16:20])
+		lsas, err := getLSAs(num, data[20:])
+		if err != nil {
+			return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
+		}
+		ospf.Content = LSUpdate{
+			NumOfLSAs: num,
+			LSAs:      lsas,
+		}
+
+	case OSPFLinkStateAcknowledgment:
+		var lsas []LSAheader
+		for i := 16; uint16(i+20) <= ospf.PacketLength; i += 20 {
+			lsa := LSAheader{
+				LSAge:       binary.BigEndian.Uint16(data[i : i+2]),
+				LSType:      binary.BigEndian.Uint16(data[i+2 : i+4]),
+				LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
+				AdvRouter:   binary.BigEndian.Uint32(data[i+8 : i+12]),
+				LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
+				LSChecksum:  binary.BigEndian.Uint16(data[i+16 : i+18]),
+				Length:      binary.BigEndian.Uint16(data[i+18 : i+20]),
+			}
+			lsas = append(lsas, lsa)
+		}
+		ospf.Content = lsas
+	default:
+	}
+
+	return nil
+}
+
+// LayerType returns LayerTypeOSPF
+func (ospf *OSPFv2) LayerType() gopacket.LayerType {
+	return LayerTypeOSPF
+}
+func (ospf *OSPFv3) LayerType() gopacket.LayerType {
+	return LayerTypeOSPF
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (ospf *OSPFv2) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+func (ospf *OSPFv3) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (ospf *OSPFv2) CanDecode() gopacket.LayerClass {
+	return LayerTypeOSPF
+}
+func (ospf *OSPFv3) CanDecode() gopacket.LayerClass {
+	return LayerTypeOSPF
+}
+
+func decodeOSPF(data []byte, p gopacket.PacketBuilder) error {
+	if len(data) < 14 {
+		return fmt.Errorf("Packet too smal for OSPF")
+	}
+
+	switch uint8(data[0]) {
+	case 2:
+		ospf := &OSPFv2{}
+		return decodingLayerDecoder(ospf, data, p)
+	case 3:
+		ospf := &OSPFv3{}
+		return decodingLayerDecoder(ospf, data, p)
+	default:
+	}
+
+	return fmt.Errorf("Unable to determine OSPF type.")
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/pflog.go b/go-controller/vendor/github.com/google/gopacket/layers/pflog.go
new file mode 100644
index 00000000000..9dbbd90d156
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/pflog.go
@@ -0,0 +1,84 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+type PFDirection uint8
+
+const (
+	PFDirectionInOut PFDirection = 0
+	PFDirectionIn    PFDirection = 1
+	PFDirectionOut   PFDirection = 2
+)
+
+// PFLog provides the layer for 'pf' packet-filter logging, as described at
+// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4
+type PFLog struct {
+	BaseLayer
+	Length              uint8
+	Family              ProtocolFamily
+	Action, Reason      uint8
+	IFName, Ruleset     []byte
+	RuleNum, SubruleNum uint32
+	UID                 uint32
+	PID                 int32
+	RuleUID             uint32
+	RulePID             int32
+	Direction           PFDirection
+	// The remainder is padding
+}
+
+func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 60 {
+		df.SetTruncated()
+		return errors.New("PFLog data less than 60 bytes")
+	}
+	pf.Length = data[0]
+	pf.Family = ProtocolFamily(data[1])
+	pf.Action = data[2]
+	pf.Reason = data[3]
+	pf.IFName = data[4:20]
+	pf.Ruleset = data[20:36]
+	pf.RuleNum = binary.BigEndian.Uint32(data[36:40])
+	pf.SubruleNum = binary.BigEndian.Uint32(data[40:44])
+	pf.UID = binary.BigEndian.Uint32(data[44:48])
+	pf.PID = int32(binary.BigEndian.Uint32(data[48:52]))
+	pf.RuleUID = binary.BigEndian.Uint32(data[52:56])
+	pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60]))
+	pf.Direction = PFDirection(data[60])
+	if pf.Length%4 != 1 {
+		return errors.New("PFLog header length should be 3 less than multiple of 4")
+	}
+	actualLength := int(pf.Length) + 3
+	if len(data) < actualLength {
+		return fmt.Errorf("PFLog data size < %d", actualLength)
+	}
+	pf.Contents = data[:actualLength]
+	pf.Payload = data[actualLength:]
+	return nil
+}
+
+// LayerType returns layers.LayerTypePFLog
+func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog }
+
+func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog }
+
+func (pf *PFLog) NextLayerType() gopacket.LayerType {
+	return pf.Family.LayerType()
+}
+
+func decodePFLog(data []byte, p gopacket.PacketBuilder) error {
+	pf := &PFLog{}
+	return decodingLayerDecoder(pf, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ports.go b/go-controller/vendor/github.com/google/gopacket/layers/ports.go
new file mode 100644
index 00000000000..1e3f42efc2b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ports.go
@@ -0,0 +1,156 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"fmt"
+	"strconv"
+
+	"github.com/google/gopacket"
+)
+
+// TCPPort is a port in a TCP layer.
+type TCPPort uint16
+
+// UDPPort is a port in a UDP layer.
+type UDPPort uint16
+
+// RUDPPort is a port in a RUDP layer.
+type RUDPPort uint8
+
+// SCTPPort is a port in a SCTP layer.
+type SCTPPort uint16
+
+// UDPLitePort is a port in a UDPLite layer.
+type UDPLitePort uint16
+
+// RUDPPortNames contains the string names for all RUDP ports.
+var RUDPPortNames = map[RUDPPort]string{}
+
+// UDPLitePortNames contains the string names for all UDPLite ports.
+var UDPLitePortNames = map[UDPLitePort]string{}
+
+// {TCP,UDP,SCTP}PortNames can be found in iana_ports.go
+
+// String returns the port as "number(name)" if there's a well-known port name,
+// or just "number" if there isn't.  Well-known names are stored in
+// TCPPortNames.
+func (a TCPPort) String() string {
+	if name, ok := TCPPortNames[a]; ok {
+		return fmt.Sprintf("%d(%s)", a, name)
+	}
+	return strconv.Itoa(int(a))
+}
+
+// LayerType returns a LayerType that would be able to decode the
+// application payload. It uses some well-known ports such as 53 for
+// DNS.
+//
+// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
+func (a TCPPort) LayerType() gopacket.LayerType {
+	lt := tcpPortLayerType[uint16(a)]
+	if lt != 0 {
+		return lt
+	}
+	return gopacket.LayerTypePayload
+}
+
+var tcpPortLayerType = [65536]gopacket.LayerType{
+	53:   LayerTypeDNS,
+	443:  LayerTypeTLS,       // https
+	502:  LayerTypeModbusTCP, // modbustcp
+	636:  LayerTypeTLS,       // ldaps
+	989:  LayerTypeTLS,       // ftps-data
+	990:  LayerTypeTLS,       // ftps
+	992:  LayerTypeTLS,       // telnets
+	993:  LayerTypeTLS,       // imaps
+	994:  LayerTypeTLS,       // ircs
+	995:  LayerTypeTLS,       // pop3s
+	5061: LayerTypeTLS,       // ips
+}
+
+// RegisterTCPPortLayerType creates a new mapping between a TCPPort
+// and an underlaying LayerType.
+func RegisterTCPPortLayerType(port TCPPort, layerType gopacket.LayerType) {
+	tcpPortLayerType[port] = layerType
+}
+
+// String returns the port as "number(name)" if there's a well-known port name,
+// or just "number" if there isn't.  Well-known names are stored in
+// UDPPortNames.
+func (a UDPPort) String() string {
+	if name, ok := UDPPortNames[a]; ok {
+		return fmt.Sprintf("%d(%s)", a, name)
+	}
+	return strconv.Itoa(int(a))
+}
+
+// LayerType returns a LayerType that would be able to decode the
+// application payload. It uses some well-known ports such as 53 for
+// DNS.
+//
+// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
+func (a UDPPort) LayerType() gopacket.LayerType {
+	lt := udpPortLayerType[uint16(a)]
+	if lt != 0 {
+		return lt
+	}
+	return gopacket.LayerTypePayload
+}
+
+var udpPortLayerType = [65536]gopacket.LayerType{
+	53:   LayerTypeDNS,
+	123:  LayerTypeNTP,
+	4789: LayerTypeVXLAN,
+	67:   LayerTypeDHCPv4,
+	68:   LayerTypeDHCPv4,
+	546:  LayerTypeDHCPv6,
+	547:  LayerTypeDHCPv6,
+	5060: LayerTypeSIP,
+	6343: LayerTypeSFlow,
+	6081: LayerTypeGeneve,
+	3784: LayerTypeBFD,
+	2152: LayerTypeGTPv1U,
+	623:  LayerTypeRMCP,
+	1812: LayerTypeRADIUS,
+}
+
+// RegisterUDPPortLayerType creates a new mapping between a UDPPort
+// and an underlaying LayerType.
+func RegisterUDPPortLayerType(port UDPPort, layerType gopacket.LayerType) {
+	udpPortLayerType[port] = layerType
+}
+
+// String returns the port as "number(name)" if there's a well-known port name,
+// or just "number" if there isn't.  Well-known names are stored in
+// RUDPPortNames.
+func (a RUDPPort) String() string {
+	if name, ok := RUDPPortNames[a]; ok {
+		return fmt.Sprintf("%d(%s)", a, name)
+	}
+	return strconv.Itoa(int(a))
+}
+
+// String returns the port as "number(name)" if there's a well-known port name,
+// or just "number" if there isn't.  Well-known names are stored in
+// SCTPPortNames.
+func (a SCTPPort) String() string {
+	if name, ok := SCTPPortNames[a]; ok {
+		return fmt.Sprintf("%d(%s)", a, name)
+	}
+	return strconv.Itoa(int(a))
+}
+
+// String returns the port as "number(name)" if there's a well-known port name,
+// or just "number" if there isn't.  Well-known names are stored in
+// UDPLitePortNames.
+func (a UDPLitePort) String() string {
+	if name, ok := UDPLitePortNames[a]; ok {
+		return fmt.Sprintf("%d(%s)", a, name)
+	}
+	return strconv.Itoa(int(a))
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/ppp.go b/go-controller/vendor/github.com/google/gopacket/layers/ppp.go
new file mode 100644
index 00000000000..e534d698cb2
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/ppp.go
@@ -0,0 +1,88 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"github.com/google/gopacket"
+)
+
+// PPP is the layer for PPP encapsulation headers.
+type PPP struct {
+	BaseLayer
+	PPPType       PPPType
+	HasPPTPHeader bool
+}
+
+// PPPEndpoint is a singleton endpoint for PPP.  Since there is no actual
+// addressing for the two ends of a PPP connection, we use a singleton value
+// named 'point' for each endpoint.
+var PPPEndpoint = gopacket.NewEndpoint(EndpointPPP, nil)
+
+// PPPFlow is a singleton flow for PPP.  Since there is no actual addressing for
+// the two ends of a PPP connection, we use a singleton value to represent the
+// flow for all PPP connections.
+var PPPFlow = gopacket.NewFlow(EndpointPPP, nil, nil)
+
+// LayerType returns LayerTypePPP
+func (p *PPP) LayerType() gopacket.LayerType { return LayerTypePPP }
+
+// LinkFlow returns PPPFlow.
+func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow }
+
+func decodePPP(data []byte, p gopacket.PacketBuilder) error {
+	ppp := &PPP{}
+	offset := 0
+	if data[0] == 0xff && data[1] == 0x03 {
+		offset = 2
+		ppp.HasPPTPHeader = true
+	}
+	if data[offset]&0x1 == 0 {
+		if data[offset+1]&0x1 == 0 {
+			return errors.New("PPP has invalid type")
+		}
+		ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[offset : offset+2]))
+		ppp.Contents = data[offset : offset+2]
+		ppp.Payload = data[offset+2:]
+	} else {
+		ppp.PPPType = PPPType(data[offset])
+		ppp.Contents = data[offset : offset+1]
+		ppp.Payload = data[offset+1:]
+	}
+	p.AddLayer(ppp)
+	p.SetLinkLayer(ppp)
+	return p.NextDecoder(ppp.PPPType)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (p *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	if p.PPPType&0x100 == 0 {
+		bytes, err := b.PrependBytes(2)
+		if err != nil {
+			return err
+		}
+		binary.BigEndian.PutUint16(bytes, uint16(p.PPPType))
+	} else {
+		bytes, err := b.PrependBytes(1)
+		if err != nil {
+			return err
+		}
+		bytes[0] = uint8(p.PPPType)
+	}
+	if p.HasPPTPHeader {
+		bytes, err := b.PrependBytes(2)
+		if err != nil {
+			return err
+		}
+		bytes[0] = 0xff
+		bytes[1] = 0x03
+	}
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/pppoe.go b/go-controller/vendor/github.com/google/gopacket/layers/pppoe.go
new file mode 100644
index 00000000000..14cd63a1899
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/pppoe.go
@@ -0,0 +1,60 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"github.com/google/gopacket"
+)
+
+// PPPoE is the layer for PPPoE encapsulation headers.
+type PPPoE struct {
+	BaseLayer
+	Version   uint8
+	Type      uint8
+	Code      PPPoECode
+	SessionId uint16
+	Length    uint16
+}
+
+// LayerType returns gopacket.LayerTypePPPoE.
+func (p *PPPoE) LayerType() gopacket.LayerType {
+	return LayerTypePPPoE
+}
+
+// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516).
+func decodePPPoE(data []byte, p gopacket.PacketBuilder) error {
+	pppoe := &PPPoE{
+		Version:   data[0] >> 4,
+		Type:      data[0] & 0x0F,
+		Code:      PPPoECode(data[1]),
+		SessionId: binary.BigEndian.Uint16(data[2:4]),
+		Length:    binary.BigEndian.Uint16(data[4:6]),
+	}
+	pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]}
+	p.AddLayer(pppoe)
+	return p.NextDecoder(pppoe.Code)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (p *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	payload := b.Bytes()
+	bytes, err := b.PrependBytes(6)
+	if err != nil {
+		return err
+	}
+	bytes[0] = (p.Version << 4) | p.Type
+	bytes[1] = byte(p.Code)
+	binary.BigEndian.PutUint16(bytes[2:], p.SessionId)
+	if opts.FixLengths {
+		p.Length = uint16(len(payload))
+	}
+	binary.BigEndian.PutUint16(bytes[4:], p.Length)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/prism.go b/go-controller/vendor/github.com/google/gopacket/layers/prism.go
new file mode 100644
index 00000000000..e1711e7f5b9
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/prism.go
@@ -0,0 +1,146 @@
+// Copyright 2015 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// http://www.tcpdump.org/linktypes/LINKTYPE_IEEE802_11_PRISM.html
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+func decodePrismValue(data []byte, pv *PrismValue) {
+	pv.DID = PrismDID(binary.LittleEndian.Uint32(data[0:4]))
+	pv.Status = binary.LittleEndian.Uint16(data[4:6])
+	pv.Length = binary.LittleEndian.Uint16(data[6:8])
+	pv.Data = data[8 : 8+pv.Length]
+}
+
+type PrismDID uint32
+
+const (
+	PrismDIDType1HostTime                  PrismDID = 0x10044
+	PrismDIDType2HostTime                  PrismDID = 0x01041
+	PrismDIDType1MACTime                   PrismDID = 0x20044
+	PrismDIDType2MACTime                   PrismDID = 0x02041
+	PrismDIDType1Channel                   PrismDID = 0x30044
+	PrismDIDType2Channel                   PrismDID = 0x03041
+	PrismDIDType1RSSI                      PrismDID = 0x40044
+	PrismDIDType2RSSI                      PrismDID = 0x04041
+	PrismDIDType1SignalQuality             PrismDID = 0x50044
+	PrismDIDType2SignalQuality             PrismDID = 0x05041
+	PrismDIDType1Signal                    PrismDID = 0x60044
+	PrismDIDType2Signal                    PrismDID = 0x06041
+	PrismDIDType1Noise                     PrismDID = 0x70044
+	PrismDIDType2Noise                     PrismDID = 0x07041
+	PrismDIDType1Rate                      PrismDID = 0x80044
+	PrismDIDType2Rate                      PrismDID = 0x08041
+	PrismDIDType1TransmittedFrameIndicator PrismDID = 0x90044
+	PrismDIDType2TransmittedFrameIndicator PrismDID = 0x09041
+	PrismDIDType1FrameLength               PrismDID = 0xA0044
+	PrismDIDType2FrameLength               PrismDID = 0x0A041
+)
+
+const (
+	PrismType1MessageCode uint16 = 0x00000044
+	PrismType2MessageCode uint16 = 0x00000041
+)
+
+func (p PrismDID) String() string {
+	dids := map[PrismDID]string{
+		PrismDIDType1HostTime:                  "Host Time",
+		PrismDIDType2HostTime:                  "Host Time",
+		PrismDIDType1MACTime:                   "MAC Time",
+		PrismDIDType2MACTime:                   "MAC Time",
+		PrismDIDType1Channel:                   "Channel",
+		PrismDIDType2Channel:                   "Channel",
+		PrismDIDType1RSSI:                      "RSSI",
+		PrismDIDType2RSSI:                      "RSSI",
+		PrismDIDType1SignalQuality:             "Signal Quality",
+		PrismDIDType2SignalQuality:             "Signal Quality",
+		PrismDIDType1Signal:                    "Signal",
+		PrismDIDType2Signal:                    "Signal",
+		PrismDIDType1Noise:                     "Noise",
+		PrismDIDType2Noise:                     "Noise",
+		PrismDIDType1Rate:                      "Rate",
+		PrismDIDType2Rate:                      "Rate",
+		PrismDIDType1TransmittedFrameIndicator: "Transmitted Frame Indicator",
+		PrismDIDType2TransmittedFrameIndicator: "Transmitted Frame Indicator",
+		PrismDIDType1FrameLength:               "Frame Length",
+		PrismDIDType2FrameLength:               "Frame Length",
+	}
+
+	if str, ok := dids[p]; ok {
+		return str
+	}
+
+	return "Unknown DID"
+}
+
+type PrismValue struct {
+	DID    PrismDID
+	Status uint16
+	Length uint16
+	Data   []byte
+}
+
+func (pv *PrismValue) IsSupplied() bool {
+	return pv.Status == 1
+}
+
+var ErrPrismExpectedMoreData = errors.New("Expected more data.")
+var ErrPrismInvalidCode = errors.New("Invalid header code.")
+
+func decodePrismHeader(data []byte, p gopacket.PacketBuilder) error {
+	d := &PrismHeader{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type PrismHeader struct {
+	BaseLayer
+	Code       uint16
+	Length     uint16
+	DeviceName string
+	Values     []PrismValue
+}
+
+func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader }
+
+func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Code = binary.LittleEndian.Uint16(data[0:4])
+	m.Length = binary.LittleEndian.Uint16(data[4:8])
+	m.DeviceName = string(data[8:24])
+	m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]}
+
+	switch m.Code {
+	case PrismType1MessageCode:
+		fallthrough
+	case PrismType2MessageCode:
+		// valid message code
+	default:
+		return ErrPrismInvalidCode
+	}
+
+	offset := uint16(24)
+
+	m.Values = make([]PrismValue, (m.Length-offset)/12)
+	for i := 0; i < len(m.Values); i++ {
+		decodePrismValue(data[offset:offset+12], &m.Values[i])
+		offset += 12
+	}
+
+	if offset != m.Length {
+		return ErrPrismExpectedMoreData
+	}
+
+	return nil
+}
+
+func (m *PrismHeader) CanDecode() gopacket.LayerClass    { return LayerTypePrismHeader }
+func (m *PrismHeader) NextLayerType() gopacket.LayerType { return LayerTypeDot11 }
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/radiotap.go b/go-controller/vendor/github.com/google/gopacket/layers/radiotap.go
new file mode 100644
index 00000000000..d09559f793e
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/radiotap.go
@@ -0,0 +1,1076 @@
+// Copyright 2014 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"hash/crc32"
+	"strings"
+
+	"github.com/google/gopacket"
+)
+
+// align calculates the number of bytes needed to align with the width
+// on the offset, returning the number of bytes we need to skip to
+// align to the offset (width).
+func align(offset uint16, width uint16) uint16 {
+	return ((((offset) + ((width) - 1)) & (^((width) - 1))) - offset)
+}
+
+type RadioTapPresent uint32
+
+const (
+	RadioTapPresentTSFT RadioTapPresent = 1 << iota
+	RadioTapPresentFlags
+	RadioTapPresentRate
+	RadioTapPresentChannel
+	RadioTapPresentFHSS
+	RadioTapPresentDBMAntennaSignal
+	RadioTapPresentDBMAntennaNoise
+	RadioTapPresentLockQuality
+	RadioTapPresentTxAttenuation
+	RadioTapPresentDBTxAttenuation
+	RadioTapPresentDBMTxPower
+	RadioTapPresentAntenna
+	RadioTapPresentDBAntennaSignal
+	RadioTapPresentDBAntennaNoise
+	RadioTapPresentRxFlags
+	RadioTapPresentTxFlags
+	RadioTapPresentRtsRetries
+	RadioTapPresentDataRetries
+	_
+	RadioTapPresentMCS
+	RadioTapPresentAMPDUStatus
+	RadioTapPresentVHT
+	RadioTapPresentEXT RadioTapPresent = 1 << 31
+)
+
+func (r RadioTapPresent) TSFT() bool {
+	return r&RadioTapPresentTSFT != 0
+}
+func (r RadioTapPresent) Flags() bool {
+	return r&RadioTapPresentFlags != 0
+}
+func (r RadioTapPresent) Rate() bool {
+	return r&RadioTapPresentRate != 0
+}
+func (r RadioTapPresent) Channel() bool {
+	return r&RadioTapPresentChannel != 0
+}
+func (r RadioTapPresent) FHSS() bool {
+	return r&RadioTapPresentFHSS != 0
+}
+func (r RadioTapPresent) DBMAntennaSignal() bool {
+	return r&RadioTapPresentDBMAntennaSignal != 0
+}
+func (r RadioTapPresent) DBMAntennaNoise() bool {
+	return r&RadioTapPresentDBMAntennaNoise != 0
+}
+func (r RadioTapPresent) LockQuality() bool {
+	return r&RadioTapPresentLockQuality != 0
+}
+func (r RadioTapPresent) TxAttenuation() bool {
+	return r&RadioTapPresentTxAttenuation != 0
+}
+func (r RadioTapPresent) DBTxAttenuation() bool {
+	return r&RadioTapPresentDBTxAttenuation != 0
+}
+func (r RadioTapPresent) DBMTxPower() bool {
+	return r&RadioTapPresentDBMTxPower != 0
+}
+func (r RadioTapPresent) Antenna() bool {
+	return r&RadioTapPresentAntenna != 0
+}
+func (r RadioTapPresent) DBAntennaSignal() bool {
+	return r&RadioTapPresentDBAntennaSignal != 0
+}
+func (r RadioTapPresent) DBAntennaNoise() bool {
+	return r&RadioTapPresentDBAntennaNoise != 0
+}
+func (r RadioTapPresent) RxFlags() bool {
+	return r&RadioTapPresentRxFlags != 0
+}
+func (r RadioTapPresent) TxFlags() bool {
+	return r&RadioTapPresentTxFlags != 0
+}
+func (r RadioTapPresent) RtsRetries() bool {
+	return r&RadioTapPresentRtsRetries != 0
+}
+func (r RadioTapPresent) DataRetries() bool {
+	return r&RadioTapPresentDataRetries != 0
+}
+func (r RadioTapPresent) MCS() bool {
+	return r&RadioTapPresentMCS != 0
+}
+func (r RadioTapPresent) AMPDUStatus() bool {
+	return r&RadioTapPresentAMPDUStatus != 0
+}
+func (r RadioTapPresent) VHT() bool {
+	return r&RadioTapPresentVHT != 0
+}
+func (r RadioTapPresent) EXT() bool {
+	return r&RadioTapPresentEXT != 0
+}
+
+type RadioTapChannelFlags uint16
+
+const (
+	RadioTapChannelFlagsTurbo   RadioTapChannelFlags = 0x0010 // Turbo channel
+	RadioTapChannelFlagsCCK     RadioTapChannelFlags = 0x0020 // CCK channel
+	RadioTapChannelFlagsOFDM    RadioTapChannelFlags = 0x0040 // OFDM channel
+	RadioTapChannelFlagsGhz2    RadioTapChannelFlags = 0x0080 // 2 GHz spectrum channel.
+	RadioTapChannelFlagsGhz5    RadioTapChannelFlags = 0x0100 // 5 GHz spectrum channel
+	RadioTapChannelFlagsPassive RadioTapChannelFlags = 0x0200 // Only passive scan allowed
+	RadioTapChannelFlagsDynamic RadioTapChannelFlags = 0x0400 // Dynamic CCK-OFDM channel
+	RadioTapChannelFlagsGFSK    RadioTapChannelFlags = 0x0800 // GFSK channel (FHSS PHY)
+)
+
+func (r RadioTapChannelFlags) Turbo() bool {
+	return r&RadioTapChannelFlagsTurbo != 0
+}
+func (r RadioTapChannelFlags) CCK() bool {
+	return r&RadioTapChannelFlagsCCK != 0
+}
+func (r RadioTapChannelFlags) OFDM() bool {
+	return r&RadioTapChannelFlagsOFDM != 0
+}
+func (r RadioTapChannelFlags) Ghz2() bool {
+	return r&RadioTapChannelFlagsGhz2 != 0
+}
+func (r RadioTapChannelFlags) Ghz5() bool {
+	return r&RadioTapChannelFlagsGhz5 != 0
+}
+func (r RadioTapChannelFlags) Passive() bool {
+	return r&RadioTapChannelFlagsPassive != 0
+}
+func (r RadioTapChannelFlags) Dynamic() bool {
+	return r&RadioTapChannelFlagsDynamic != 0
+}
+func (r RadioTapChannelFlags) GFSK() bool {
+	return r&RadioTapChannelFlagsGFSK != 0
+}
+
+// String provides a human readable string for RadioTapChannelFlags.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the RadioTapChannelFlags value, not its string.
+func (a RadioTapChannelFlags) String() string {
+	var out bytes.Buffer
+	if a.Turbo() {
+		out.WriteString("Turbo,")
+	}
+	if a.CCK() {
+		out.WriteString("CCK,")
+	}
+	if a.OFDM() {
+		out.WriteString("OFDM,")
+	}
+	if a.Ghz2() {
+		out.WriteString("Ghz2,")
+	}
+	if a.Ghz5() {
+		out.WriteString("Ghz5,")
+	}
+	if a.Passive() {
+		out.WriteString("Passive,")
+	}
+	if a.Dynamic() {
+		out.WriteString("Dynamic,")
+	}
+	if a.GFSK() {
+		out.WriteString("GFSK,")
+	}
+
+	if length := out.Len(); length > 0 {
+		return string(out.Bytes()[:length-1]) // strip final comma
+	}
+	return ""
+}
+
+type RadioTapFlags uint8
+
+const (
+	RadioTapFlagsCFP           RadioTapFlags = 1 << iota // sent/received during CFP
+	RadioTapFlagsShortPreamble                           // sent/received * with short * preamble
+	RadioTapFlagsWEP                                     // sent/received * with WEP encryption
+	RadioTapFlagsFrag                                    // sent/received * with fragmentation
+	RadioTapFlagsFCS                                     // frame includes FCS
+	RadioTapFlagsDatapad                                 // frame has padding between * 802.11 header and payload * (to 32-bit boundary)
+	RadioTapFlagsBadFCS                                  // does not pass FCS check
+	RadioTapFlagsShortGI                                 // HT short GI
+)
+
+func (r RadioTapFlags) CFP() bool {
+	return r&RadioTapFlagsCFP != 0
+}
+func (r RadioTapFlags) ShortPreamble() bool {
+	return r&RadioTapFlagsShortPreamble != 0
+}
+func (r RadioTapFlags) WEP() bool {
+	return r&RadioTapFlagsWEP != 0
+}
+func (r RadioTapFlags) Frag() bool {
+	return r&RadioTapFlagsFrag != 0
+}
+func (r RadioTapFlags) FCS() bool {
+	return r&RadioTapFlagsFCS != 0
+}
+func (r RadioTapFlags) Datapad() bool {
+	return r&RadioTapFlagsDatapad != 0
+}
+func (r RadioTapFlags) BadFCS() bool {
+	return r&RadioTapFlagsBadFCS != 0
+}
+func (r RadioTapFlags) ShortGI() bool {
+	return r&RadioTapFlagsShortGI != 0
+}
+
+// String provides a human readable string for RadioTapFlags.
+// This string is possibly subject to change over time; if you're storing this
+// persistently, you should probably store the RadioTapFlags value, not its string.
+func (a RadioTapFlags) String() string {
+	var out bytes.Buffer
+	if a.CFP() {
+		out.WriteString("CFP,")
+	}
+	if a.ShortPreamble() {
+		out.WriteString("SHORT-PREAMBLE,")
+	}
+	if a.WEP() {
+		out.WriteString("WEP,")
+	}
+	if a.Frag() {
+		out.WriteString("FRAG,")
+	}
+	if a.FCS() {
+		out.WriteString("FCS,")
+	}
+	if a.Datapad() {
+		out.WriteString("DATAPAD,")
+	}
+	if a.ShortGI() {
+		out.WriteString("SHORT-GI,")
+	}
+
+	if length := out.Len(); length > 0 {
+		return string(out.Bytes()[:length-1]) // strip final comma
+	}
+	return ""
+}
+
+type RadioTapRate uint8
+
+func (a RadioTapRate) String() string {
+	return fmt.Sprintf("%v Mb/s", 0.5*float32(a))
+}
+
+type RadioTapChannelFrequency uint16
+
+func (a RadioTapChannelFrequency) String() string {
+	return fmt.Sprintf("%d MHz", a)
+}
+
+type RadioTapRxFlags uint16
+
+const (
+	RadioTapRxFlagsBadPlcp RadioTapRxFlags = 0x0002
+)
+
+func (self RadioTapRxFlags) BadPlcp() bool {
+	return self&RadioTapRxFlagsBadPlcp != 0
+}
+
+func (self RadioTapRxFlags) String() string {
+	if self.BadPlcp() {
+		return "BADPLCP"
+	}
+	return ""
+}
+
+type RadioTapTxFlags uint16
+
+const (
+	RadioTapTxFlagsFail RadioTapTxFlags = 1 << iota
+	RadioTapTxFlagsCTS
+	RadioTapTxFlagsRTS
+	RadioTapTxFlagsNoACK
+)
+
+func (self RadioTapTxFlags) Fail() bool  { return self&RadioTapTxFlagsFail != 0 }
+func (self RadioTapTxFlags) CTS() bool   { return self&RadioTapTxFlagsCTS != 0 }
+func (self RadioTapTxFlags) RTS() bool   { return self&RadioTapTxFlagsRTS != 0 }
+func (self RadioTapTxFlags) NoACK() bool { return self&RadioTapTxFlagsNoACK != 0 }
+
+func (self RadioTapTxFlags) String() string {
+	var tokens []string
+	if self.Fail() {
+		tokens = append(tokens, "Fail")
+	}
+	if self.CTS() {
+		tokens = append(tokens, "CTS")
+	}
+	if self.RTS() {
+		tokens = append(tokens, "RTS")
+	}
+	if self.NoACK() {
+		tokens = append(tokens, "NoACK")
+	}
+	return strings.Join(tokens, ",")
+}
+
+type RadioTapMCS struct {
+	Known RadioTapMCSKnown
+	Flags RadioTapMCSFlags
+	MCS   uint8
+}
+
+func (self RadioTapMCS) String() string {
+	var tokens []string
+	if self.Known.Bandwidth() {
+		token := "?"
+		switch self.Flags.Bandwidth() {
+		case 0:
+			token = "20"
+		case 1:
+			token = "40"
+		case 2:
+			token = "40(20L)"
+		case 3:
+			token = "40(20U)"
+		}
+		tokens = append(tokens, token)
+	}
+	if self.Known.MCSIndex() {
+		tokens = append(tokens, fmt.Sprintf("MCSIndex#%d", self.MCS))
+	}
+	if self.Known.GuardInterval() {
+		if self.Flags.ShortGI() {
+			tokens = append(tokens, fmt.Sprintf("shortGI"))
+		} else {
+			tokens = append(tokens, fmt.Sprintf("longGI"))
+		}
+	}
+	if self.Known.HTFormat() {
+		if self.Flags.Greenfield() {
+			tokens = append(tokens, fmt.Sprintf("HT-greenfield"))
+		} else {
+			tokens = append(tokens, fmt.Sprintf("HT-mixed"))
+		}
+	}
+	if self.Known.FECType() {
+		if self.Flags.FECLDPC() {
+			tokens = append(tokens, fmt.Sprintf("LDPC"))
+		} else {
+			tokens = append(tokens, fmt.Sprintf("BCC"))
+		}
+	}
+	if self.Known.STBC() {
+		tokens = append(tokens, fmt.Sprintf("STBC#%d", self.Flags.STBC()))
+	}
+	if self.Known.NESS() {
+		num := 0
+		if self.Known.NESS1() {
+			num |= 0x02
+		}
+		if self.Flags.NESS0() {
+			num |= 0x01
+		}
+		tokens = append(tokens, fmt.Sprintf("num-of-ESS#%d", num))
+	}
+	return strings.Join(tokens, ",")
+}
+
+type RadioTapMCSKnown uint8
+
+const (
+	RadioTapMCSKnownBandwidth RadioTapMCSKnown = 1 << iota
+	RadioTapMCSKnownMCSIndex
+	RadioTapMCSKnownGuardInterval
+	RadioTapMCSKnownHTFormat
+	RadioTapMCSKnownFECType
+	RadioTapMCSKnownSTBC
+	RadioTapMCSKnownNESS
+	RadioTapMCSKnownNESS1
+)
+
+func (self RadioTapMCSKnown) Bandwidth() bool     { return self&RadioTapMCSKnownBandwidth != 0 }
+func (self RadioTapMCSKnown) MCSIndex() bool      { return self&RadioTapMCSKnownMCSIndex != 0 }
+func (self RadioTapMCSKnown) GuardInterval() bool { return self&RadioTapMCSKnownGuardInterval != 0 }
+func (self RadioTapMCSKnown) HTFormat() bool      { return self&RadioTapMCSKnownHTFormat != 0 }
+func (self RadioTapMCSKnown) FECType() bool       { return self&RadioTapMCSKnownFECType != 0 }
+func (self RadioTapMCSKnown) STBC() bool          { return self&RadioTapMCSKnownSTBC != 0 }
+func (self RadioTapMCSKnown) NESS() bool          { return self&RadioTapMCSKnownNESS != 0 }
+func (self RadioTapMCSKnown) NESS1() bool         { return self&RadioTapMCSKnownNESS1 != 0 }
+
+type RadioTapMCSFlags uint8
+
+const (
+	RadioTapMCSFlagsBandwidthMask RadioTapMCSFlags = 0x03
+	RadioTapMCSFlagsShortGI                        = 0x04
+	RadioTapMCSFlagsGreenfield                     = 0x08
+	RadioTapMCSFlagsFECLDPC                        = 0x10
+	RadioTapMCSFlagsSTBCMask                       = 0x60
+	RadioTapMCSFlagsNESS0                          = 0x80
+)
+
+func (self RadioTapMCSFlags) Bandwidth() int {
+	return int(self & RadioTapMCSFlagsBandwidthMask)
+}
+func (self RadioTapMCSFlags) ShortGI() bool    { return self&RadioTapMCSFlagsShortGI != 0 }
+func (self RadioTapMCSFlags) Greenfield() bool { return self&RadioTapMCSFlagsGreenfield != 0 }
+func (self RadioTapMCSFlags) FECLDPC() bool    { return self&RadioTapMCSFlagsFECLDPC != 0 }
+func (self RadioTapMCSFlags) STBC() int {
+	return int(self&RadioTapMCSFlagsSTBCMask) >> 5
+}
+func (self RadioTapMCSFlags) NESS0() bool { return self&RadioTapMCSFlagsNESS0 != 0 }
+
+type RadioTapAMPDUStatus struct {
+	Reference uint32
+	Flags     RadioTapAMPDUStatusFlags
+	CRC       uint8
+}
+
+func (self RadioTapAMPDUStatus) String() string {
+	tokens := []string{
+		fmt.Sprintf("ref#%x", self.Reference),
+	}
+	if self.Flags.ReportZerolen() && self.Flags.IsZerolen() {
+		tokens = append(tokens, fmt.Sprintf("zero-length"))
+	}
+	if self.Flags.LastKnown() && self.Flags.IsLast() {
+		tokens = append(tokens, "last")
+	}
+	if self.Flags.DelimCRCErr() {
+		tokens = append(tokens, "delimiter CRC error")
+	}
+	if self.Flags.DelimCRCKnown() {
+		tokens = append(tokens, fmt.Sprintf("delimiter-CRC=%02x", self.CRC))
+	}
+	return strings.Join(tokens, ",")
+}
+
+type RadioTapAMPDUStatusFlags uint16
+
+const (
+	RadioTapAMPDUStatusFlagsReportZerolen RadioTapAMPDUStatusFlags = 1 << iota
+	RadioTapAMPDUIsZerolen
+	RadioTapAMPDULastKnown
+	RadioTapAMPDUIsLast
+	RadioTapAMPDUDelimCRCErr
+	RadioTapAMPDUDelimCRCKnown
+)
+
+func (self RadioTapAMPDUStatusFlags) ReportZerolen() bool {
+	return self&RadioTapAMPDUStatusFlagsReportZerolen != 0
+}
+func (self RadioTapAMPDUStatusFlags) IsZerolen() bool   { return self&RadioTapAMPDUIsZerolen != 0 }
+func (self RadioTapAMPDUStatusFlags) LastKnown() bool   { return self&RadioTapAMPDULastKnown != 0 }
+func (self RadioTapAMPDUStatusFlags) IsLast() bool      { return self&RadioTapAMPDUIsLast != 0 }
+func (self RadioTapAMPDUStatusFlags) DelimCRCErr() bool { return self&RadioTapAMPDUDelimCRCErr != 0 }
+func (self RadioTapAMPDUStatusFlags) DelimCRCKnown() bool {
+	return self&RadioTapAMPDUDelimCRCKnown != 0
+}
+
+type RadioTapVHT struct {
+	Known      RadioTapVHTKnown
+	Flags      RadioTapVHTFlags
+	Bandwidth  uint8
+	MCSNSS     [4]RadioTapVHTMCSNSS
+	Coding     uint8
+	GroupId    uint8
+	PartialAID uint16
+}
+
+func (self RadioTapVHT) String() string {
+	var tokens []string
+	if self.Known.STBC() {
+		if self.Flags.STBC() {
+			tokens = append(tokens, "STBC")
+		} else {
+			tokens = append(tokens, "no STBC")
+		}
+	}
+	if self.Known.TXOPPSNotAllowed() {
+		if self.Flags.TXOPPSNotAllowed() {
+			tokens = append(tokens, "TXOP doze not allowed")
+		} else {
+			tokens = append(tokens, "TXOP doze allowed")
+		}
+	}
+	if self.Known.GI() {
+		if self.Flags.SGI() {
+			tokens = append(tokens, "short GI")
+		} else {
+			tokens = append(tokens, "long GI")
+		}
+	}
+	if self.Known.SGINSYMDisambiguation() {
+		if self.Flags.SGINSYMMod() {
+			tokens = append(tokens, "NSYM mod 10=9")
+		} else {
+			tokens = append(tokens, "NSYM mod 10!=9 or no short GI")
+		}
+	}
+	if self.Known.LDPCExtraOFDMSymbol() {
+		if self.Flags.LDPCExtraOFDMSymbol() {
+			tokens = append(tokens, "LDPC extra OFDM symbols")
+		} else {
+			tokens = append(tokens, "no LDPC extra OFDM symbols")
+		}
+	}
+	if self.Known.Beamformed() {
+		if self.Flags.Beamformed() {
+			tokens = append(tokens, "beamformed")
+		} else {
+			tokens = append(tokens, "no beamformed")
+		}
+	}
+	if self.Known.Bandwidth() {
+		token := "?"
+		switch self.Bandwidth & 0x1f {
+		case 0:
+			token = "20"
+		case 1:
+			token = "40"
+		case 2:
+			token = "40(20L)"
+		case 3:
+			token = "40(20U)"
+		case 4:
+			token = "80"
+		case 5:
+			token = "80(40L)"
+		case 6:
+			token = "80(40U)"
+		case 7:
+			token = "80(20LL)"
+		case 8:
+			token = "80(20LU)"
+		case 9:
+			token = "80(20UL)"
+		case 10:
+			token = "80(20UU)"
+		case 11:
+			token = "160"
+		case 12:
+			token = "160(80L)"
+		case 13:
+			token = "160(80U)"
+		case 14:
+			token = "160(40LL)"
+		case 15:
+			token = "160(40LU)"
+		case 16:
+			token = "160(40UL)"
+		case 17:
+			token = "160(40UU)"
+		case 18:
+			token = "160(20LLL)"
+		case 19:
+			token = "160(20LLU)"
+		case 20:
+			token = "160(20LUL)"
+		case 21:
+			token = "160(20LUU)"
+		case 22:
+			token = "160(20ULL)"
+		case 23:
+			token = "160(20ULU)"
+		case 24:
+			token = "160(20UUL)"
+		case 25:
+			token = "160(20UUU)"
+		}
+		tokens = append(tokens, token)
+	}
+	for i, MCSNSS := range self.MCSNSS {
+		if MCSNSS.Present() {
+			fec := "?"
+			switch self.Coding & (1 << uint8(i)) {
+			case 0:
+				fec = "BCC"
+			case 1:
+				fec = "LDPC"
+			}
+			tokens = append(tokens, fmt.Sprintf("user%d(%s,%s)", i, MCSNSS.String(), fec))
+		}
+	}
+	if self.Known.GroupId() {
+		tokens = append(tokens,
+			fmt.Sprintf("group=%d", self.GroupId))
+	}
+	if self.Known.PartialAID() {
+		tokens = append(tokens,
+			fmt.Sprintf("partial-AID=%d", self.PartialAID))
+	}
+	return strings.Join(tokens, ",")
+}
+
+type RadioTapVHTKnown uint16
+
+const (
+	RadioTapVHTKnownSTBC RadioTapVHTKnown = 1 << iota
+	RadioTapVHTKnownTXOPPSNotAllowed
+	RadioTapVHTKnownGI
+	RadioTapVHTKnownSGINSYMDisambiguation
+	RadioTapVHTKnownLDPCExtraOFDMSymbol
+	RadioTapVHTKnownBeamformed
+	RadioTapVHTKnownBandwidth
+	RadioTapVHTKnownGroupId
+	RadioTapVHTKnownPartialAID
+)
+
+func (self RadioTapVHTKnown) STBC() bool { return self&RadioTapVHTKnownSTBC != 0 }
+func (self RadioTapVHTKnown) TXOPPSNotAllowed() bool {
+	return self&RadioTapVHTKnownTXOPPSNotAllowed != 0
+}
+func (self RadioTapVHTKnown) GI() bool { return self&RadioTapVHTKnownGI != 0 }
+func (self RadioTapVHTKnown) SGINSYMDisambiguation() bool {
+	return self&RadioTapVHTKnownSGINSYMDisambiguation != 0
+}
+func (self RadioTapVHTKnown) LDPCExtraOFDMSymbol() bool {
+	return self&RadioTapVHTKnownLDPCExtraOFDMSymbol != 0
+}
+func (self RadioTapVHTKnown) Beamformed() bool { return self&RadioTapVHTKnownBeamformed != 0 }
+func (self RadioTapVHTKnown) Bandwidth() bool  { return self&RadioTapVHTKnownBandwidth != 0 }
+func (self RadioTapVHTKnown) GroupId() bool    { return self&RadioTapVHTKnownGroupId != 0 }
+func (self RadioTapVHTKnown) PartialAID() bool { return self&RadioTapVHTKnownPartialAID != 0 }
+
+type RadioTapVHTFlags uint8
+
+const (
+	RadioTapVHTFlagsSTBC RadioTapVHTFlags = 1 << iota
+	RadioTapVHTFlagsTXOPPSNotAllowed
+	RadioTapVHTFlagsSGI
+	RadioTapVHTFlagsSGINSYMMod
+	RadioTapVHTFlagsLDPCExtraOFDMSymbol
+	RadioTapVHTFlagsBeamformed
+)
+
+func (self RadioTapVHTFlags) STBC() bool { return self&RadioTapVHTFlagsSTBC != 0 }
+func (self RadioTapVHTFlags) TXOPPSNotAllowed() bool {
+	return self&RadioTapVHTFlagsTXOPPSNotAllowed != 0
+}
+func (self RadioTapVHTFlags) SGI() bool        { return self&RadioTapVHTFlagsSGI != 0 }
+func (self RadioTapVHTFlags) SGINSYMMod() bool { return self&RadioTapVHTFlagsSGINSYMMod != 0 }
+func (self RadioTapVHTFlags) LDPCExtraOFDMSymbol() bool {
+	return self&RadioTapVHTFlagsLDPCExtraOFDMSymbol != 0
+}
+func (self RadioTapVHTFlags) Beamformed() bool { return self&RadioTapVHTFlagsBeamformed != 0 }
+
+type RadioTapVHTMCSNSS uint8
+
+func (self RadioTapVHTMCSNSS) Present() bool {
+	return self&0x0F != 0
+}
+
+func (self RadioTapVHTMCSNSS) String() string {
+	return fmt.Sprintf("NSS#%dMCS#%d", uint32(self&0xf), uint32(self>>4))
+}
+
+func decodeRadioTap(data []byte, p gopacket.PacketBuilder) error {
+	d := &RadioTap{}
+	// TODO: Should we set LinkLayer here? And implement LinkFlow
+	return decodingLayerDecoder(d, data, p)
+}
+
+type RadioTap struct {
+	BaseLayer
+
+	// Version 0. Only increases for drastic changes, introduction of compatible new fields does not count.
+	Version uint8
+	// Length of the whole header in bytes, including it_version, it_pad, it_len, and data fields.
+	Length uint16
+	// Present is a bitmap telling which fields are present. Set bit 31 (0x80000000) to extend the bitmap by another 32 bits. Additional extensions are made by setting bit 31.
+	Present RadioTapPresent
+	// TSFT: value in microseconds of the MAC's 64-bit 802.11 Time Synchronization Function timer when the first bit of the MPDU arrived at the MAC. For received frames, only.
+	TSFT  uint64
+	Flags RadioTapFlags
+	// Rate Tx/Rx data rate
+	Rate RadioTapRate
+	// ChannelFrequency Tx/Rx frequency in MHz, followed by flags
+	ChannelFrequency RadioTapChannelFrequency
+	ChannelFlags     RadioTapChannelFlags
+	// FHSS For frequency-hopping radios, the hop set (first byte) and pattern (second byte).
+	FHSS uint16
+	// DBMAntennaSignal RF signal power at the antenna, decibel difference from one milliwatt.
+	DBMAntennaSignal int8
+	// DBMAntennaNoise RF noise power at the antenna, decibel difference from one milliwatt.
+	DBMAntennaNoise int8
+	// LockQuality Quality of Barker code lock. Unitless. Monotonically nondecreasing with "better" lock strength. Called "Signal Quality" in datasheets.
+	LockQuality uint16
+	// TxAttenuation Transmit power expressed as unitless distance from max power set at factory calibration.  0 is max power. Monotonically nondecreasing with lower power levels.
+	TxAttenuation uint16
+	// DBTxAttenuation Transmit power expressed as decibel distance from max power set at factory calibration.  0 is max power.  Monotonically nondecreasing with lower power levels.
+	DBTxAttenuation uint16
+	// DBMTxPower Transmit power expressed as dBm (decibels from a 1 milliwatt reference). This is the absolute power level measured at the antenna port.
+	DBMTxPower int8
+	// Antenna Unitless indication of the Rx/Tx antenna for this packet. The first antenna is antenna 0.
+	Antenna uint8
+	// DBAntennaSignal RF signal power at the antenna, decibel difference from an arbitrary, fixed reference.
+	DBAntennaSignal uint8
+	// DBAntennaNoise RF noise power at the antenna, decibel difference from an arbitrary, fixed reference point.
+	DBAntennaNoise uint8
+	//
+	RxFlags     RadioTapRxFlags
+	TxFlags     RadioTapTxFlags
+	RtsRetries  uint8
+	DataRetries uint8
+	MCS         RadioTapMCS
+	AMPDUStatus RadioTapAMPDUStatus
+	VHT         RadioTapVHT
+}
+
+func (m *RadioTap) LayerType() gopacket.LayerType { return LayerTypeRadioTap }
+
+func (m *RadioTap) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return errors.New("RadioTap too small")
+	}
+	m.Version = uint8(data[0])
+	m.Length = binary.LittleEndian.Uint16(data[2:4])
+	m.Present = RadioTapPresent(binary.LittleEndian.Uint32(data[4:8]))
+
+	offset := uint16(4)
+
+	for (binary.LittleEndian.Uint32(data[offset:offset+4]) & 0x80000000) != 0 {
+		// This parser only handles standard radiotap namespace,
+		// and expects all fields are packed in the first it_present.
+		// Extended bitmap will be just ignored.
+		offset += 4
+	}
+	offset += 4 // skip the bitmap
+
+	if m.Present.TSFT() {
+		offset += align(offset, 8)
+		m.TSFT = binary.LittleEndian.Uint64(data[offset : offset+8])
+		offset += 8
+	}
+	if m.Present.Flags() {
+		m.Flags = RadioTapFlags(data[offset])
+		offset++
+	}
+	if m.Present.Rate() {
+		m.Rate = RadioTapRate(data[offset])
+		offset++
+	}
+	if m.Present.Channel() {
+		offset += align(offset, 2)
+		m.ChannelFrequency = RadioTapChannelFrequency(binary.LittleEndian.Uint16(data[offset : offset+2]))
+		offset += 2
+		m.ChannelFlags = RadioTapChannelFlags(binary.LittleEndian.Uint16(data[offset : offset+2]))
+		offset += 2
+	}
+	if m.Present.FHSS() {
+		m.FHSS = binary.LittleEndian.Uint16(data[offset : offset+2])
+		offset += 2
+	}
+	if m.Present.DBMAntennaSignal() {
+		m.DBMAntennaSignal = int8(data[offset])
+		offset++
+	}
+	if m.Present.DBMAntennaNoise() {
+		m.DBMAntennaNoise = int8(data[offset])
+		offset++
+	}
+	if m.Present.LockQuality() {
+		offset += align(offset, 2)
+		m.LockQuality = binary.LittleEndian.Uint16(data[offset : offset+2])
+		offset += 2
+	}
+	if m.Present.TxAttenuation() {
+		offset += align(offset, 2)
+		m.TxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2])
+		offset += 2
+	}
+	if m.Present.DBTxAttenuation() {
+		offset += align(offset, 2)
+		m.DBTxAttenuation = binary.LittleEndian.Uint16(data[offset : offset+2])
+		offset += 2
+	}
+	if m.Present.DBMTxPower() {
+		m.DBMTxPower = int8(data[offset])
+		offset++
+	}
+	if m.Present.Antenna() {
+		m.Antenna = uint8(data[offset])
+		offset++
+	}
+	if m.Present.DBAntennaSignal() {
+		m.DBAntennaSignal = uint8(data[offset])
+		offset++
+	}
+	if m.Present.DBAntennaNoise() {
+		m.DBAntennaNoise = uint8(data[offset])
+		offset++
+	}
+	if m.Present.RxFlags() {
+		offset += align(offset, 2)
+		m.RxFlags = RadioTapRxFlags(binary.LittleEndian.Uint16(data[offset:]))
+		offset += 2
+	}
+	if m.Present.TxFlags() {
+		offset += align(offset, 2)
+		m.TxFlags = RadioTapTxFlags(binary.LittleEndian.Uint16(data[offset:]))
+		offset += 2
+	}
+	if m.Present.RtsRetries() {
+		m.RtsRetries = uint8(data[offset])
+		offset++
+	}
+	if m.Present.DataRetries() {
+		m.DataRetries = uint8(data[offset])
+		offset++
+	}
+	if m.Present.MCS() {
+		m.MCS = RadioTapMCS{
+			RadioTapMCSKnown(data[offset]),
+			RadioTapMCSFlags(data[offset+1]),
+			uint8(data[offset+2]),
+		}
+		offset += 3
+	}
+	if m.Present.AMPDUStatus() {
+		offset += align(offset, 4)
+		m.AMPDUStatus = RadioTapAMPDUStatus{
+			Reference: binary.LittleEndian.Uint32(data[offset:]),
+			Flags:     RadioTapAMPDUStatusFlags(binary.LittleEndian.Uint16(data[offset+4:])),
+			CRC:       uint8(data[offset+6]),
+		}
+		offset += 8
+	}
+	if m.Present.VHT() {
+		offset += align(offset, 2)
+		m.VHT = RadioTapVHT{
+			Known:     RadioTapVHTKnown(binary.LittleEndian.Uint16(data[offset:])),
+			Flags:     RadioTapVHTFlags(data[offset+2]),
+			Bandwidth: uint8(data[offset+3]),
+			MCSNSS: [4]RadioTapVHTMCSNSS{
+				RadioTapVHTMCSNSS(data[offset+4]),
+				RadioTapVHTMCSNSS(data[offset+5]),
+				RadioTapVHTMCSNSS(data[offset+6]),
+				RadioTapVHTMCSNSS(data[offset+7]),
+			},
+			Coding:     uint8(data[offset+8]),
+			GroupId:    uint8(data[offset+9]),
+			PartialAID: binary.LittleEndian.Uint16(data[offset+10:]),
+		}
+		offset += 12
+	}
+
+	payload := data[m.Length:]
+
+	// Remove non standard padding used by some Wi-Fi drivers
+	if m.Flags.Datapad() &&
+		payload[0]&0xC == 0x8 { //&& // Data frame
+		headlen := 24
+		if payload[0]&0x8C == 0x88 { // QoS
+			headlen += 2
+		}
+		if payload[1]&0x3 == 0x3 { // 4 addresses
+			headlen += 2
+		}
+		if headlen%4 == 2 {
+			payload = append(payload[:headlen], payload[headlen+2:len(payload)]...)
+		}
+	}
+
+	if !m.Flags.FCS() {
+		// Dot11.DecodeFromBytes() expects FCS present and performs a hard chop on the checksum
+		// If a user is handing in subslices or packets from a buffered stream, the capacity of the slice
+		// may extend beyond the len, rather than expecting callers to enforce cap==len on every packet
+		// we take the hit in this one case and do a reallocation.  If the user DOES enforce cap==len
+		// then the reallocation will happen anyway on the append.  This is requried because the append
+		// write to the memory directly after the payload if there is sufficient capacity, which callers
+		// may not expect.
+		reallocPayload := make([]byte, len(payload)+4)
+		copy(reallocPayload[0:len(payload)], payload)
+		h := crc32.NewIEEE()
+		h.Write(payload)
+		binary.LittleEndian.PutUint32(reallocPayload[len(payload):], h.Sum32())
+		payload = reallocPayload
+	}
+	m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: payload}
+
+	return nil
+}
+
+func (m RadioTap) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	buf := make([]byte, 1024)
+
+	buf[0] = m.Version
+	buf[1] = 0
+
+	binary.LittleEndian.PutUint32(buf[4:8], uint32(m.Present))
+
+	offset := uint16(4)
+
+	for (binary.LittleEndian.Uint32(buf[offset:offset+4]) & 0x80000000) != 0 {
+		offset += 4
+	}
+
+	offset += 4
+
+	if m.Present.TSFT() {
+		offset += align(offset, 8)
+		binary.LittleEndian.PutUint64(buf[offset:offset+8], m.TSFT)
+		offset += 8
+	}
+
+	if m.Present.Flags() {
+		buf[offset] = uint8(m.Flags)
+		offset++
+	}
+
+	if m.Present.Rate() {
+		buf[offset] = uint8(m.Rate)
+		offset++
+	}
+
+	if m.Present.Channel() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFrequency))
+		offset += 2
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.ChannelFlags))
+		offset += 2
+	}
+
+	if m.Present.FHSS() {
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], m.FHSS)
+		offset += 2
+	}
+
+	if m.Present.DBMAntennaSignal() {
+		buf[offset] = byte(m.DBMAntennaSignal)
+		offset++
+	}
+
+	if m.Present.DBMAntennaNoise() {
+		buf[offset] = byte(m.DBMAntennaNoise)
+		offset++
+	}
+
+	if m.Present.LockQuality() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], m.LockQuality)
+		offset += 2
+	}
+
+	if m.Present.TxAttenuation() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], m.TxAttenuation)
+		offset += 2
+	}
+
+	if m.Present.DBTxAttenuation() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], m.DBTxAttenuation)
+		offset += 2
+	}
+
+	if m.Present.DBMTxPower() {
+		buf[offset] = byte(m.DBMTxPower)
+		offset++
+	}
+
+	if m.Present.Antenna() {
+		buf[offset] = uint8(m.Antenna)
+		offset++
+	}
+
+	if m.Present.DBAntennaSignal() {
+		buf[offset] = uint8(m.DBAntennaSignal)
+		offset++
+	}
+
+	if m.Present.DBAntennaNoise() {
+		buf[offset] = uint8(m.DBAntennaNoise)
+		offset++
+	}
+
+	if m.Present.RxFlags() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.RxFlags))
+		offset += 2
+	}
+
+	if m.Present.TxFlags() {
+		offset += align(offset, 2)
+		binary.LittleEndian.PutUint16(buf[offset:offset+2], uint16(m.TxFlags))
+		offset += 2
+	}
+
+	if m.Present.RtsRetries() {
+		buf[offset] = m.RtsRetries
+		offset++
+	}
+
+	if m.Present.DataRetries() {
+		buf[offset] = m.DataRetries
+		offset++
+	}
+
+	if m.Present.MCS() {
+		buf[offset] = uint8(m.MCS.Known)
+		buf[offset+1] = uint8(m.MCS.Flags)
+		buf[offset+2] = uint8(m.MCS.MCS)
+
+		offset += 3
+	}
+
+	if m.Present.AMPDUStatus() {
+		offset += align(offset, 4)
+
+		binary.LittleEndian.PutUint32(buf[offset:offset+4], m.AMPDUStatus.Reference)
+		binary.LittleEndian.PutUint16(buf[offset+4:offset+6], uint16(m.AMPDUStatus.Flags))
+
+		buf[offset+6] = m.AMPDUStatus.CRC
+
+		offset += 8
+	}
+
+	if m.Present.VHT() {
+		offset += align(offset, 2)
+
+		binary.LittleEndian.PutUint16(buf[offset:], uint16(m.VHT.Known))
+
+		buf[offset+2] = uint8(m.VHT.Flags)
+		buf[offset+3] = uint8(m.VHT.Bandwidth)
+		buf[offset+4] = uint8(m.VHT.MCSNSS[0])
+		buf[offset+5] = uint8(m.VHT.MCSNSS[1])
+		buf[offset+6] = uint8(m.VHT.MCSNSS[2])
+		buf[offset+7] = uint8(m.VHT.MCSNSS[3])
+		buf[offset+8] = uint8(m.VHT.Coding)
+		buf[offset+9] = uint8(m.VHT.GroupId)
+
+		binary.LittleEndian.PutUint16(buf[offset+10:offset+12], m.VHT.PartialAID)
+
+		offset += 12
+	}
+
+	packetBuf, err := b.PrependBytes(int(offset))
+
+	if err != nil {
+		return err
+	}
+
+	if opts.FixLengths {
+		m.Length = offset
+	}
+
+	binary.LittleEndian.PutUint16(buf[2:4], m.Length)
+
+	copy(packetBuf, buf)
+
+	return nil
+}
+
+func (m *RadioTap) CanDecode() gopacket.LayerClass    { return LayerTypeRadioTap }
+func (m *RadioTap) NextLayerType() gopacket.LayerType { return LayerTypeDot11 }
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/radius.go b/go-controller/vendor/github.com/google/gopacket/layers/radius.go
new file mode 100644
index 00000000000..c43ea294516
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/radius.go
@@ -0,0 +1,560 @@
+// Copyright 2020 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 (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+const (
+	// RFC 2865 3.  Packet Format
+	// `The minimum length is 20 and maximum length is 4096.`
+	radiusMinimumRecordSizeInBytes int = 20
+	radiusMaximumRecordSizeInBytes int = 4096
+
+	// RFC 2865 5.  Attributes
+	// `The Length field is one octet, and indicates the length of this Attribute including the Type, Length and Value fields.`
+	// `The Value field is zero or more octets and contains information specific to the Attribute.`
+	radiusAttributesMinimumRecordSizeInBytes int = 2
+)
+
+// RADIUS represents a Remote Authentication Dial In User Service layer.
+type RADIUS struct {
+	BaseLayer
+
+	Code          RADIUSCode
+	Identifier    RADIUSIdentifier
+	Length        RADIUSLength
+	Authenticator RADIUSAuthenticator
+	Attributes    []RADIUSAttribute
+}
+
+// RADIUSCode represents packet type.
+type RADIUSCode uint8
+
+// constants that define RADIUSCode.
+const (
+	RADIUSCodeAccessRequest      RADIUSCode = 1   // RFC2865 3.  Packet Format
+	RADIUSCodeAccessAccept       RADIUSCode = 2   // RFC2865 3.  Packet Format
+	RADIUSCodeAccessReject       RADIUSCode = 3   // RFC2865 3.  Packet Format
+	RADIUSCodeAccountingRequest  RADIUSCode = 4   // RFC2865 3.  Packet Format
+	RADIUSCodeAccountingResponse RADIUSCode = 5   // RFC2865 3.  Packet Format
+	RADIUSCodeAccessChallenge    RADIUSCode = 11  // RFC2865 3.  Packet Format
+	RADIUSCodeStatusServer       RADIUSCode = 12  // RFC2865 3.  Packet Format (experimental)
+	RADIUSCodeStatusClient       RADIUSCode = 13  // RFC2865 3.  Packet Format (experimental)
+	RADIUSCodeReserved           RADIUSCode = 255 // RFC2865 3.  Packet Format
+)
+
+// String returns a string version of a RADIUSCode.
+func (t RADIUSCode) String() (s string) {
+	switch t {
+	case RADIUSCodeAccessRequest:
+		s = "Access-Request"
+	case RADIUSCodeAccessAccept:
+		s = "Access-Accept"
+	case RADIUSCodeAccessReject:
+		s = "Access-Reject"
+	case RADIUSCodeAccountingRequest:
+		s = "Accounting-Request"
+	case RADIUSCodeAccountingResponse:
+		s = "Accounting-Response"
+	case RADIUSCodeAccessChallenge:
+		s = "Access-Challenge"
+	case RADIUSCodeStatusServer:
+		s = "Status-Server"
+	case RADIUSCodeStatusClient:
+		s = "Status-Client"
+	case RADIUSCodeReserved:
+		s = "Reserved"
+	default:
+		s = fmt.Sprintf("Unknown(%d)", t)
+	}
+	return
+}
+
+// RADIUSIdentifier represents packet identifier.
+type RADIUSIdentifier uint8
+
+// RADIUSLength represents packet length.
+type RADIUSLength uint16
+
+// RADIUSAuthenticator represents authenticator.
+type RADIUSAuthenticator [16]byte
+
+// RADIUSAttribute represents attributes.
+type RADIUSAttribute struct {
+	Type   RADIUSAttributeType
+	Length RADIUSAttributeLength
+	Value  RADIUSAttributeValue
+}
+
+// RADIUSAttributeType represents attribute type.
+type RADIUSAttributeType uint8
+
+// constants that define RADIUSAttributeType.
+const (
+	RADIUSAttributeTypeUserName               RADIUSAttributeType = 1  // RFC2865  5.1.  User-Name
+	RADIUSAttributeTypeUserPassword           RADIUSAttributeType = 2  // RFC2865  5.2.  User-Password
+	RADIUSAttributeTypeCHAPPassword           RADIUSAttributeType = 3  // RFC2865  5.3.  CHAP-Password
+	RADIUSAttributeTypeNASIPAddress           RADIUSAttributeType = 4  // RFC2865  5.4.  NAS-IP-Address
+	RADIUSAttributeTypeNASPort                RADIUSAttributeType = 5  // RFC2865  5.5.  NAS-Port
+	RADIUSAttributeTypeServiceType            RADIUSAttributeType = 6  // RFC2865  5.6.  Service-Type
+	RADIUSAttributeTypeFramedProtocol         RADIUSAttributeType = 7  // RFC2865  5.7.  Framed-Protocol
+	RADIUSAttributeTypeFramedIPAddress        RADIUSAttributeType = 8  // RFC2865  5.8.  Framed-IP-Address
+	RADIUSAttributeTypeFramedIPNetmask        RADIUSAttributeType = 9  // RFC2865  5.9.  Framed-IP-Netmask
+	RADIUSAttributeTypeFramedRouting          RADIUSAttributeType = 10 // RFC2865 5.10.  Framed-Routing
+	RADIUSAttributeTypeFilterId               RADIUSAttributeType = 11 // RFC2865 5.11.  Filter-Id
+	RADIUSAttributeTypeFramedMTU              RADIUSAttributeType = 12 // RFC2865 5.12.  Framed-MTU
+	RADIUSAttributeTypeFramedCompression      RADIUSAttributeType = 13 // RFC2865 5.13.  Framed-Compression
+	RADIUSAttributeTypeLoginIPHost            RADIUSAttributeType = 14 // RFC2865 5.14.  Login-IP-Host
+	RADIUSAttributeTypeLoginService           RADIUSAttributeType = 15 // RFC2865 5.15.  Login-Service
+	RADIUSAttributeTypeLoginTCPPort           RADIUSAttributeType = 16 // RFC2865 5.16.  Login-TCP-Port
+	RADIUSAttributeTypeReplyMessage           RADIUSAttributeType = 18 // RFC2865 5.18.  Reply-Message
+	RADIUSAttributeTypeCallbackNumber         RADIUSAttributeType = 19 // RFC2865 5.19.  Callback-Number
+	RADIUSAttributeTypeCallbackId             RADIUSAttributeType = 20 // RFC2865 5.20.  Callback-Id
+	RADIUSAttributeTypeFramedRoute            RADIUSAttributeType = 22 // RFC2865 5.22.  Framed-Route
+	RADIUSAttributeTypeFramedIPXNetwork       RADIUSAttributeType = 23 // RFC2865 5.23.  Framed-IPX-Network
+	RADIUSAttributeTypeState                  RADIUSAttributeType = 24 // RFC2865 5.24.  State
+	RADIUSAttributeTypeClass                  RADIUSAttributeType = 25 // RFC2865 5.25.  Class
+	RADIUSAttributeTypeVendorSpecific         RADIUSAttributeType = 26 // RFC2865 5.26.  Vendor-Specific
+	RADIUSAttributeTypeSessionTimeout         RADIUSAttributeType = 27 // RFC2865 5.27.  Session-Timeout
+	RADIUSAttributeTypeIdleTimeout            RADIUSAttributeType = 28 // RFC2865 5.28.  Idle-Timeout
+	RADIUSAttributeTypeTerminationAction      RADIUSAttributeType = 29 // RFC2865 5.29.  Termination-Action
+	RADIUSAttributeTypeCalledStationId        RADIUSAttributeType = 30 // RFC2865 5.30.  Called-Station-Id
+	RADIUSAttributeTypeCallingStationId       RADIUSAttributeType = 31 // RFC2865 5.31.  Calling-Station-Id
+	RADIUSAttributeTypeNASIdentifier          RADIUSAttributeType = 32 // RFC2865 5.32.  NAS-Identifier
+	RADIUSAttributeTypeProxyState             RADIUSAttributeType = 33 // RFC2865 5.33.  Proxy-State
+	RADIUSAttributeTypeLoginLATService        RADIUSAttributeType = 34 // RFC2865 5.34.  Login-LAT-Service
+	RADIUSAttributeTypeLoginLATNode           RADIUSAttributeType = 35 // RFC2865 5.35.  Login-LAT-Node
+	RADIUSAttributeTypeLoginLATGroup          RADIUSAttributeType = 36 // RFC2865 5.36.  Login-LAT-Group
+	RADIUSAttributeTypeFramedAppleTalkLink    RADIUSAttributeType = 37 // RFC2865 5.37.  Framed-AppleTalk-Link
+	RADIUSAttributeTypeFramedAppleTalkNetwork RADIUSAttributeType = 38 // RFC2865 5.38.  Framed-AppleTalk-Network
+	RADIUSAttributeTypeFramedAppleTalkZone    RADIUSAttributeType = 39 // RFC2865 5.39.  Framed-AppleTalk-Zone
+	RADIUSAttributeTypeAcctStatusType         RADIUSAttributeType = 40 // RFC2866  5.1.  Acct-Status-Type
+	RADIUSAttributeTypeAcctDelayTime          RADIUSAttributeType = 41 // RFC2866  5.2.  Acct-Delay-Time
+	RADIUSAttributeTypeAcctInputOctets        RADIUSAttributeType = 42 // RFC2866  5.3.  Acct-Input-Octets
+	RADIUSAttributeTypeAcctOutputOctets       RADIUSAttributeType = 43 // RFC2866  5.4.  Acct-Output-Octets
+	RADIUSAttributeTypeAcctSessionId          RADIUSAttributeType = 44 // RFC2866  5.5.  Acct-Session-Id
+	RADIUSAttributeTypeAcctAuthentic          RADIUSAttributeType = 45 // RFC2866  5.6.  Acct-Authentic
+	RADIUSAttributeTypeAcctSessionTime        RADIUSAttributeType = 46 // RFC2866  5.7.  Acct-Session-Time
+	RADIUSAttributeTypeAcctInputPackets       RADIUSAttributeType = 47 // RFC2866  5.8.  Acct-Input-Packets
+	RADIUSAttributeTypeAcctOutputPackets      RADIUSAttributeType = 48 // RFC2866  5.9.  Acct-Output-Packets
+	RADIUSAttributeTypeAcctTerminateCause     RADIUSAttributeType = 49 // RFC2866 5.10.  Acct-Terminate-Cause
+	RADIUSAttributeTypeAcctMultiSessionId     RADIUSAttributeType = 50 // RFC2866 5.11.  Acct-Multi-Session-Id
+	RADIUSAttributeTypeAcctLinkCount          RADIUSAttributeType = 51 // RFC2866 5.12.  Acct-Link-Count
+	RADIUSAttributeTypeAcctInputGigawords     RADIUSAttributeType = 52 // RFC2869  5.1.  Acct-Input-Gigawords
+	RADIUSAttributeTypeAcctOutputGigawords    RADIUSAttributeType = 53 // RFC2869  5.2.  Acct-Output-Gigawords
+	RADIUSAttributeTypeEventTimestamp         RADIUSAttributeType = 55 // RFC2869  5.3.  Event-Timestamp
+	RADIUSAttributeTypeCHAPChallenge          RADIUSAttributeType = 60 // RFC2865 5.40.  CHAP-Challenge
+	RADIUSAttributeTypeNASPortType            RADIUSAttributeType = 61 // RFC2865 5.41.  NAS-Port-Type
+	RADIUSAttributeTypePortLimit              RADIUSAttributeType = 62 // RFC2865 5.42.  Port-Limit
+	RADIUSAttributeTypeLoginLATPort           RADIUSAttributeType = 63 // RFC2865 5.43.  Login-LAT-Port
+	RADIUSAttributeTypeTunnelType             RADIUSAttributeType = 64 // RFC2868  3.1.  Tunnel-Type
+	RADIUSAttributeTypeTunnelMediumType       RADIUSAttributeType = 65 // RFC2868  3.2.  Tunnel-Medium-Type
+	RADIUSAttributeTypeTunnelClientEndpoint   RADIUSAttributeType = 66 // RFC2868  3.3.  Tunnel-Client-Endpoint
+	RADIUSAttributeTypeTunnelServerEndpoint   RADIUSAttributeType = 67 // RFC2868  3.4.  Tunnel-Server-Endpoint
+	RADIUSAttributeTypeAcctTunnelConnection   RADIUSAttributeType = 68 // RFC2867  4.1.  Acct-Tunnel-Connection
+	RADIUSAttributeTypeTunnelPassword         RADIUSAttributeType = 69 // RFC2868  3.5.  Tunnel-Password
+	RADIUSAttributeTypeARAPPassword           RADIUSAttributeType = 70 // RFC2869  5.4.  ARAP-Password
+	RADIUSAttributeTypeARAPFeatures           RADIUSAttributeType = 71 // RFC2869  5.5.  ARAP-Features
+	RADIUSAttributeTypeARAPZoneAccess         RADIUSAttributeType = 72 // RFC2869  5.6.  ARAP-Zone-Access
+	RADIUSAttributeTypeARAPSecurity           RADIUSAttributeType = 73 // RFC2869  5.7.  ARAP-Security
+	RADIUSAttributeTypeARAPSecurityData       RADIUSAttributeType = 74 // RFC2869  5.8.  ARAP-Security-Data
+	RADIUSAttributeTypePasswordRetry          RADIUSAttributeType = 75 // RFC2869  5.9.  Password-Retry
+	RADIUSAttributeTypePrompt                 RADIUSAttributeType = 76 // RFC2869 5.10.  Prompt
+	RADIUSAttributeTypeConnectInfo            RADIUSAttributeType = 77 // RFC2869 5.11.  Connect-Info
+	RADIUSAttributeTypeConfigurationToken     RADIUSAttributeType = 78 // RFC2869 5.12.  Configuration-Token
+	RADIUSAttributeTypeEAPMessage             RADIUSAttributeType = 79 // RFC2869 5.13.  EAP-Message
+	RADIUSAttributeTypeMessageAuthenticator   RADIUSAttributeType = 80 // RFC2869 5.14.  Message-Authenticator
+	RADIUSAttributeTypeTunnelPrivateGroupID   RADIUSAttributeType = 81 // RFC2868  3.6.  Tunnel-Private-Group-ID
+	RADIUSAttributeTypeTunnelAssignmentID     RADIUSAttributeType = 82 // RFC2868  3.7.  Tunnel-Assignment-ID
+	RADIUSAttributeTypeTunnelPreference       RADIUSAttributeType = 83 // RFC2868  3.8.  Tunnel-Preference
+	RADIUSAttributeTypeARAPChallengeResponse  RADIUSAttributeType = 84 // RFC2869 5.15.  ARAP-Challenge-Response
+	RADIUSAttributeTypeAcctInterimInterval    RADIUSAttributeType = 85 // RFC2869 5.16.  Acct-Interim-Interval
+	RADIUSAttributeTypeAcctTunnelPacketsLost  RADIUSAttributeType = 86 // RFC2867  4.2.  Acct-Tunnel-Packets-Lost
+	RADIUSAttributeTypeNASPortId              RADIUSAttributeType = 87 // RFC2869 5.17.  NAS-Port-Id
+	RADIUSAttributeTypeFramedPool             RADIUSAttributeType = 88 // RFC2869 5.18.  Framed-Pool
+	RADIUSAttributeTypeTunnelClientAuthID     RADIUSAttributeType = 90 // RFC2868  3.9.  Tunnel-Client-Auth-ID
+	RADIUSAttributeTypeTunnelServerAuthID     RADIUSAttributeType = 91 // RFC2868 3.10.  Tunnel-Server-Auth-ID
+)
+
+// RADIUSAttributeType represents attribute length.
+type RADIUSAttributeLength uint8
+
+// RADIUSAttributeType represents attribute value.
+type RADIUSAttributeValue []byte
+
+// String returns a string version of a RADIUSAttributeType.
+func (t RADIUSAttributeType) String() (s string) {
+	switch t {
+	case RADIUSAttributeTypeUserName:
+		s = "User-Name"
+	case RADIUSAttributeTypeUserPassword:
+		s = "User-Password"
+	case RADIUSAttributeTypeCHAPPassword:
+		s = "CHAP-Password"
+	case RADIUSAttributeTypeNASIPAddress:
+		s = "NAS-IP-Address"
+	case RADIUSAttributeTypeNASPort:
+		s = "NAS-Port"
+	case RADIUSAttributeTypeServiceType:
+		s = "Service-Type"
+	case RADIUSAttributeTypeFramedProtocol:
+		s = "Framed-Protocol"
+	case RADIUSAttributeTypeFramedIPAddress:
+		s = "Framed-IP-Address"
+	case RADIUSAttributeTypeFramedIPNetmask:
+		s = "Framed-IP-Netmask"
+	case RADIUSAttributeTypeFramedRouting:
+		s = "Framed-Routing"
+	case RADIUSAttributeTypeFilterId:
+		s = "Filter-Id"
+	case RADIUSAttributeTypeFramedMTU:
+		s = "Framed-MTU"
+	case RADIUSAttributeTypeFramedCompression:
+		s = "Framed-Compression"
+	case RADIUSAttributeTypeLoginIPHost:
+		s = "Login-IP-Host"
+	case RADIUSAttributeTypeLoginService:
+		s = "Login-Service"
+	case RADIUSAttributeTypeLoginTCPPort:
+		s = "Login-TCP-Port"
+	case RADIUSAttributeTypeReplyMessage:
+		s = "Reply-Message"
+	case RADIUSAttributeTypeCallbackNumber:
+		s = "Callback-Number"
+	case RADIUSAttributeTypeCallbackId:
+		s = "Callback-Id"
+	case RADIUSAttributeTypeFramedRoute:
+		s = "Framed-Route"
+	case RADIUSAttributeTypeFramedIPXNetwork:
+		s = "Framed-IPX-Network"
+	case RADIUSAttributeTypeState:
+		s = "State"
+	case RADIUSAttributeTypeClass:
+		s = "Class"
+	case RADIUSAttributeTypeVendorSpecific:
+		s = "Vendor-Specific"
+	case RADIUSAttributeTypeSessionTimeout:
+		s = "Session-Timeout"
+	case RADIUSAttributeTypeIdleTimeout:
+		s = "Idle-Timeout"
+	case RADIUSAttributeTypeTerminationAction:
+		s = "Termination-Action"
+	case RADIUSAttributeTypeCalledStationId:
+		s = "Called-Station-Id"
+	case RADIUSAttributeTypeCallingStationId:
+		s = "Calling-Station-Id"
+	case RADIUSAttributeTypeNASIdentifier:
+		s = "NAS-Identifier"
+	case RADIUSAttributeTypeProxyState:
+		s = "Proxy-State"
+	case RADIUSAttributeTypeLoginLATService:
+		s = "Login-LAT-Service"
+	case RADIUSAttributeTypeLoginLATNode:
+		s = "Login-LAT-Node"
+	case RADIUSAttributeTypeLoginLATGroup:
+		s = "Login-LAT-Group"
+	case RADIUSAttributeTypeFramedAppleTalkLink:
+		s = "Framed-AppleTalk-Link"
+	case RADIUSAttributeTypeFramedAppleTalkNetwork:
+		s = "Framed-AppleTalk-Network"
+	case RADIUSAttributeTypeFramedAppleTalkZone:
+		s = "Framed-AppleTalk-Zone"
+	case RADIUSAttributeTypeAcctStatusType:
+		s = "Acct-Status-Type"
+	case RADIUSAttributeTypeAcctDelayTime:
+		s = "Acct-Delay-Time"
+	case RADIUSAttributeTypeAcctInputOctets:
+		s = "Acct-Input-Octets"
+	case RADIUSAttributeTypeAcctOutputOctets:
+		s = "Acct-Output-Octets"
+	case RADIUSAttributeTypeAcctSessionId:
+		s = "Acct-Session-Id"
+	case RADIUSAttributeTypeAcctAuthentic:
+		s = "Acct-Authentic"
+	case RADIUSAttributeTypeAcctSessionTime:
+		s = "Acct-Session-Time"
+	case RADIUSAttributeTypeAcctInputPackets:
+		s = "Acct-Input-Packets"
+	case RADIUSAttributeTypeAcctOutputPackets:
+		s = "Acct-Output-Packets"
+	case RADIUSAttributeTypeAcctTerminateCause:
+		s = "Acct-Terminate-Cause"
+	case RADIUSAttributeTypeAcctMultiSessionId:
+		s = "Acct-Multi-Session-Id"
+	case RADIUSAttributeTypeAcctLinkCount:
+		s = "Acct-Link-Count"
+	case RADIUSAttributeTypeAcctInputGigawords:
+		s = "Acct-Input-Gigawords"
+	case RADIUSAttributeTypeAcctOutputGigawords:
+		s = "Acct-Output-Gigawords"
+	case RADIUSAttributeTypeEventTimestamp:
+		s = "Event-Timestamp"
+	case RADIUSAttributeTypeCHAPChallenge:
+		s = "CHAP-Challenge"
+	case RADIUSAttributeTypeNASPortType:
+		s = "NAS-Port-Type"
+	case RADIUSAttributeTypePortLimit:
+		s = "Port-Limit"
+	case RADIUSAttributeTypeLoginLATPort:
+		s = "Login-LAT-Port"
+	case RADIUSAttributeTypeTunnelType:
+		s = "Tunnel-Type"
+	case RADIUSAttributeTypeTunnelMediumType:
+		s = "Tunnel-Medium-Type"
+	case RADIUSAttributeTypeTunnelClientEndpoint:
+		s = "Tunnel-Client-Endpoint"
+	case RADIUSAttributeTypeTunnelServerEndpoint:
+		s = "Tunnel-Server-Endpoint"
+	case RADIUSAttributeTypeAcctTunnelConnection:
+		s = "Acct-Tunnel-Connection"
+	case RADIUSAttributeTypeTunnelPassword:
+		s = "Tunnel-Password"
+	case RADIUSAttributeTypeARAPPassword:
+		s = "ARAP-Password"
+	case RADIUSAttributeTypeARAPFeatures:
+		s = "ARAP-Features"
+	case RADIUSAttributeTypeARAPZoneAccess:
+		s = "ARAP-Zone-Access"
+	case RADIUSAttributeTypeARAPSecurity:
+		s = "ARAP-Security"
+	case RADIUSAttributeTypeARAPSecurityData:
+		s = "ARAP-Security-Data"
+	case RADIUSAttributeTypePasswordRetry:
+		s = "Password-Retry"
+	case RADIUSAttributeTypePrompt:
+		s = "Prompt"
+	case RADIUSAttributeTypeConnectInfo:
+		s = "Connect-Info"
+	case RADIUSAttributeTypeConfigurationToken:
+		s = "Configuration-Token"
+	case RADIUSAttributeTypeEAPMessage:
+		s = "EAP-Message"
+	case RADIUSAttributeTypeMessageAuthenticator:
+		s = "Message-Authenticator"
+	case RADIUSAttributeTypeTunnelPrivateGroupID:
+		s = "Tunnel-Private-Group-ID"
+	case RADIUSAttributeTypeTunnelAssignmentID:
+		s = "Tunnel-Assignment-ID"
+	case RADIUSAttributeTypeTunnelPreference:
+		s = "Tunnel-Preference"
+	case RADIUSAttributeTypeARAPChallengeResponse:
+		s = "ARAP-Challenge-Response"
+	case RADIUSAttributeTypeAcctInterimInterval:
+		s = "Acct-Interim-Interval"
+	case RADIUSAttributeTypeAcctTunnelPacketsLost:
+		s = "Acct-Tunnel-Packets-Lost"
+	case RADIUSAttributeTypeNASPortId:
+		s = "NAS-Port-Id"
+	case RADIUSAttributeTypeFramedPool:
+		s = "Framed-Pool"
+	case RADIUSAttributeTypeTunnelClientAuthID:
+		s = "Tunnel-Client-Auth-ID"
+	case RADIUSAttributeTypeTunnelServerAuthID:
+		s = "Tunnel-Server-Auth-ID"
+	default:
+		s = fmt.Sprintf("Unknown(%d)", t)
+	}
+	return
+}
+
+// Len returns the length of a RADIUS packet.
+func (radius *RADIUS) Len() (int, error) {
+	n := radiusMinimumRecordSizeInBytes
+	for _, v := range radius.Attributes {
+		alen, err := attributeValueLength(v.Value)
+		if err != nil {
+			return 0, err
+		}
+		n += int(alen) + 2 // Added Type and Length
+	}
+	return n, nil
+}
+
+// LayerType returns LayerTypeRADIUS.
+func (radius *RADIUS) LayerType() gopacket.LayerType {
+	return LayerTypeRADIUS
+}
+
+// DecodeFromBytes decodes the given bytes into this layer.
+func (radius *RADIUS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) > radiusMaximumRecordSizeInBytes {
+		df.SetTruncated()
+		return fmt.Errorf("RADIUS length %d too big", len(data))
+	}
+
+	if len(data) < radiusMinimumRecordSizeInBytes {
+		df.SetTruncated()
+		return fmt.Errorf("RADIUS length %d too short", len(data))
+	}
+
+	radius.BaseLayer = BaseLayer{Contents: data}
+
+	radius.Code = RADIUSCode(data[0])
+	radius.Identifier = RADIUSIdentifier(data[1])
+	radius.Length = RADIUSLength(binary.BigEndian.Uint16(data[2:4]))
+
+	if int(radius.Length) > radiusMaximumRecordSizeInBytes {
+		df.SetTruncated()
+		return fmt.Errorf("RADIUS length %d too big", radius.Length)
+	}
+
+	if int(radius.Length) < radiusMinimumRecordSizeInBytes {
+		df.SetTruncated()
+		return fmt.Errorf("RADIUS length %d too short", radius.Length)
+	}
+
+	// RFC 2865 3.  Packet Format
+	// `If the packet is shorter than the Length field indicates, it MUST be silently discarded.`
+	if int(radius.Length) > len(data) {
+		df.SetTruncated()
+		return fmt.Errorf("RADIUS length %d too big", radius.Length)
+	}
+
+	// RFC 2865 3.  Packet Format
+	// `Octets outside the range of the Length field MUST be treated as padding and ignored on reception.`
+	if int(radius.Length) < len(data) {
+		df.SetTruncated()
+		data = data[:radius.Length]
+	}
+
+	copy(radius.Authenticator[:], data[4:20])
+
+	if len(data) == radiusMinimumRecordSizeInBytes {
+		return nil
+	}
+
+	pos := radiusMinimumRecordSizeInBytes
+	for {
+		if len(data) == pos {
+			break
+		}
+
+		if len(data[pos:]) < radiusAttributesMinimumRecordSizeInBytes {
+			df.SetTruncated()
+			return fmt.Errorf("RADIUS attributes length %d too short", len(data[pos:]))
+		}
+
+		attr := RADIUSAttribute{}
+		attr.Type = RADIUSAttributeType(data[pos])
+		attr.Length = RADIUSAttributeLength(data[pos+1])
+
+		if int(attr.Length) > len(data[pos:]) {
+			df.SetTruncated()
+			return fmt.Errorf("RADIUS attributes length %d too big", attr.Length)
+		}
+
+		if int(attr.Length) < radiusAttributesMinimumRecordSizeInBytes {
+			df.SetTruncated()
+			return fmt.Errorf("RADIUS attributes length %d too short", attr.Length)
+		}
+
+		if int(attr.Length) > radiusAttributesMinimumRecordSizeInBytes {
+			attr.Value = make([]byte, attr.Length-2)
+			copy(attr.Value[:], data[pos+2:pos+int(attr.Length)])
+			radius.Attributes = append(radius.Attributes, attr)
+		}
+
+		pos += int(attr.Length)
+	}
+
+	for _, v := range radius.Attributes {
+		if v.Type == RADIUSAttributeTypeEAPMessage {
+			radius.BaseLayer.Payload = append(radius.BaseLayer.Payload, v.Value...)
+		}
+	}
+
+	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 (radius *RADIUS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	plen, err := radius.Len()
+	if err != nil {
+		return err
+	}
+
+	if opts.FixLengths {
+		radius.Length = RADIUSLength(plen)
+	}
+
+	data, err := b.PrependBytes(plen)
+	if err != nil {
+		return err
+	}
+
+	data[0] = byte(radius.Code)
+	data[1] = byte(radius.Identifier)
+	binary.BigEndian.PutUint16(data[2:], uint16(radius.Length))
+	copy(data[4:20], radius.Authenticator[:])
+
+	pos := radiusMinimumRecordSizeInBytes
+	for _, v := range radius.Attributes {
+		if opts.FixLengths {
+			v.Length, err = attributeValueLength(v.Value)
+			if err != nil {
+				return err
+			}
+		}
+
+		data[pos] = byte(v.Type)
+		data[pos+1] = byte(v.Length)
+		copy(data[pos+2:], v.Value[:])
+
+		pos += len(v.Value) + 2 // Added Type and Length
+	}
+
+	return nil
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode.
+func (radius *RADIUS) CanDecode() gopacket.LayerClass {
+	return LayerTypeRADIUS
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer.
+func (radius *RADIUS) NextLayerType() gopacket.LayerType {
+	if len(radius.BaseLayer.Payload) > 0 {
+		return LayerTypeEAP
+	} else {
+		return gopacket.LayerTypeZero
+	}
+}
+
+// Payload returns the EAP Type-Data for EAP-Message attributes.
+func (radius *RADIUS) Payload() []byte {
+	return radius.BaseLayer.Payload
+}
+
+func decodeRADIUS(data []byte, p gopacket.PacketBuilder) error {
+	radius := &RADIUS{}
+	err := radius.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(radius)
+	p.SetApplicationLayer(radius)
+	next := radius.NextLayerType()
+	if next == gopacket.LayerTypeZero {
+		return nil
+	}
+	return p.NextDecoder(next)
+}
+
+func attributeValueLength(v []byte) (RADIUSAttributeLength, error) {
+	n := len(v)
+	if n > 255 {
+		return 0, fmt.Errorf("RADIUS attribute value length %d too long", n)
+	} else {
+		return RADIUSAttributeLength(n), nil
+	}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/rmcp.go b/go-controller/vendor/github.com/google/gopacket/layers/rmcp.go
new file mode 100644
index 00000000000..5474fee4ad6
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/rmcp.go
@@ -0,0 +1,170 @@
+// 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 header specified in section 3.2.2.2 of
+// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
+
+import (
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a
+// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined)
+// are currently reserved.
+type RMCPClass uint8
+
+// LayerType returns the payload layer type corresponding to a RMCP class.
+func (c RMCPClass) LayerType() gopacket.LayerType {
+	if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 {
+		return lt
+	}
+	return gopacket.LayerTypePayload
+}
+
+func (c RMCPClass) String() string {
+	return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType())
+}
+
+const (
+	// RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower
+	// values are considered legacy, while higher values are reserved by the
+	// specification.
+	RMCPVersion1 uint8 = 0x06
+
+	// RMCPNormal indicates a "normal" message, i.e. not an acknowledgement.
+	RMCPNormal uint8 = 0
+
+	// RMCPAck indicates a message is acknowledging a received normal message.
+	RMCPAck uint8 = 1 << 7
+
+	// RMCPClassASF identifies an RMCP message as containing an ASF-RMCP
+	// payload.
+	RMCPClassASF RMCPClass = 0x06
+
+	// RMCPClassIPMI identifies an RMCP message as containing an IPMI payload.
+	RMCPClassIPMI RMCPClass = 0x07
+
+	// RMCPClassOEM identifies an RMCP message as containing an OEM-defined
+	// payload.
+	RMCPClassOEM RMCPClass = 0x08
+)
+
+var (
+	rmcpClassLayerTypes = [16]gopacket.LayerType{
+		RMCPClassASF: LayerTypeASF,
+		// RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not
+		// implemented, so we return LayerTypePayload
+	}
+)
+
+// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of
+// a certain class should processed by the provided layer type. This overrides
+// any existing registrations, including defaults.
+func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) {
+	rmcpClassLayerTypes[c] = l
+}
+
+// RMCP describes the format of an RMCP header, which forms a UDP payload. See
+// section 3.2.2.2.
+type RMCP struct {
+	BaseLayer
+
+	// Version identifies the version of the RMCP header. 0x06 indicates RMCP
+	// v1.0; lower values are legacy, higher values are reserved.
+	Version uint8
+
+	// Sequence is the sequence number assicated with the message. Note that
+	// this rolls over to 0 after 254, not 255. Seq num 255 indicates the
+	// receiver must not send an ACK.
+	Sequence uint8
+
+	// Ack indicates whether this packet is an acknowledgement. If it is, the
+	// payload will be empty.
+	Ack bool
+
+	// Class idicates the structure of the payload. There are only 2^4 valid
+	// values, however there is no uint4 data type. N.B. the Ack bit has been
+	// split off into another field. The most significant 4 bits of this field
+	// will always be 0.
+	Class RMCPClass
+}
+
+// LayerType returns LayerTypeRMCP. It partially satisfies Layer and
+// SerializableLayer.
+func (*RMCP) LayerType() gopacket.LayerType {
+	return LayerTypeRMCP
+}
+
+// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer.
+func (r *RMCP) CanDecode() gopacket.LayerClass {
+	return r.LayerType()
+}
+
+// DecodeFromBytes makes the layer represent the provided bytes. It partially
+// satisfies DecodingLayer.
+func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 4 {
+		df.SetTruncated()
+		return fmt.Errorf("invalid RMCP header, length %v less than 4",
+			len(data))
+	}
+
+	r.BaseLayer.Contents = data[:4]
+	r.BaseLayer.Payload = data[4:]
+
+	r.Version = uint8(data[0])
+	// 1 byte reserved
+	r.Sequence = uint8(data[2])
+	r.Ack = data[3]&RMCPAck != 0
+	r.Class = RMCPClass(data[3] & 0xF)
+	return nil
+}
+
+// NextLayerType returns the data layer of this RMCP layer. This partially
+// satisfies DecodingLayer.
+func (r *RMCP) NextLayerType() gopacket.LayerType {
+	return r.Class.LayerType()
+}
+
+// Payload returns the data layer. It partially satisfies ApplicationLayer.
+func (r *RMCP) Payload() []byte {
+	return r.BaseLayer.Payload
+}
+
+// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
+// partially satisfying SerializableLayer.
+func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
+	// The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths
+	// to work around issues in LAN chips. This is no longer necessary as of
+	// IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The
+	// same approach is taken by FreeIPMI:
+	// http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	bytes[0] = r.Version
+	bytes[1] = 0x00
+	bytes[2] = r.Sequence
+	bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer
+	return nil
+}
+
+// decodeRMCP decodes the byte slice into an RMCP type, and sets the application
+// layer to it.
+func decodeRMCP(data []byte, p gopacket.PacketBuilder) error {
+	rmcp := &RMCP{}
+	err := rmcp.DecodeFromBytes(data, p)
+	p.AddLayer(rmcp)
+	p.SetApplicationLayer(rmcp)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(rmcp.NextLayerType())
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/rudp.go b/go-controller/vendor/github.com/google/gopacket/layers/rudp.go
new file mode 100644
index 00000000000..8435129b947
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/rudp.go
@@ -0,0 +1,93 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"fmt"
+	"github.com/google/gopacket"
+)
+
+type RUDP struct {
+	BaseLayer
+	SYN, ACK, EACK, RST, NUL bool
+	Version                  uint8
+	HeaderLength             uint8
+	SrcPort, DstPort         RUDPPort
+	DataLength               uint16
+	Seq, Ack, Checksum       uint32
+	VariableHeaderArea       []byte
+	// RUDPHeaderSyn contains SYN information for the RUDP packet,
+	// if the SYN flag is set
+	*RUDPHeaderSYN
+	// RUDPHeaderEack contains EACK information for the RUDP packet,
+	// if the EACK flag is set.
+	*RUDPHeaderEACK
+}
+
+type RUDPHeaderSYN struct {
+	MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16
+}
+
+type RUDPHeaderEACK struct {
+	SeqsReceivedOK []uint32
+}
+
+// LayerType returns gopacket.LayerTypeRUDP.
+func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP }
+
+func decodeRUDP(data []byte, p gopacket.PacketBuilder) error {
+	r := &RUDP{
+		SYN:          data[0]&0x80 != 0,
+		ACK:          data[0]&0x40 != 0,
+		EACK:         data[0]&0x20 != 0,
+		RST:          data[0]&0x10 != 0,
+		NUL:          data[0]&0x08 != 0,
+		Version:      data[0] & 0x3,
+		HeaderLength: data[1],
+		SrcPort:      RUDPPort(data[2]),
+		DstPort:      RUDPPort(data[3]),
+		DataLength:   binary.BigEndian.Uint16(data[4:6]),
+		Seq:          binary.BigEndian.Uint32(data[6:10]),
+		Ack:          binary.BigEndian.Uint32(data[10:14]),
+		Checksum:     binary.BigEndian.Uint32(data[14:18]),
+	}
+	if r.HeaderLength < 9 {
+		return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength)
+	}
+	hlen := int(r.HeaderLength) * 2
+	r.Contents = data[:hlen]
+	r.Payload = data[hlen : hlen+int(r.DataLength)]
+	r.VariableHeaderArea = data[18:hlen]
+	headerData := r.VariableHeaderArea
+	switch {
+	case r.SYN:
+		if len(headerData) != 6 {
+			return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData))
+		}
+		r.RUDPHeaderSYN = &RUDPHeaderSYN{
+			MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]),
+			MaxSegmentSize:         binary.BigEndian.Uint16(headerData[2:4]),
+			OptionFlags:            binary.BigEndian.Uint16(headerData[4:6]),
+		}
+	case r.EACK:
+		if len(headerData)%4 != 0 {
+			return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData))
+		}
+		r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)}
+		for i := 0; i < len(headerData); i += 4 {
+			r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4])
+		}
+	}
+	p.AddLayer(r)
+	p.SetTransportLayer(r)
+	return p.NextDecoder(gopacket.LayerTypePayload)
+}
+
+func (r *RUDP) TransportFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)})
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/sctp.go b/go-controller/vendor/github.com/google/gopacket/layers/sctp.go
new file mode 100644
index 00000000000..511176e560f
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/sctp.go
@@ -0,0 +1,746 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"hash/crc32"
+
+	"github.com/google/gopacket"
+)
+
+// SCTP contains information on the top level of an SCTP packet.
+type SCTP struct {
+	BaseLayer
+	SrcPort, DstPort SCTPPort
+	VerificationTag  uint32
+	Checksum         uint32
+	sPort, dPort     []byte
+}
+
+// LayerType returns gopacket.LayerTypeSCTP
+func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP }
+
+func decodeSCTP(data []byte, p gopacket.PacketBuilder) error {
+	sctp := &SCTP{}
+	err := sctp.DecodeFromBytes(data, p)
+	p.AddLayer(sctp)
+	p.SetTransportLayer(sctp)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(sctpChunkTypePrefixDecoder)
+}
+
+var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)
+
+// TransportFlow returns a flow based on the source and destination SCTP port.
+func (s *SCTP) TransportFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort)
+}
+
+func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error {
+	chunkType := SCTPChunkType(data[0])
+	return chunkType.Decode(data, p)
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(12)
+	if err != nil {
+		return err
+	}
+	binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort))
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort))
+	binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag)
+	if opts.ComputeChecksums {
+		// Note:  MakeTable(Castagnoli) actually only creates the table once, then
+		// passes back a singleton on every other call, so this shouldn't cause
+		// excessive memory allocation.
+		binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli)))
+	}
+	return nil
+}
+
+func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 12 {
+		return errors.New("Invalid SCTP common header length")
+	}
+	sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2]))
+	sctp.sPort = data[:2]
+	sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4]))
+	sctp.dPort = data[2:4]
+	sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8])
+	sctp.Checksum = binary.BigEndian.Uint32(data[8:12])
+	sctp.BaseLayer = BaseLayer{data[:12], data[12:]}
+
+	return nil
+}
+
+func (t *SCTP) CanDecode() gopacket.LayerClass {
+	return LayerTypeSCTP
+}
+
+func (t *SCTP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// SCTPChunk contains the common fields in all SCTP chunks.
+type SCTPChunk struct {
+	BaseLayer
+	Type   SCTPChunkType
+	Flags  uint8
+	Length uint16
+	// ActualLength is the total length of an SCTP chunk, including padding.
+	// SCTP chunks start and end on 4-byte boundaries.  So if a chunk has a length
+	// of 18, it means that it has data up to and including byte 18, then padding
+	// up to the next 4-byte boundary, 20.  In this case, Length would be 18, and
+	// ActualLength would be 20.
+	ActualLength int
+}
+
+func roundUpToNearest4(i int) int {
+	if i%4 == 0 {
+		return i
+	}
+	return i + 4 - (i % 4)
+}
+
+func decodeSCTPChunk(data []byte) (SCTPChunk, error) {
+	length := binary.BigEndian.Uint16(data[2:4])
+	if length < 4 {
+		return SCTPChunk{}, errors.New("invalid SCTP chunk length")
+	}
+	actual := roundUpToNearest4(int(length))
+	ct := SCTPChunkType(data[0])
+
+	// For SCTP Data, use a separate layer for the payload
+	delta := 0
+	if ct == SCTPChunkTypeData {
+		delta = int(actual) - int(length)
+		actual = 16
+	}
+
+	return SCTPChunk{
+		Type:         ct,
+		Flags:        data[1],
+		Length:       length,
+		ActualLength: actual,
+		BaseLayer:    BaseLayer{data[:actual], data[actual : len(data)-delta]},
+	}, nil
+}
+
+// SCTPParameter is a TLV parameter inside a SCTPChunk.
+type SCTPParameter struct {
+	Type         uint16
+	Length       uint16
+	ActualLength int
+	Value        []byte
+}
+
+func decodeSCTPParameter(data []byte) SCTPParameter {
+	length := binary.BigEndian.Uint16(data[2:4])
+	return SCTPParameter{
+		Type:         binary.BigEndian.Uint16(data[0:2]),
+		Length:       length,
+		Value:        data[4:length],
+		ActualLength: roundUpToNearest4(int(length)),
+	}
+}
+
+func (p SCTPParameter) Bytes() []byte {
+	length := 4 + len(p.Value)
+	data := make([]byte, roundUpToNearest4(length))
+	binary.BigEndian.PutUint16(data[0:2], p.Type)
+	binary.BigEndian.PutUint16(data[2:4], uint16(length))
+	copy(data[4:], p.Value)
+	return data
+}
+
+// SCTPUnknownChunkType is the layer type returned when we don't recognize the
+// chunk type.  Since there's a length in a known location, we can skip over
+// it even if we don't know what it is, and continue parsing the rest of the
+// chunks.  This chunk is stored as an ErrorLayer in the packet.
+type SCTPUnknownChunkType struct {
+	SCTPChunk
+	bytes []byte
+}
+
+func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPUnknownChunkType{SCTPChunk: chunk}
+	sc.bytes = data[:sc.ActualLength]
+	p.AddLayer(sc)
+	p.SetErrorLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(s.ActualLength)
+	if err != nil {
+		return err
+	}
+	copy(bytes, s.bytes)
+	return nil
+}
+
+// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType.
+func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType }
+
+// Payload returns all bytes in this header, including the decoded Type, Length,
+// and Flags.
+func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes }
+
+// Error implements ErrorLayer.
+func (s *SCTPUnknownChunkType) Error() error {
+	return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type)
+}
+
+// SCTPData is the SCTP Data chunk layer.
+type SCTPData struct {
+	SCTPChunk
+	Unordered, BeginFragment, EndFragment bool
+	TSN                                   uint32
+	StreamId                              uint16
+	StreamSequence                        uint16
+	PayloadProtocol                       SCTPPayloadProtocol
+}
+
+// LayerType returns gopacket.LayerTypeSCTPData.
+func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData }
+
+// SCTPPayloadProtocol represents a payload protocol
+type SCTPPayloadProtocol uint32
+
+// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml
+const (
+	SCTPProtocolReserved  SCTPPayloadProtocol = 0
+	SCTPPayloadUIA                            = 1
+	SCTPPayloadM2UA                           = 2
+	SCTPPayloadM3UA                           = 3
+	SCTPPayloadSUA                            = 4
+	SCTPPayloadM2PA                           = 5
+	SCTPPayloadV5UA                           = 6
+	SCTPPayloadH248                           = 7
+	SCTPPayloadBICC                           = 8
+	SCTPPayloadTALI                           = 9
+	SCTPPayloadDUA                            = 10
+	SCTPPayloadASAP                           = 11
+	SCTPPayloadENRP                           = 12
+	SCTPPayloadH323                           = 13
+	SCTPPayloadQIPC                           = 14
+	SCTPPayloadSIMCO                          = 15
+	SCTPPayloadDDPSegment                     = 16
+	SCTPPayloadDDPStream                      = 17
+	SCTPPayloadS1AP                           = 18
+)
+
+func (p SCTPPayloadProtocol) String() string {
+	switch p {
+	case SCTPProtocolReserved:
+		return "Reserved"
+	case SCTPPayloadUIA:
+		return "UIA"
+	case SCTPPayloadM2UA:
+		return "M2UA"
+	case SCTPPayloadM3UA:
+		return "M3UA"
+	case SCTPPayloadSUA:
+		return "SUA"
+	case SCTPPayloadM2PA:
+		return "M2PA"
+	case SCTPPayloadV5UA:
+		return "V5UA"
+	case SCTPPayloadH248:
+		return "H.248"
+	case SCTPPayloadBICC:
+		return "BICC"
+	case SCTPPayloadTALI:
+		return "TALI"
+	case SCTPPayloadDUA:
+		return "DUA"
+	case SCTPPayloadASAP:
+		return "ASAP"
+	case SCTPPayloadENRP:
+		return "ENRP"
+	case SCTPPayloadH323:
+		return "H.323"
+	case SCTPPayloadQIPC:
+		return "QIPC"
+	case SCTPPayloadSIMCO:
+		return "SIMCO"
+	case SCTPPayloadDDPSegment:
+		return "DDPSegment"
+	case SCTPPayloadDDPStream:
+		return "DDPStream"
+	case SCTPPayloadS1AP:
+		return "S1AP"
+	}
+	return fmt.Sprintf("Unknown(%d)", p)
+}
+
+func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPData{
+		SCTPChunk:       chunk,
+		Unordered:       data[1]&0x4 != 0,
+		BeginFragment:   data[1]&0x2 != 0,
+		EndFragment:     data[1]&0x1 != 0,
+		TSN:             binary.BigEndian.Uint32(data[4:8]),
+		StreamId:        binary.BigEndian.Uint16(data[8:10]),
+		StreamSequence:  binary.BigEndian.Uint16(data[10:12]),
+		PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])),
+	}
+	// Length is the length in bytes of the data, INCLUDING the 16-byte header.
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.LayerTypePayload)
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	payload := b.Bytes()
+	// Pad the payload to a 32 bit boundary
+	if rem := len(payload) % 4; rem != 0 {
+		b.AppendBytes(4 - rem)
+	}
+	length := 16
+	bytes, err := b.PrependBytes(length)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	flags := uint8(0)
+	if sc.Unordered {
+		flags |= 0x4
+	}
+	if sc.BeginFragment {
+		flags |= 0x2
+	}
+	if sc.EndFragment {
+		flags |= 0x1
+	}
+	bytes[1] = flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload)))
+	binary.BigEndian.PutUint32(bytes[4:8], sc.TSN)
+	binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId)
+	binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence)
+	binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol))
+	return nil
+}
+
+// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet.
+type SCTPInitParameter SCTPParameter
+
+// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck
+// messages.
+type SCTPInit struct {
+	SCTPChunk
+	InitiateTag                     uint32
+	AdvertisedReceiverWindowCredit  uint32
+	OutboundStreams, InboundStreams uint16
+	InitialTSN                      uint32
+	Parameters                      []SCTPInitParameter
+}
+
+// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck.
+func (sc *SCTPInit) LayerType() gopacket.LayerType {
+	if sc.Type == SCTPChunkTypeInitAck {
+		return LayerTypeSCTPInitAck
+	}
+	// sc.Type == SCTPChunkTypeInit
+	return LayerTypeSCTPInit
+}
+
+func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPInit{
+		SCTPChunk:                      chunk,
+		InitiateTag:                    binary.BigEndian.Uint32(data[4:8]),
+		AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
+		OutboundStreams:                binary.BigEndian.Uint16(data[12:14]),
+		InboundStreams:                 binary.BigEndian.Uint16(data[14:16]),
+		InitialTSN:                     binary.BigEndian.Uint32(data[16:20]),
+	}
+	paramData := data[20:sc.ActualLength]
+	for len(paramData) > 0 {
+		p := SCTPInitParameter(decodeSCTPParameter(paramData))
+		paramData = paramData[p.ActualLength:]
+		sc.Parameters = append(sc.Parameters, p)
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var payload []byte
+	for _, param := range sc.Parameters {
+		payload = append(payload, SCTPParameter(param).Bytes()...)
+	}
+	length := 20 + len(payload)
+	bytes, err := b.PrependBytes(roundUpToNearest4(length))
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+	binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag)
+	binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
+	binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams)
+	binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams)
+	binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN)
+	copy(bytes[20:], payload)
+	return nil
+}
+
+// SCTPSack is the SCTP Selective ACK chunk layer.
+type SCTPSack struct {
+	SCTPChunk
+	CumulativeTSNAck               uint32
+	AdvertisedReceiverWindowCredit uint32
+	NumGapACKs, NumDuplicateTSNs   uint16
+	GapACKs                        []uint16
+	DuplicateTSNs                  []uint32
+}
+
+// LayerType return LayerTypeSCTPSack
+func (sc *SCTPSack) LayerType() gopacket.LayerType {
+	return LayerTypeSCTPSack
+}
+
+func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPSack{
+		SCTPChunk:                      chunk,
+		CumulativeTSNAck:               binary.BigEndian.Uint32(data[4:8]),
+		AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
+		NumGapACKs:                     binary.BigEndian.Uint16(data[12:14]),
+		NumDuplicateTSNs:               binary.BigEndian.Uint16(data[14:16]),
+	}
+	// We maximize gapAcks and dupTSNs here so we're not allocating tons
+	// of memory based on a user-controlable field.  Our maximums are not exact,
+	// but should give us sane defaults... we'll still hit slice boundaries and
+	// fail if the user-supplied values are too high (in the for loops below), but
+	// the amount of memory we'll have allocated because of that should be small
+	// (< sc.ActualLength)
+	gapAcks := sc.SCTPChunk.ActualLength / 2
+	dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4
+	if gapAcks > int(sc.NumGapACKs) {
+		gapAcks = int(sc.NumGapACKs)
+	}
+	if dupTSNs > int(sc.NumDuplicateTSNs) {
+		dupTSNs = int(sc.NumDuplicateTSNs)
+	}
+	sc.GapACKs = make([]uint16, 0, gapAcks)
+	sc.DuplicateTSNs = make([]uint32, 0, dupTSNs)
+	bytesRemaining := data[16:]
+	for i := 0; i < int(sc.NumGapACKs); i++ {
+		sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))
+		bytesRemaining = bytesRemaining[2:]
+	}
+	for i := 0; i < int(sc.NumDuplicateTSNs); i++ {
+		sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))
+		bytesRemaining = bytesRemaining[4:]
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs)
+	bytes, err := b.PrependBytes(roundUpToNearest4(length))
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+	binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
+	binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
+	binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs)))
+	binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs)))
+	for i, v := range sc.GapACKs {
+		binary.BigEndian.PutUint16(bytes[16+i*2:], v)
+	}
+	offset := 16 + 2*len(sc.GapACKs)
+	for i, v := range sc.DuplicateTSNs {
+		binary.BigEndian.PutUint32(bytes[offset+i*4:], v)
+	}
+	return nil
+}
+
+// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and
+// heartbeat ack layers.
+type SCTPHeartbeatParameter SCTPParameter
+
+// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack.
+type SCTPHeartbeat struct {
+	SCTPChunk
+	Parameters []SCTPHeartbeatParameter
+}
+
+// LayerType returns gopacket.LayerTypeSCTPHeartbeat.
+func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType {
+	if sc.Type == SCTPChunkTypeHeartbeatAck {
+		return LayerTypeSCTPHeartbeatAck
+	}
+	// sc.Type == SCTPChunkTypeHeartbeat
+	return LayerTypeSCTPHeartbeat
+}
+
+func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPHeartbeat{
+		SCTPChunk: chunk,
+	}
+	paramData := data[4:sc.Length]
+	for len(paramData) > 0 {
+		p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData))
+		paramData = paramData[p.ActualLength:]
+		sc.Parameters = append(sc.Parameters, p)
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var payload []byte
+	for _, param := range sc.Parameters {
+		payload = append(payload, SCTPParameter(param).Bytes()...)
+	}
+	length := 4 + len(payload)
+
+	bytes, err := b.PrependBytes(roundUpToNearest4(length))
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+	copy(bytes[4:], payload)
+	return nil
+}
+
+// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers.
+type SCTPErrorParameter SCTPParameter
+
+// SCTPError is the SCTP error layer, also used for SCTP aborts.
+type SCTPError struct {
+	SCTPChunk
+	Parameters []SCTPErrorParameter
+}
+
+// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError.
+func (sc *SCTPError) LayerType() gopacket.LayerType {
+	if sc.Type == SCTPChunkTypeAbort {
+		return LayerTypeSCTPAbort
+	}
+	// sc.Type == SCTPChunkTypeError
+	return LayerTypeSCTPError
+}
+
+func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error {
+	// remarkably similar to decodeSCTPHeartbeat ;)
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPError{
+		SCTPChunk: chunk,
+	}
+	paramData := data[4:sc.Length]
+	for len(paramData) > 0 {
+		p := SCTPErrorParameter(decodeSCTPParameter(paramData))
+		paramData = paramData[p.ActualLength:]
+		sc.Parameters = append(sc.Parameters, p)
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var payload []byte
+	for _, param := range sc.Parameters {
+		payload = append(payload, SCTPParameter(param).Bytes()...)
+	}
+	length := 4 + len(payload)
+
+	bytes, err := b.PrependBytes(roundUpToNearest4(length))
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+	copy(bytes[4:], payload)
+	return nil
+}
+
+// SCTPShutdown is the SCTP shutdown layer.
+type SCTPShutdown struct {
+	SCTPChunk
+	CumulativeTSNAck uint32
+}
+
+// LayerType returns gopacket.LayerTypeSCTPShutdown.
+func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown }
+
+func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPShutdown{
+		SCTPChunk:        chunk,
+		CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], 8)
+	binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
+	return nil
+}
+
+// SCTPShutdownAck is the SCTP shutdown layer.
+type SCTPShutdownAck struct {
+	SCTPChunk
+}
+
+// LayerType returns gopacket.LayerTypeSCTPShutdownAck.
+func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck }
+
+func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPShutdownAck{
+		SCTPChunk: chunk,
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], 4)
+	return nil
+}
+
+// SCTPCookieEcho is the SCTP Cookie Echo layer.
+type SCTPCookieEcho struct {
+	SCTPChunk
+	Cookie []byte
+}
+
+// LayerType returns gopacket.LayerTypeSCTPCookieEcho.
+func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho }
+
+func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPCookieEcho{
+		SCTPChunk: chunk,
+	}
+	sc.Cookie = data[4:sc.Length]
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	length := 4 + len(sc.Cookie)
+	bytes, err := b.PrependBytes(roundUpToNearest4(length))
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
+	copy(bytes[4:], sc.Cookie)
+	return nil
+}
+
+// This struct is used by all empty SCTP chunks (currently CookieAck and
+// ShutdownComplete).
+type SCTPEmptyLayer struct {
+	SCTPChunk
+}
+
+// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or
+// LayerTypeSCTPCookieAck.
+func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType {
+	if sc.Type == SCTPChunkTypeShutdownComplete {
+		return LayerTypeSCTPShutdownComplete
+	}
+	// sc.Type == SCTPChunkTypeCookieAck
+	return LayerTypeSCTPCookieAck
+}
+
+func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error {
+	chunk, err := decodeSCTPChunk(data)
+	if err != nil {
+		return err
+	}
+	sc := &SCTPEmptyLayer{
+		SCTPChunk: chunk,
+	}
+	p.AddLayer(sc)
+	return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
+}
+
+// SerializeTo is for gopacket.SerializableLayer.
+func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(4)
+	if err != nil {
+		return err
+	}
+	bytes[0] = uint8(sc.Type)
+	bytes[1] = sc.Flags
+	binary.BigEndian.PutUint16(bytes[2:4], 4)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/sflow.go b/go-controller/vendor/github.com/google/gopacket/layers/sflow.go
new file mode 100644
index 00000000000..bc1c9733ba5
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/sflow.go
@@ -0,0 +1,2567 @@
+// 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.
+
+/*
+This layer decodes SFlow version 5 datagrams.
+
+The specification can be found here: http://sflow.org/sflow_version_5.txt
+
+Additional developer information about sflow can be found at:
+http://sflow.org/developers/specifications.php
+
+And SFlow in general:
+http://sflow.org/index.php
+
+Two forms of sample data are defined: compact and expanded. The
+Specification has this to say:
+
+    Compact and expand forms of counter and flow samples are defined.
+    An agent must not mix compact/expanded encodings.  If an agent
+    will never use ifIndex numbers >= 2^24 then it must use compact
+    encodings for all interfaces.  Otherwise the expanded formats must
+    be used for all interfaces.
+
+This decoder only supports the compact form, because that is the only
+one for which data was available.
+
+The datagram is composed of one or more samples of type flow or counter,
+and each sample is composed of one or more records describing the sample.
+A sample is a single instance of sampled inforamtion, and each record in
+the sample gives additional / supplimentary information about the sample.
+
+The following sample record types are supported:
+
+	Raw Packet Header
+	opaque = flow_data; enterprise = 0; format = 1
+
+	Extended Switch Data
+	opaque = flow_data; enterprise = 0; format = 1001
+
+	Extended Router Data
+	opaque = flow_data; enterprise = 0; format = 1002
+
+	Extended Gateway Data
+	opaque = flow_data; enterprise = 0; format = 1003
+
+	Extended User Data
+	opaque = flow_data; enterprise = 0; format = 1004
+
+	Extended URL Data
+	opaque = flow_data; enterprise = 0; format = 1005
+
+The following types of counter records are supported:
+
+	Generic Interface Counters - see RFC 2233
+	opaque = counter_data; enterprise = 0; format = 1
+
+	Ethernet Interface Counters - see RFC 2358
+	opaque = counter_data; enterprise = 0; format = 2
+
+SFlow is encoded using XDR (RFC4506). There are a few places
+where the standard 4-byte fields are partitioned into two
+bitfields of different lengths. I'm not sure why the designers
+chose to pack together two values like this in some places, and
+in others they use the entire 4-byte value to store a number that
+will never be more than a few bits. In any case, there are a couple
+of types defined to handle the decoding of these bitfields, and
+that's why they're there. */
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+// SFlowRecord holds both flow sample records and counter sample records.
+// A Record is the structure that actually holds the sampled data
+// and / or counters.
+type SFlowRecord interface {
+}
+
+// SFlowDataSource encodes a 2-bit SFlowSourceFormat in its most significant
+// 2 bits, and an SFlowSourceValue in its least significant 30 bits.
+// These types and values define the meaning of the inteface information
+// presented in the sample metadata.
+type SFlowDataSource int32
+
+func (sdc SFlowDataSource) decode() (SFlowSourceFormat, SFlowSourceValue) {
+	leftField := sdc >> 30
+	rightField := uint32(0x3FFFFFFF) & uint32(sdc)
+	return SFlowSourceFormat(leftField), SFlowSourceValue(rightField)
+}
+
+type SFlowDataSourceExpanded struct {
+	SourceIDClass SFlowSourceFormat
+	SourceIDIndex SFlowSourceValue
+}
+
+func (sdce SFlowDataSourceExpanded) decode() (SFlowSourceFormat, SFlowSourceValue) {
+	leftField := sdce.SourceIDClass >> 30
+	rightField := uint32(0x3FFFFFFF) & uint32(sdce.SourceIDIndex)
+	return SFlowSourceFormat(leftField), SFlowSourceValue(rightField)
+}
+
+type SFlowSourceFormat uint32
+
+type SFlowSourceValue uint32
+
+const (
+	SFlowTypeSingleInterface      SFlowSourceFormat = 0
+	SFlowTypePacketDiscarded      SFlowSourceFormat = 1
+	SFlowTypeMultipleDestinations SFlowSourceFormat = 2
+)
+
+func (sdf SFlowSourceFormat) String() string {
+	switch sdf {
+	case SFlowTypeSingleInterface:
+		return "Single Interface"
+	case SFlowTypePacketDiscarded:
+		return "Packet Discarded"
+	case SFlowTypeMultipleDestinations:
+		return "Multiple Destinations"
+	default:
+		return "UNKNOWN"
+	}
+}
+
+func decodeSFlow(data []byte, p gopacket.PacketBuilder) error {
+	s := &SFlowDatagram{}
+	err := s.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(s)
+	p.SetApplicationLayer(s)
+	return nil
+}
+
+// SFlowDatagram is the outermost container which holds some basic information
+// about the reporting agent, and holds at least one sample record
+type SFlowDatagram struct {
+	BaseLayer
+
+	DatagramVersion uint32
+	AgentAddress    net.IP
+	SubAgentID      uint32
+	SequenceNumber  uint32
+	AgentUptime     uint32
+	SampleCount     uint32
+	FlowSamples     []SFlowFlowSample
+	CounterSamples  []SFlowCounterSample
+}
+
+// An SFlow  datagram's outer container has the following
+// structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |           int sFlow version (2|4|5)           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |   int IP version of the Agent (1=v4|2=v6)     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /    Agent IP address (v4=4byte|v6=16byte)      /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int sub agent id                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |         int datagram sequence number          |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |            int switch uptime in ms            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |          int n samples in datagram            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                  n samples                    /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+// SFlowDataFormat encodes the EnterpriseID in the most
+// significant 12 bits, and the SampleType in the least significant
+// 20 bits.
+type SFlowDataFormat uint32
+
+func (sdf SFlowDataFormat) decode() (SFlowEnterpriseID, SFlowSampleType) {
+	leftField := sdf >> 12
+	rightField := uint32(0xFFF) & uint32(sdf)
+	return SFlowEnterpriseID(leftField), SFlowSampleType(rightField)
+}
+
+// SFlowEnterpriseID is used to differentiate between the
+// official SFlow standard, and other, vendor-specific
+// types of flow data. (Similiar to SNMP's enterprise MIB
+// OIDs) Only the office SFlow Enterprise ID is decoded
+// here.
+type SFlowEnterpriseID uint32
+
+const (
+	SFlowStandard SFlowEnterpriseID = 0
+)
+
+func (eid SFlowEnterpriseID) String() string {
+	switch eid {
+	case SFlowStandard:
+		return "Standard SFlow"
+	default:
+		return ""
+	}
+}
+
+func (eid SFlowEnterpriseID) GetType() SFlowEnterpriseID {
+	return SFlowStandard
+}
+
+// SFlowSampleType specifies the type of sample. Only flow samples
+// and counter samples are supported
+type SFlowSampleType uint32
+
+const (
+	SFlowTypeFlowSample            SFlowSampleType = 1
+	SFlowTypeCounterSample         SFlowSampleType = 2
+	SFlowTypeExpandedFlowSample    SFlowSampleType = 3
+	SFlowTypeExpandedCounterSample SFlowSampleType = 4
+)
+
+func (st SFlowSampleType) GetType() SFlowSampleType {
+	switch st {
+	case SFlowTypeFlowSample:
+		return SFlowTypeFlowSample
+	case SFlowTypeCounterSample:
+		return SFlowTypeCounterSample
+	case SFlowTypeExpandedFlowSample:
+		return SFlowTypeExpandedFlowSample
+	case SFlowTypeExpandedCounterSample:
+		return SFlowTypeExpandedCounterSample
+	default:
+		panic("Invalid Sample Type")
+	}
+}
+
+func (st SFlowSampleType) String() string {
+	switch st {
+	case SFlowTypeFlowSample:
+		return "Flow Sample"
+	case SFlowTypeCounterSample:
+		return "Counter Sample"
+	case SFlowTypeExpandedFlowSample:
+		return "Expanded Flow Sample"
+	case SFlowTypeExpandedCounterSample:
+		return "Expanded Counter Sample"
+	default:
+		return ""
+	}
+}
+
+func (s *SFlowDatagram) LayerType() gopacket.LayerType { return LayerTypeSFlow }
+
+func (d *SFlowDatagram) Payload() []byte { return nil }
+
+func (d *SFlowDatagram) CanDecode() gopacket.LayerClass { return LayerTypeSFlow }
+
+func (d *SFlowDatagram) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload }
+
+// SFlowIPType determines what form the IP address being decoded will
+// take. This is an XDR union type allowing for both IPv4 and IPv6
+type SFlowIPType uint32
+
+const (
+	SFlowIPv4 SFlowIPType = 1
+	SFlowIPv6 SFlowIPType = 2
+)
+
+func (s SFlowIPType) String() string {
+	switch s {
+	case SFlowIPv4:
+		return "IPv4"
+	case SFlowIPv6:
+		return "IPv6"
+	default:
+		return ""
+	}
+}
+
+func (s SFlowIPType) Length() int {
+	switch s {
+	case SFlowIPv4:
+		return 4
+	case SFlowIPv6:
+		return 16
+	default:
+		return 0
+	}
+}
+
+func (s *SFlowDatagram) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	var agentAddressType SFlowIPType
+
+	data, s.DatagramVersion = data[4:], binary.BigEndian.Uint32(data[:4])
+	data, agentAddressType = data[4:], SFlowIPType(binary.BigEndian.Uint32(data[:4]))
+	data, s.AgentAddress = data[agentAddressType.Length():], data[:agentAddressType.Length()]
+	data, s.SubAgentID = data[4:], binary.BigEndian.Uint32(data[:4])
+	data, s.SequenceNumber = data[4:], binary.BigEndian.Uint32(data[:4])
+	data, s.AgentUptime = data[4:], binary.BigEndian.Uint32(data[:4])
+	data, s.SampleCount = data[4:], binary.BigEndian.Uint32(data[:4])
+
+	if s.SampleCount < 1 {
+		return fmt.Errorf("SFlow Datagram has invalid sample length: %d", s.SampleCount)
+	}
+	for i := uint32(0); i < s.SampleCount; i++ {
+		sdf := SFlowDataFormat(binary.BigEndian.Uint32(data[:4]))
+		_, sampleType := sdf.decode()
+		switch sampleType {
+		case SFlowTypeFlowSample:
+			if flowSample, err := decodeFlowSample(&data, false); err == nil {
+				s.FlowSamples = append(s.FlowSamples, flowSample)
+			} else {
+				return err
+			}
+		case SFlowTypeCounterSample:
+			if counterSample, err := decodeCounterSample(&data, false); err == nil {
+				s.CounterSamples = append(s.CounterSamples, counterSample)
+			} else {
+				return err
+			}
+		case SFlowTypeExpandedFlowSample:
+			if flowSample, err := decodeFlowSample(&data, true); err == nil {
+				s.FlowSamples = append(s.FlowSamples, flowSample)
+			} else {
+				return err
+			}
+		case SFlowTypeExpandedCounterSample:
+			if counterSample, err := decodeCounterSample(&data, true); err == nil {
+				s.CounterSamples = append(s.CounterSamples, counterSample)
+			} else {
+				return err
+			}
+
+		default:
+			return fmt.Errorf("Unsupported SFlow sample type %d", sampleType)
+		}
+	}
+	return nil
+}
+
+// SFlowFlowSample represents a sampled packet and contains
+// one or more records describing the packet
+type SFlowFlowSample struct {
+	EnterpriseID          SFlowEnterpriseID
+	Format                SFlowSampleType
+	SampleLength          uint32
+	SequenceNumber        uint32
+	SourceIDClass         SFlowSourceFormat
+	SourceIDIndex         SFlowSourceValue
+	SamplingRate          uint32
+	SamplePool            uint32
+	Dropped               uint32
+	InputInterfaceFormat  uint32
+	InputInterface        uint32
+	OutputInterfaceFormat uint32
+	OutputInterface       uint32
+	RecordCount           uint32
+	Records               []SFlowRecord
+}
+
+// Flow samples have the following structure. Note
+// the bit fields to encode the Enterprise ID and the
+// Flow record format: type 1
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  sample length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |          int sample sequence number           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |id type |       src id index value             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int sampling rate               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                int sample pool                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    int drops                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 int input ifIndex             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                int output ifIndex             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int number of records           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                   flow records                /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+// Flow samples have the following structure.
+// Flow record format: type 3
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  sample length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |          int sample sequence number           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int src id type                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |             int src id index value            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int sampling rate               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                int sample pool                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    int drops                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |           int input interface format          |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |           int input interface value           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |           int output interface format         |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |           int output interface value          |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int number of records           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                   flow records                /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowFlowDataFormat uint32
+
+func (fdf SFlowFlowDataFormat) decode() (SFlowEnterpriseID, SFlowFlowRecordType) {
+	leftField := fdf >> 12
+	rightField := uint32(0xFFF) & uint32(fdf)
+	return SFlowEnterpriseID(leftField), SFlowFlowRecordType(rightField)
+}
+
+func (fs SFlowFlowSample) GetRecords() []SFlowRecord {
+	return fs.Records
+}
+
+func (fs SFlowFlowSample) GetType() SFlowSampleType {
+	return SFlowTypeFlowSample
+}
+
+func skipRecord(data *[]byte) {
+	recordLength := int(binary.BigEndian.Uint32((*data)[4:]))
+	*data = (*data)[(recordLength+((4-recordLength)%4))+8:]
+}
+
+func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) {
+	s := SFlowFlowSample{}
+	var sdf SFlowDataFormat
+	*data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	var sdc SFlowDataSource
+
+	s.EnterpriseID, s.Format = sdf.decode()
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if expanded {
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.SourceIDClass = (*data)[4:], SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4]))
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.SourceIDIndex = (*data)[4:], SFlowSourceValue(binary.BigEndian.Uint32((*data)[:4]))
+	} else {
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4]))
+		s.SourceIDClass, s.SourceIDIndex = sdc.decode()
+	}
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.SamplingRate = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.SamplePool = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.Dropped = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	if expanded {
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.InputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.OutputInterfaceFormat = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	} else {
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.InputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		if len(*data) < 4 {
+			return SFlowFlowSample{}, errors.New("ethernet counters too small")
+		}
+		*data, s.OutputInterface = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	}
+	if len(*data) < 4 {
+		return SFlowFlowSample{}, errors.New("ethernet counters too small")
+	}
+	*data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	for i := uint32(0); i < s.RecordCount; i++ {
+		rdf := SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+		enterpriseID, flowRecordType := rdf.decode()
+
+		// Try to decode when EnterpriseID is 0 signaling
+		// default sflow structs are used according specification
+		// Unexpected behavior detected for e.g. with pmacct
+		if enterpriseID == 0 {
+			switch flowRecordType {
+			case SFlowTypeRawPacketFlow:
+				if record, err := decodeRawPacketFlowRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedUserFlow:
+				if record, err := decodeExtendedUserFlow(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedUrlFlow:
+				if record, err := decodeExtendedURLRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedSwitchFlow:
+				if record, err := decodeExtendedSwitchFlowRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedRouterFlow:
+				if record, err := decodeExtendedRouterFlowRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedGatewayFlow:
+				if record, err := decodeExtendedGatewayFlowRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeEthernetFrameFlow:
+				if record, err := decodeEthernetFrameFlowRecord(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeIpv4Flow:
+				if record, err := decodeSFlowIpv4Record(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeIpv6Flow:
+				if record, err := decodeSFlowIpv6Record(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedMlpsFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedMlpsFlow")
+			case SFlowTypeExtendedNatFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedNatFlow")
+			case SFlowTypeExtendedMlpsTunnelFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedMlpsTunnelFlow")
+			case SFlowTypeExtendedMlpsVcFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedMlpsVcFlow")
+			case SFlowTypeExtendedMlpsFecFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedMlpsFecFlow")
+			case SFlowTypeExtendedMlpsLvpFecFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow")
+			case SFlowTypeExtendedVlanFlow:
+				// TODO
+				skipRecord(data)
+				return s, errors.New("skipping TypeExtendedVlanFlow")
+			case SFlowTypeExtendedIpv4TunnelEgressFlow:
+				if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedIpv4TunnelIngressFlow:
+				if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedIpv6TunnelEgressFlow:
+				if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedIpv6TunnelIngressFlow:
+				if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedDecapsulateEgressFlow:
+				if record, err := decodeExtendedDecapsulateEgress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedDecapsulateIngressFlow:
+				if record, err := decodeExtendedDecapsulateIngress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedVniEgressFlow:
+				if record, err := decodeExtendedVniEgress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			case SFlowTypeExtendedVniIngressFlow:
+				if record, err := decodeExtendedVniIngress(data); err == nil {
+					s.Records = append(s.Records, record)
+				} else {
+					return s, err
+				}
+			default:
+				return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType)
+			}
+		} else {
+			skipRecord(data)
+		}
+	}
+	return s, nil
+}
+
+// Counter samples report information about various counter
+// objects. Typically these are items like IfInOctets, or
+// CPU / Memory stats, etc. SFlow will report these at regular
+// intervals as configured on the agent. If one were sufficiently
+// industrious, this could be used to replace the typical
+// SNMP polling used for such things.
+type SFlowCounterSample struct {
+	EnterpriseID   SFlowEnterpriseID
+	Format         SFlowSampleType
+	SampleLength   uint32
+	SequenceNumber uint32
+	SourceIDClass  SFlowSourceFormat
+	SourceIDIndex  SFlowSourceValue
+	RecordCount    uint32
+	Records        []SFlowRecord
+}
+
+// Counter samples have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |          int sample sequence number           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |id type |       src id index value             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               int number of records           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                counter records                /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowCounterDataFormat uint32
+
+func (cdf SFlowCounterDataFormat) decode() (SFlowEnterpriseID, SFlowCounterRecordType) {
+	leftField := cdf >> 12
+	rightField := uint32(0xFFF) & uint32(cdf)
+	return SFlowEnterpriseID(leftField), SFlowCounterRecordType(rightField)
+}
+
+// GetRecords will return a slice of interface types
+// representing records. A type switch can be used to
+// get at the underlying SFlowCounterRecordType.
+func (cs SFlowCounterSample) GetRecords() []SFlowRecord {
+	return cs.Records
+}
+
+// GetType will report the type of sample. Only the
+// compact form of counter samples is supported
+func (cs SFlowCounterSample) GetType() SFlowSampleType {
+	return SFlowTypeCounterSample
+}
+
+type SFlowCounterRecordType uint32
+
+const (
+	SFlowTypeGenericInterfaceCounters   SFlowCounterRecordType = 1
+	SFlowTypeEthernetInterfaceCounters  SFlowCounterRecordType = 2
+	SFlowTypeTokenRingInterfaceCounters SFlowCounterRecordType = 3
+	SFlowType100BaseVGInterfaceCounters SFlowCounterRecordType = 4
+	SFlowTypeVLANCounters               SFlowCounterRecordType = 5
+	SFlowTypeLACPCounters               SFlowCounterRecordType = 7
+	SFlowTypeProcessorCounters          SFlowCounterRecordType = 1001
+	SFlowTypeOpenflowPortCounters       SFlowCounterRecordType = 1004
+	SFlowTypePORTNAMECounters           SFlowCounterRecordType = 1005
+	SFLowTypeAPPRESOURCESCounters       SFlowCounterRecordType = 2203
+	SFlowTypeOVSDPCounters              SFlowCounterRecordType = 2207
+)
+
+func (cr SFlowCounterRecordType) String() string {
+	switch cr {
+	case SFlowTypeGenericInterfaceCounters:
+		return "Generic Interface Counters"
+	case SFlowTypeEthernetInterfaceCounters:
+		return "Ethernet Interface Counters"
+	case SFlowTypeTokenRingInterfaceCounters:
+		return "Token Ring Interface Counters"
+	case SFlowType100BaseVGInterfaceCounters:
+		return "100BaseVG Interface Counters"
+	case SFlowTypeVLANCounters:
+		return "VLAN Counters"
+	case SFlowTypeLACPCounters:
+		return "LACP Counters"
+	case SFlowTypeProcessorCounters:
+		return "Processor Counters"
+	case SFlowTypeOpenflowPortCounters:
+		return "Openflow Port Counters"
+	case SFlowTypePORTNAMECounters:
+		return "PORT NAME Counters"
+	case SFLowTypeAPPRESOURCESCounters:
+		return "App Resources Counters"
+	case SFlowTypeOVSDPCounters:
+		return "OVSDP Counters"
+	default:
+		return ""
+
+	}
+}
+
+func decodeCounterSample(data *[]byte, expanded bool) (SFlowCounterSample, error) {
+	s := SFlowCounterSample{}
+	var sdc SFlowDataSource
+	var sdce SFlowDataSourceExpanded
+	var sdf SFlowDataFormat
+
+	*data, sdf = (*data)[4:], SFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	s.EnterpriseID, s.Format = sdf.decode()
+	*data, s.SampleLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, s.SequenceNumber = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if expanded {
+		*data, sdce = (*data)[8:], SFlowDataSourceExpanded{SFlowSourceFormat(binary.BigEndian.Uint32((*data)[:4])), SFlowSourceValue(binary.BigEndian.Uint32((*data)[4:8]))}
+		s.SourceIDClass, s.SourceIDIndex = sdce.decode()
+	} else {
+		*data, sdc = (*data)[4:], SFlowDataSource(binary.BigEndian.Uint32((*data)[:4]))
+		s.SourceIDClass, s.SourceIDIndex = sdc.decode()
+	}
+	*data, s.RecordCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	for i := uint32(0); i < s.RecordCount; i++ {
+		cdf := SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+		_, counterRecordType := cdf.decode()
+		switch counterRecordType {
+		case SFlowTypeGenericInterfaceCounters:
+			if record, err := decodeGenericInterfaceCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeEthernetInterfaceCounters:
+			if record, err := decodeEthernetCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeTokenRingInterfaceCounters:
+			skipRecord(data)
+			return s, errors.New("skipping TypeTokenRingInterfaceCounters")
+		case SFlowType100BaseVGInterfaceCounters:
+			skipRecord(data)
+			return s, errors.New("skipping Type100BaseVGInterfaceCounters")
+		case SFlowTypeVLANCounters:
+			if record, err := decodeVLANCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeLACPCounters:
+			if record, err := decodeLACPCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeProcessorCounters:
+			if record, err := decodeProcessorCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeOpenflowPortCounters:
+			if record, err := decodeOpenflowportCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypePORTNAMECounters:
+			if record, err := decodePortnameCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFLowTypeAPPRESOURCESCounters:
+			if record, err := decodeAppresourcesCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		case SFlowTypeOVSDPCounters:
+			if record, err := decodeOVSDPCounters(data); err == nil {
+				s.Records = append(s.Records, record)
+			} else {
+				return s, err
+			}
+		default:
+			return s, fmt.Errorf("Invalid counter record type: %d", counterRecordType)
+		}
+	}
+	return s, nil
+}
+
+// SFlowBaseFlowRecord holds the fields common to all records
+// of type SFlowFlowRecordType
+type SFlowBaseFlowRecord struct {
+	EnterpriseID   SFlowEnterpriseID
+	Format         SFlowFlowRecordType
+	FlowDataLength uint32
+}
+
+func (bfr SFlowBaseFlowRecord) GetType() SFlowFlowRecordType {
+	return bfr.Format
+}
+
+// SFlowFlowRecordType denotes what kind of Flow Record is
+// represented. See RFC 3176
+type SFlowFlowRecordType uint32
+
+const (
+	SFlowTypeRawPacketFlow                  SFlowFlowRecordType = 1
+	SFlowTypeEthernetFrameFlow              SFlowFlowRecordType = 2
+	SFlowTypeIpv4Flow                       SFlowFlowRecordType = 3
+	SFlowTypeIpv6Flow                       SFlowFlowRecordType = 4
+	SFlowTypeExtendedSwitchFlow             SFlowFlowRecordType = 1001
+	SFlowTypeExtendedRouterFlow             SFlowFlowRecordType = 1002
+	SFlowTypeExtendedGatewayFlow            SFlowFlowRecordType = 1003
+	SFlowTypeExtendedUserFlow               SFlowFlowRecordType = 1004
+	SFlowTypeExtendedUrlFlow                SFlowFlowRecordType = 1005
+	SFlowTypeExtendedMlpsFlow               SFlowFlowRecordType = 1006
+	SFlowTypeExtendedNatFlow                SFlowFlowRecordType = 1007
+	SFlowTypeExtendedMlpsTunnelFlow         SFlowFlowRecordType = 1008
+	SFlowTypeExtendedMlpsVcFlow             SFlowFlowRecordType = 1009
+	SFlowTypeExtendedMlpsFecFlow            SFlowFlowRecordType = 1010
+	SFlowTypeExtendedMlpsLvpFecFlow         SFlowFlowRecordType = 1011
+	SFlowTypeExtendedVlanFlow               SFlowFlowRecordType = 1012
+	SFlowTypeExtendedIpv4TunnelEgressFlow   SFlowFlowRecordType = 1023
+	SFlowTypeExtendedIpv4TunnelIngressFlow  SFlowFlowRecordType = 1024
+	SFlowTypeExtendedIpv6TunnelEgressFlow   SFlowFlowRecordType = 1025
+	SFlowTypeExtendedIpv6TunnelIngressFlow  SFlowFlowRecordType = 1026
+	SFlowTypeExtendedDecapsulateEgressFlow  SFlowFlowRecordType = 1027
+	SFlowTypeExtendedDecapsulateIngressFlow SFlowFlowRecordType = 1028
+	SFlowTypeExtendedVniEgressFlow          SFlowFlowRecordType = 1029
+	SFlowTypeExtendedVniIngressFlow         SFlowFlowRecordType = 1030
+)
+
+func (rt SFlowFlowRecordType) String() string {
+	switch rt {
+	case SFlowTypeRawPacketFlow:
+		return "Raw Packet Flow Record"
+	case SFlowTypeEthernetFrameFlow:
+		return "Ethernet Frame Flow Record"
+	case SFlowTypeIpv4Flow:
+		return "IPv4 Flow Record"
+	case SFlowTypeIpv6Flow:
+		return "IPv6 Flow Record"
+	case SFlowTypeExtendedSwitchFlow:
+		return "Extended Switch Flow Record"
+	case SFlowTypeExtendedRouterFlow:
+		return "Extended Router Flow Record"
+	case SFlowTypeExtendedGatewayFlow:
+		return "Extended Gateway Flow Record"
+	case SFlowTypeExtendedUserFlow:
+		return "Extended User Flow Record"
+	case SFlowTypeExtendedUrlFlow:
+		return "Extended URL Flow Record"
+	case SFlowTypeExtendedMlpsFlow:
+		return "Extended MPLS Flow Record"
+	case SFlowTypeExtendedNatFlow:
+		return "Extended NAT Flow Record"
+	case SFlowTypeExtendedMlpsTunnelFlow:
+		return "Extended MPLS Tunnel Flow Record"
+	case SFlowTypeExtendedMlpsVcFlow:
+		return "Extended MPLS VC Flow Record"
+	case SFlowTypeExtendedMlpsFecFlow:
+		return "Extended MPLS FEC Flow Record"
+	case SFlowTypeExtendedMlpsLvpFecFlow:
+		return "Extended MPLS LVP FEC Flow Record"
+	case SFlowTypeExtendedVlanFlow:
+		return "Extended VLAN Flow Record"
+	case SFlowTypeExtendedIpv4TunnelEgressFlow:
+		return "Extended IPv4 Tunnel Egress Record"
+	case SFlowTypeExtendedIpv4TunnelIngressFlow:
+		return "Extended IPv4 Tunnel Ingress Record"
+	case SFlowTypeExtendedIpv6TunnelEgressFlow:
+		return "Extended IPv6 Tunnel Egress Record"
+	case SFlowTypeExtendedIpv6TunnelIngressFlow:
+		return "Extended IPv6 Tunnel Ingress Record"
+	case SFlowTypeExtendedDecapsulateEgressFlow:
+		return "Extended Decapsulate Egress Record"
+	case SFlowTypeExtendedDecapsulateIngressFlow:
+		return "Extended Decapsulate Ingress Record"
+	case SFlowTypeExtendedVniEgressFlow:
+		return "Extended VNI Ingress Record"
+	case SFlowTypeExtendedVniIngressFlow:
+		return "Extended VNI Ingress Record"
+	default:
+		return ""
+	}
+}
+
+// SFlowRawPacketFlowRecords hold information about a sampled
+// packet grabbed as it transited the agent. This is
+// perhaps the most useful and interesting record type,
+// as it holds the headers of the sampled packet and
+// can be used to build up a complete picture of the
+// traffic patterns on a network.
+//
+// The raw packet header is sent back into gopacket for
+// decoding, and the resulting gopackt.Packet is stored
+// in the Header member
+type SFlowRawPacketFlowRecord struct {
+	SFlowBaseFlowRecord
+	HeaderProtocol SFlowRawHeaderProtocol
+	FrameLength    uint32
+	PayloadRemoved uint32
+	HeaderLength   uint32
+	Header         gopacket.Packet
+}
+
+// Raw packet record types have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 Header Protocol               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 Frame Length                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 Payload Removed               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 Header Length                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  \                     Header                    \
+//  \                                               \
+//  \                                               \
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowRawHeaderProtocol uint32
+
+const (
+	SFlowProtoEthernet   SFlowRawHeaderProtocol = 1
+	SFlowProtoISO88024   SFlowRawHeaderProtocol = 2
+	SFlowProtoISO88025   SFlowRawHeaderProtocol = 3
+	SFlowProtoFDDI       SFlowRawHeaderProtocol = 4
+	SFlowProtoFrameRelay SFlowRawHeaderProtocol = 5
+	SFlowProtoX25        SFlowRawHeaderProtocol = 6
+	SFlowProtoPPP        SFlowRawHeaderProtocol = 7
+	SFlowProtoSMDS       SFlowRawHeaderProtocol = 8
+	SFlowProtoAAL5       SFlowRawHeaderProtocol = 9
+	SFlowProtoAAL5_IP    SFlowRawHeaderProtocol = 10 /* e.g. Cisco AAL5 mux */
+	SFlowProtoIPv4       SFlowRawHeaderProtocol = 11
+	SFlowProtoIPv6       SFlowRawHeaderProtocol = 12
+	SFlowProtoMPLS       SFlowRawHeaderProtocol = 13
+	SFlowProtoPOS        SFlowRawHeaderProtocol = 14 /* RFC 1662, 2615 */
+)
+
+func (sfhp SFlowRawHeaderProtocol) String() string {
+	switch sfhp {
+	case SFlowProtoEthernet:
+		return "ETHERNET-ISO88023"
+	case SFlowProtoISO88024:
+		return "ISO88024-TOKENBUS"
+	case SFlowProtoISO88025:
+		return "ISO88025-TOKENRING"
+	case SFlowProtoFDDI:
+		return "FDDI"
+	case SFlowProtoFrameRelay:
+		return "FRAME-RELAY"
+	case SFlowProtoX25:
+		return "X25"
+	case SFlowProtoPPP:
+		return "PPP"
+	case SFlowProtoSMDS:
+		return "SMDS"
+	case SFlowProtoAAL5:
+		return "AAL5"
+	case SFlowProtoAAL5_IP:
+		return "AAL5-IP"
+	case SFlowProtoIPv4:
+		return "IPv4"
+	case SFlowProtoIPv6:
+		return "IPv6"
+	case SFlowProtoMPLS:
+		return "MPLS"
+	case SFlowProtoPOS:
+		return "POS"
+	}
+	return "UNKNOWN"
+}
+
+func decodeRawPacketFlowRecord(data *[]byte) (SFlowRawPacketFlowRecord, error) {
+	rec := SFlowRawPacketFlowRecord{}
+	header := []byte{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.HeaderProtocol = (*data)[4:], SFlowRawHeaderProtocol(binary.BigEndian.Uint32((*data)[:4]))
+	*data, rec.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.PayloadRemoved = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.HeaderLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	headerLenWithPadding := int(rec.HeaderLength + ((4 - rec.HeaderLength) % 4))
+	*data, header = (*data)[headerLenWithPadding:], (*data)[:headerLenWithPadding]
+	rec.Header = gopacket.NewPacket(header, LayerTypeEthernet, gopacket.Default)
+	return rec, nil
+}
+
+// SFlowExtendedSwitchFlowRecord give additional information
+// about the sampled packet if it's available. It's mainly
+// useful for getting at the incoming and outgoing VLANs
+// An agent may or may not provide this information.
+type SFlowExtendedSwitchFlowRecord struct {
+	SFlowBaseFlowRecord
+	IncomingVLAN         uint32
+	IncomingVLANPriority uint32
+	OutgoingVLAN         uint32
+	OutgoingVLANPriority uint32
+}
+
+// Extended switch records have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   Incoming VLAN               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Incoming VLAN Priority         |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   Outgoing VLAN               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Outgoing VLAN Priority         |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+func decodeExtendedSwitchFlowRecord(data *[]byte) (SFlowExtendedSwitchFlowRecord, error) {
+	es := SFlowExtendedSwitchFlowRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	es.EnterpriseID, es.Format = fdf.decode()
+	*data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, es.IncomingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, es.IncomingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, es.OutgoingVLAN = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, es.OutgoingVLANPriority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return es, nil
+}
+
+// SFlowExtendedRouterFlowRecord gives additional information
+// about the layer 3 routing information used to forward
+// the packet
+type SFlowExtendedRouterFlowRecord struct {
+	SFlowBaseFlowRecord
+	NextHop                net.IP
+	NextHopSourceMask      uint32
+	NextHopDestinationMask uint32
+}
+
+// Extended router records have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |   IP version of next hop router (1=v4|2=v6)   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /     Next Hop address (v4=4byte|v6=16byte)     /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |              Next Hop Source Mask             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |              Next Hop Destination Mask        |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+func decodeExtendedRouterFlowRecord(data *[]byte) (SFlowExtendedRouterFlowRecord, error) {
+	er := SFlowExtendedRouterFlowRecord{}
+	var fdf SFlowFlowDataFormat
+	var extendedRouterAddressType SFlowIPType
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	er.EnterpriseID, er.Format = fdf.decode()
+	*data, er.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, extendedRouterAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4]))
+	*data, er.NextHop = (*data)[extendedRouterAddressType.Length():], (*data)[:extendedRouterAddressType.Length()]
+	*data, er.NextHopSourceMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, er.NextHopDestinationMask = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return er, nil
+}
+
+// SFlowExtendedGatewayFlowRecord describes information treasured by
+// nework engineers everywhere: AS path information listing which
+// BGP peer sent the packet, and various other BGP related info.
+// This information is vital because it gives a picture of how much
+// traffic is being sent from / received by various BGP peers.
+
+// Extended gateway records have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |   IP version of next hop router (1=v4|2=v6)   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /     Next Hop address (v4=4byte|v6=16byte)     /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                       AS                      |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  Source AS                    |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    Peer AS                    |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  AS Path Count                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                AS Path / Sequence             /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                   Communities                 /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    Local Pref                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+// AS Path / Sequence:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |     AS Source Type (Path=1 / Sequence=2)      |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |              Path / Sequence length           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /              Path / Sequence Members          /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+// Communities:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                communitiy length              |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /              communitiy Members               /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowExtendedGatewayFlowRecord struct {
+	SFlowBaseFlowRecord
+	NextHop     net.IP
+	AS          uint32
+	SourceAS    uint32
+	PeerAS      uint32
+	ASPathCount uint32
+	ASPath      []SFlowASDestination
+	Communities []uint32
+	LocalPref   uint32
+}
+
+type SFlowASPathType uint32
+
+const (
+	SFlowASSet      SFlowASPathType = 1
+	SFlowASSequence SFlowASPathType = 2
+)
+
+func (apt SFlowASPathType) String() string {
+	switch apt {
+	case SFlowASSet:
+		return "AS Set"
+	case SFlowASSequence:
+		return "AS Sequence"
+	default:
+		return ""
+	}
+}
+
+type SFlowASDestination struct {
+	Type    SFlowASPathType
+	Count   uint32
+	Members []uint32
+}
+
+func (asd SFlowASDestination) String() string {
+	switch asd.Type {
+	case SFlowASSet:
+		return fmt.Sprint("AS Set:", asd.Members)
+	case SFlowASSequence:
+		return fmt.Sprint("AS Sequence:", asd.Members)
+	default:
+		return ""
+	}
+}
+
+func (ad *SFlowASDestination) decodePath(data *[]byte) {
+	*data, ad.Type = (*data)[4:], SFlowASPathType(binary.BigEndian.Uint32((*data)[:4]))
+	*data, ad.Count = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	ad.Members = make([]uint32, ad.Count)
+	for i := uint32(0); i < ad.Count; i++ {
+		var member uint32
+		*data, member = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		ad.Members[i] = member
+	}
+}
+
+func decodeExtendedGatewayFlowRecord(data *[]byte) (SFlowExtendedGatewayFlowRecord, error) {
+	eg := SFlowExtendedGatewayFlowRecord{}
+	var fdf SFlowFlowDataFormat
+	var extendedGatewayAddressType SFlowIPType
+	var communitiesLength uint32
+	var community uint32
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	eg.EnterpriseID, eg.Format = fdf.decode()
+	*data, eg.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, extendedGatewayAddressType = (*data)[4:], SFlowIPType(binary.BigEndian.Uint32((*data)[:4]))
+	*data, eg.NextHop = (*data)[extendedGatewayAddressType.Length():], (*data)[:extendedGatewayAddressType.Length()]
+	*data, eg.AS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, eg.SourceAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, eg.PeerAS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, eg.ASPathCount = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	for i := uint32(0); i < eg.ASPathCount; i++ {
+		asPath := SFlowASDestination{}
+		asPath.decodePath(data)
+		eg.ASPath = append(eg.ASPath, asPath)
+	}
+	*data, communitiesLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	eg.Communities = make([]uint32, communitiesLength)
+	for j := uint32(0); j < communitiesLength; j++ {
+		*data, community = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+		eg.Communities[j] = community
+	}
+	*data, eg.LocalPref = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return eg, nil
+}
+
+// **************************************************
+//  Extended URL Flow Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   direction                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                      URL                      |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                      Host                     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowURLDirection uint32
+
+const (
+	SFlowURLsrc SFlowURLDirection = 1
+	SFlowURLdst SFlowURLDirection = 2
+)
+
+func (urld SFlowURLDirection) String() string {
+	switch urld {
+	case SFlowURLsrc:
+		return "Source address is the server"
+	case SFlowURLdst:
+		return "Destination address is the server"
+	default:
+		return ""
+	}
+}
+
+type SFlowExtendedURLRecord struct {
+	SFlowBaseFlowRecord
+	Direction SFlowURLDirection
+	URL       string
+	Host      string
+}
+
+func decodeExtendedURLRecord(data *[]byte) (SFlowExtendedURLRecord, error) {
+	eur := SFlowExtendedURLRecord{}
+	var fdf SFlowFlowDataFormat
+	var urlLen uint32
+	var urlLenWithPad int
+	var hostLen uint32
+	var hostLenWithPad int
+	var urlBytes []byte
+	var hostBytes []byte
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	eur.EnterpriseID, eur.Format = fdf.decode()
+	*data, eur.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, eur.Direction = (*data)[4:], SFlowURLDirection(binary.BigEndian.Uint32((*data)[:4]))
+	*data, urlLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	urlLenWithPad = int(urlLen + ((4 - urlLen) % 4))
+	*data, urlBytes = (*data)[urlLenWithPad:], (*data)[:urlLenWithPad]
+	eur.URL = string(urlBytes[:urlLen])
+	*data, hostLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	hostLenWithPad = int(hostLen + ((4 - hostLen) % 4))
+	*data, hostBytes = (*data)[hostLenWithPad:], (*data)[:hostLenWithPad]
+	eur.Host = string(hostBytes[:hostLen])
+	return eur, nil
+}
+
+// **************************************************
+//  Extended User Flow Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Source Character Set           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 Source User Id                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |              Destination Character Set        |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               Destination User ID             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowExtendedUserFlow struct {
+	SFlowBaseFlowRecord
+	SourceCharSet      SFlowCharSet
+	SourceUserID       string
+	DestinationCharSet SFlowCharSet
+	DestinationUserID  string
+}
+
+type SFlowCharSet uint32
+
+const (
+	SFlowCSunknown                 SFlowCharSet = 2
+	SFlowCSASCII                   SFlowCharSet = 3
+	SFlowCSISOLatin1               SFlowCharSet = 4
+	SFlowCSISOLatin2               SFlowCharSet = 5
+	SFlowCSISOLatin3               SFlowCharSet = 6
+	SFlowCSISOLatin4               SFlowCharSet = 7
+	SFlowCSISOLatinCyrillic        SFlowCharSet = 8
+	SFlowCSISOLatinArabic          SFlowCharSet = 9
+	SFlowCSISOLatinGreek           SFlowCharSet = 10
+	SFlowCSISOLatinHebrew          SFlowCharSet = 11
+	SFlowCSISOLatin5               SFlowCharSet = 12
+	SFlowCSISOLatin6               SFlowCharSet = 13
+	SFlowCSISOTextComm             SFlowCharSet = 14
+	SFlowCSHalfWidthKatakana       SFlowCharSet = 15
+	SFlowCSJISEncoding             SFlowCharSet = 16
+	SFlowCSShiftJIS                SFlowCharSet = 17
+	SFlowCSEUCPkdFmtJapanese       SFlowCharSet = 18
+	SFlowCSEUCFixWidJapanese       SFlowCharSet = 19
+	SFlowCSISO4UnitedKingdom       SFlowCharSet = 20
+	SFlowCSISO11SwedishForNames    SFlowCharSet = 21
+	SFlowCSISO15Italian            SFlowCharSet = 22
+	SFlowCSISO17Spanish            SFlowCharSet = 23
+	SFlowCSISO21German             SFlowCharSet = 24
+	SFlowCSISO60DanishNorwegian    SFlowCharSet = 25
+	SFlowCSISO69French             SFlowCharSet = 26
+	SFlowCSISO10646UTF1            SFlowCharSet = 27
+	SFlowCSISO646basic1983         SFlowCharSet = 28
+	SFlowCSINVARIANT               SFlowCharSet = 29
+	SFlowCSISO2IntlRefVersion      SFlowCharSet = 30
+	SFlowCSNATSSEFI                SFlowCharSet = 31
+	SFlowCSNATSSEFIADD             SFlowCharSet = 32
+	SFlowCSNATSDANO                SFlowCharSet = 33
+	SFlowCSNATSDANOADD             SFlowCharSet = 34
+	SFlowCSISO10Swedish            SFlowCharSet = 35
+	SFlowCSKSC56011987             SFlowCharSet = 36
+	SFlowCSISO2022KR               SFlowCharSet = 37
+	SFlowCSEUCKR                   SFlowCharSet = 38
+	SFlowCSISO2022JP               SFlowCharSet = 39
+	SFlowCSISO2022JP2              SFlowCharSet = 40
+	SFlowCSISO13JISC6220jp         SFlowCharSet = 41
+	SFlowCSISO14JISC6220ro         SFlowCharSet = 42
+	SFlowCSISO16Portuguese         SFlowCharSet = 43
+	SFlowCSISO18Greek7Old          SFlowCharSet = 44
+	SFlowCSISO19LatinGreek         SFlowCharSet = 45
+	SFlowCSISO25French             SFlowCharSet = 46
+	SFlowCSISO27LatinGreek1        SFlowCharSet = 47
+	SFlowCSISO5427Cyrillic         SFlowCharSet = 48
+	SFlowCSISO42JISC62261978       SFlowCharSet = 49
+	SFlowCSISO47BSViewdata         SFlowCharSet = 50
+	SFlowCSISO49INIS               SFlowCharSet = 51
+	SFlowCSISO50INIS8              SFlowCharSet = 52
+	SFlowCSISO51INISCyrillic       SFlowCharSet = 53
+	SFlowCSISO54271981             SFlowCharSet = 54
+	SFlowCSISO5428Greek            SFlowCharSet = 55
+	SFlowCSISO57GB1988             SFlowCharSet = 56
+	SFlowCSISO58GB231280           SFlowCharSet = 57
+	SFlowCSISO61Norwegian2         SFlowCharSet = 58
+	SFlowCSISO70VideotexSupp1      SFlowCharSet = 59
+	SFlowCSISO84Portuguese2        SFlowCharSet = 60
+	SFlowCSISO85Spanish2           SFlowCharSet = 61
+	SFlowCSISO86Hungarian          SFlowCharSet = 62
+	SFlowCSISO87JISX0208           SFlowCharSet = 63
+	SFlowCSISO88Greek7             SFlowCharSet = 64
+	SFlowCSISO89ASMO449            SFlowCharSet = 65
+	SFlowCSISO90                   SFlowCharSet = 66
+	SFlowCSISO91JISC62291984a      SFlowCharSet = 67
+	SFlowCSISO92JISC62991984b      SFlowCharSet = 68
+	SFlowCSISO93JIS62291984badd    SFlowCharSet = 69
+	SFlowCSISO94JIS62291984hand    SFlowCharSet = 70
+	SFlowCSISO95JIS62291984handadd SFlowCharSet = 71
+	SFlowCSISO96JISC62291984kana   SFlowCharSet = 72
+	SFlowCSISO2033                 SFlowCharSet = 73
+	SFlowCSISO99NAPLPS             SFlowCharSet = 74
+	SFlowCSISO102T617bit           SFlowCharSet = 75
+	SFlowCSISO103T618bit           SFlowCharSet = 76
+	SFlowCSISO111ECMACyrillic      SFlowCharSet = 77
+	SFlowCSa71                     SFlowCharSet = 78
+	SFlowCSa72                     SFlowCharSet = 79
+	SFlowCSISO123CSAZ24341985gr    SFlowCharSet = 80
+	SFlowCSISO88596E               SFlowCharSet = 81
+	SFlowCSISO88596I               SFlowCharSet = 82
+	SFlowCSISO128T101G2            SFlowCharSet = 83
+	SFlowCSISO88598E               SFlowCharSet = 84
+	SFlowCSISO88598I               SFlowCharSet = 85
+	SFlowCSISO139CSN369103         SFlowCharSet = 86
+	SFlowCSISO141JUSIB1002         SFlowCharSet = 87
+	SFlowCSISO143IECP271           SFlowCharSet = 88
+	SFlowCSISO146Serbian           SFlowCharSet = 89
+	SFlowCSISO147Macedonian        SFlowCharSet = 90
+	SFlowCSISO150                  SFlowCharSet = 91
+	SFlowCSISO151Cuba              SFlowCharSet = 92
+	SFlowCSISO6937Add              SFlowCharSet = 93
+	SFlowCSISO153GOST1976874       SFlowCharSet = 94
+	SFlowCSISO8859Supp             SFlowCharSet = 95
+	SFlowCSISO10367Box             SFlowCharSet = 96
+	SFlowCSISO158Lap               SFlowCharSet = 97
+	SFlowCSISO159JISX02121990      SFlowCharSet = 98
+	SFlowCSISO646Danish            SFlowCharSet = 99
+	SFlowCSUSDK                    SFlowCharSet = 100
+	SFlowCSDKUS                    SFlowCharSet = 101
+	SFlowCSKSC5636                 SFlowCharSet = 102
+	SFlowCSUnicode11UTF7           SFlowCharSet = 103
+	SFlowCSISO2022CN               SFlowCharSet = 104
+	SFlowCSISO2022CNEXT            SFlowCharSet = 105
+	SFlowCSUTF8                    SFlowCharSet = 106
+	SFlowCSISO885913               SFlowCharSet = 109
+	SFlowCSISO885914               SFlowCharSet = 110
+	SFlowCSISO885915               SFlowCharSet = 111
+	SFlowCSISO885916               SFlowCharSet = 112
+	SFlowCSGBK                     SFlowCharSet = 113
+	SFlowCSGB18030                 SFlowCharSet = 114
+	SFlowCSOSDEBCDICDF0415         SFlowCharSet = 115
+	SFlowCSOSDEBCDICDF03IRV        SFlowCharSet = 116
+	SFlowCSOSDEBCDICDF041          SFlowCharSet = 117
+	SFlowCSISO115481               SFlowCharSet = 118
+	SFlowCSKZ1048                  SFlowCharSet = 119
+	SFlowCSUnicode                 SFlowCharSet = 1000
+	SFlowCSUCS4                    SFlowCharSet = 1001
+	SFlowCSUnicodeASCII            SFlowCharSet = 1002
+	SFlowCSUnicodeLatin1           SFlowCharSet = 1003
+	SFlowCSUnicodeJapanese         SFlowCharSet = 1004
+	SFlowCSUnicodeIBM1261          SFlowCharSet = 1005
+	SFlowCSUnicodeIBM1268          SFlowCharSet = 1006
+	SFlowCSUnicodeIBM1276          SFlowCharSet = 1007
+	SFlowCSUnicodeIBM1264          SFlowCharSet = 1008
+	SFlowCSUnicodeIBM1265          SFlowCharSet = 1009
+	SFlowCSUnicode11               SFlowCharSet = 1010
+	SFlowCSSCSU                    SFlowCharSet = 1011
+	SFlowCSUTF7                    SFlowCharSet = 1012
+	SFlowCSUTF16BE                 SFlowCharSet = 1013
+	SFlowCSUTF16LE                 SFlowCharSet = 1014
+	SFlowCSUTF16                   SFlowCharSet = 1015
+	SFlowCSCESU8                   SFlowCharSet = 1016
+	SFlowCSUTF32                   SFlowCharSet = 1017
+	SFlowCSUTF32BE                 SFlowCharSet = 1018
+	SFlowCSUTF32LE                 SFlowCharSet = 1019
+	SFlowCSBOCU1                   SFlowCharSet = 1020
+	SFlowCSWindows30Latin1         SFlowCharSet = 2000
+	SFlowCSWindows31Latin1         SFlowCharSet = 2001
+	SFlowCSWindows31Latin2         SFlowCharSet = 2002
+	SFlowCSWindows31Latin5         SFlowCharSet = 2003
+	SFlowCSHPRoman8                SFlowCharSet = 2004
+	SFlowCSAdobeStandardEncoding   SFlowCharSet = 2005
+	SFlowCSVenturaUS               SFlowCharSet = 2006
+	SFlowCSVenturaInternational    SFlowCharSet = 2007
+	SFlowCSDECMCS                  SFlowCharSet = 2008
+	SFlowCSPC850Multilingual       SFlowCharSet = 2009
+	SFlowCSPCp852                  SFlowCharSet = 2010
+	SFlowCSPC8CodePage437          SFlowCharSet = 2011
+	SFlowCSPC8DanishNorwegian      SFlowCharSet = 2012
+	SFlowCSPC862LatinHebrew        SFlowCharSet = 2013
+	SFlowCSPC8Turkish              SFlowCharSet = 2014
+	SFlowCSIBMSymbols              SFlowCharSet = 2015
+	SFlowCSIBMThai                 SFlowCharSet = 2016
+	SFlowCSHPLegal                 SFlowCharSet = 2017
+	SFlowCSHPPiFont                SFlowCharSet = 2018
+	SFlowCSHPMath8                 SFlowCharSet = 2019
+	SFlowCSHPPSMath                SFlowCharSet = 2020
+	SFlowCSHPDesktop               SFlowCharSet = 2021
+	SFlowCSVenturaMath             SFlowCharSet = 2022
+	SFlowCSMicrosoftPublishing     SFlowCharSet = 2023
+	SFlowCSWindows31J              SFlowCharSet = 2024
+	SFlowCSGB2312                  SFlowCharSet = 2025
+	SFlowCSBig5                    SFlowCharSet = 2026
+	SFlowCSMacintosh               SFlowCharSet = 2027
+	SFlowCSIBM037                  SFlowCharSet = 2028
+	SFlowCSIBM038                  SFlowCharSet = 2029
+	SFlowCSIBM273                  SFlowCharSet = 2030
+	SFlowCSIBM274                  SFlowCharSet = 2031
+	SFlowCSIBM275                  SFlowCharSet = 2032
+	SFlowCSIBM277                  SFlowCharSet = 2033
+	SFlowCSIBM278                  SFlowCharSet = 2034
+	SFlowCSIBM280                  SFlowCharSet = 2035
+	SFlowCSIBM281                  SFlowCharSet = 2036
+	SFlowCSIBM284                  SFlowCharSet = 2037
+	SFlowCSIBM285                  SFlowCharSet = 2038
+	SFlowCSIBM290                  SFlowCharSet = 2039
+	SFlowCSIBM297                  SFlowCharSet = 2040
+	SFlowCSIBM420                  SFlowCharSet = 2041
+	SFlowCSIBM423                  SFlowCharSet = 2042
+	SFlowCSIBM424                  SFlowCharSet = 2043
+	SFlowCSIBM500                  SFlowCharSet = 2044
+	SFlowCSIBM851                  SFlowCharSet = 2045
+	SFlowCSIBM855                  SFlowCharSet = 2046
+	SFlowCSIBM857                  SFlowCharSet = 2047
+	SFlowCSIBM860                  SFlowCharSet = 2048
+	SFlowCSIBM861                  SFlowCharSet = 2049
+	SFlowCSIBM863                  SFlowCharSet = 2050
+	SFlowCSIBM864                  SFlowCharSet = 2051
+	SFlowCSIBM865                  SFlowCharSet = 2052
+	SFlowCSIBM868                  SFlowCharSet = 2053
+	SFlowCSIBM869                  SFlowCharSet = 2054
+	SFlowCSIBM870                  SFlowCharSet = 2055
+	SFlowCSIBM871                  SFlowCharSet = 2056
+	SFlowCSIBM880                  SFlowCharSet = 2057
+	SFlowCSIBM891                  SFlowCharSet = 2058
+	SFlowCSIBM903                  SFlowCharSet = 2059
+	SFlowCSIBBM904                 SFlowCharSet = 2060
+	SFlowCSIBM905                  SFlowCharSet = 2061
+	SFlowCSIBM918                  SFlowCharSet = 2062
+	SFlowCSIBM1026                 SFlowCharSet = 2063
+	SFlowCSIBMEBCDICATDE           SFlowCharSet = 2064
+	SFlowCSEBCDICATDEA             SFlowCharSet = 2065
+	SFlowCSEBCDICCAFR              SFlowCharSet = 2066
+	SFlowCSEBCDICDKNO              SFlowCharSet = 2067
+	SFlowCSEBCDICDKNOA             SFlowCharSet = 2068
+	SFlowCSEBCDICFISE              SFlowCharSet = 2069
+	SFlowCSEBCDICFISEA             SFlowCharSet = 2070
+	SFlowCSEBCDICFR                SFlowCharSet = 2071
+	SFlowCSEBCDICIT                SFlowCharSet = 2072
+	SFlowCSEBCDICPT                SFlowCharSet = 2073
+	SFlowCSEBCDICES                SFlowCharSet = 2074
+	SFlowCSEBCDICESA               SFlowCharSet = 2075
+	SFlowCSEBCDICESS               SFlowCharSet = 2076
+	SFlowCSEBCDICUK                SFlowCharSet = 2077
+	SFlowCSEBCDICUS                SFlowCharSet = 2078
+	SFlowCSUnknown8BiT             SFlowCharSet = 2079
+	SFlowCSMnemonic                SFlowCharSet = 2080
+	SFlowCSMnem                    SFlowCharSet = 2081
+	SFlowCSVISCII                  SFlowCharSet = 2082
+	SFlowCSVIQR                    SFlowCharSet = 2083
+	SFlowCSKOI8R                   SFlowCharSet = 2084
+	SFlowCSHZGB2312                SFlowCharSet = 2085
+	SFlowCSIBM866                  SFlowCharSet = 2086
+	SFlowCSPC775Baltic             SFlowCharSet = 2087
+	SFlowCSKOI8U                   SFlowCharSet = 2088
+	SFlowCSIBM00858                SFlowCharSet = 2089
+	SFlowCSIBM00924                SFlowCharSet = 2090
+	SFlowCSIBM01140                SFlowCharSet = 2091
+	SFlowCSIBM01141                SFlowCharSet = 2092
+	SFlowCSIBM01142                SFlowCharSet = 2093
+	SFlowCSIBM01143                SFlowCharSet = 2094
+	SFlowCSIBM01144                SFlowCharSet = 2095
+	SFlowCSIBM01145                SFlowCharSet = 2096
+	SFlowCSIBM01146                SFlowCharSet = 2097
+	SFlowCSIBM01147                SFlowCharSet = 2098
+	SFlowCSIBM01148                SFlowCharSet = 2099
+	SFlowCSIBM01149                SFlowCharSet = 2100
+	SFlowCSBig5HKSCS               SFlowCharSet = 2101
+	SFlowCSIBM1047                 SFlowCharSet = 2102
+	SFlowCSPTCP154                 SFlowCharSet = 2103
+	SFlowCSAmiga1251               SFlowCharSet = 2104
+	SFlowCSKOI7switched            SFlowCharSet = 2105
+	SFlowCSBRF                     SFlowCharSet = 2106
+	SFlowCSTSCII                   SFlowCharSet = 2107
+	SFlowCSCP51932                 SFlowCharSet = 2108
+	SFlowCSWindows874              SFlowCharSet = 2109
+	SFlowCSWindows1250             SFlowCharSet = 2250
+	SFlowCSWindows1251             SFlowCharSet = 2251
+	SFlowCSWindows1252             SFlowCharSet = 2252
+	SFlowCSWindows1253             SFlowCharSet = 2253
+	SFlowCSWindows1254             SFlowCharSet = 2254
+	SFlowCSWindows1255             SFlowCharSet = 2255
+	SFlowCSWindows1256             SFlowCharSet = 2256
+	SFlowCSWindows1257             SFlowCharSet = 2257
+	SFlowCSWindows1258             SFlowCharSet = 2258
+	SFlowCSTIS620                  SFlowCharSet = 2259
+	SFlowCS50220                   SFlowCharSet = 2260
+	SFlowCSreserved                SFlowCharSet = 3000
+)
+
+func decodeExtendedUserFlow(data *[]byte) (SFlowExtendedUserFlow, error) {
+	eu := SFlowExtendedUserFlow{}
+	var fdf SFlowFlowDataFormat
+	var srcUserLen uint32
+	var srcUserLenWithPad int
+	var srcUserBytes []byte
+	var dstUserLen uint32
+	var dstUserLenWithPad int
+	var dstUserBytes []byte
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	eu.EnterpriseID, eu.Format = fdf.decode()
+	*data, eu.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, eu.SourceCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4]))
+	*data, srcUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	srcUserLenWithPad = int(srcUserLen + ((4 - srcUserLen) % 4))
+	*data, srcUserBytes = (*data)[srcUserLenWithPad:], (*data)[:srcUserLenWithPad]
+	eu.SourceUserID = string(srcUserBytes[:srcUserLen])
+	*data, eu.DestinationCharSet = (*data)[4:], SFlowCharSet(binary.BigEndian.Uint32((*data)[:4]))
+	*data, dstUserLen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	dstUserLenWithPad = int(dstUserLen + ((4 - dstUserLen) % 4))
+	*data, dstUserBytes = (*data)[dstUserLenWithPad:], (*data)[:dstUserLenWithPad]
+	eu.DestinationUserID = string(dstUserBytes[:dstUserLen])
+	return eu, nil
+}
+
+// **************************************************
+//  Packet IP version 4 Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                     Length                    |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    Protocol                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  Source IPv4                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Destination IPv4               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   Source Port                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Destionation Port              |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   TCP Flags                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                      TOS                      |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowIpv4Record struct {
+	// The length of the IP packet excluding ower layer encapsulations
+	Length uint32
+	// IP Protocol type (for example, TCP = 6, UDP = 17)
+	Protocol uint32
+	// Source IP Address
+	IPSrc net.IP
+	// Destination IP Address
+	IPDst net.IP
+	// TCP/UDP source port number or equivalent
+	PortSrc uint32
+	// TCP/UDP destination port number or equivalent
+	PortDst uint32
+	// TCP flags
+	TCPFlags uint32
+	// IP type of service
+	TOS uint32
+}
+
+func decodeSFlowIpv4Record(data *[]byte) (SFlowIpv4Record, error) {
+	si := SFlowIpv4Record{}
+
+	*data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.IPSrc = (*data)[4:], net.IP((*data)[:4])
+	*data, si.IPDst = (*data)[4:], net.IP((*data)[:4])
+	*data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.TOS = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return si, nil
+}
+
+// **************************************************
+//  Packet IP version 6 Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                     Length                    |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    Protocol                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  Source IPv4                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Destination IPv4               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   Source Port                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Destionation Port              |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   TCP Flags                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    Priority                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowIpv6Record struct {
+	// The length of the IP packet excluding ower layer encapsulations
+	Length uint32
+	// IP Protocol type (for example, TCP = 6, UDP = 17)
+	Protocol uint32
+	// Source IP Address
+	IPSrc net.IP
+	// Destination IP Address
+	IPDst net.IP
+	// TCP/UDP source port number or equivalent
+	PortSrc uint32
+	// TCP/UDP destination port number or equivalent
+	PortDst uint32
+	// TCP flags
+	TCPFlags uint32
+	// IP priority
+	Priority uint32
+}
+
+func decodeSFlowIpv6Record(data *[]byte) (SFlowIpv6Record, error) {
+	si := SFlowIpv6Record{}
+
+	*data, si.Length = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.Protocol = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.IPSrc = (*data)[16:], net.IP((*data)[:16])
+	*data, si.IPDst = (*data)[16:], net.IP((*data)[:16])
+	*data, si.PortSrc = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.PortDst = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.TCPFlags = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, si.Priority = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return si, nil
+}
+
+// **************************************************
+//  Extended IPv4 Tunnel Egress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /           Packet IP version 4 Record          /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedIpv4TunnelEgressRecord struct {
+	SFlowBaseFlowRecord
+	SFlowIpv4Record SFlowIpv4Record
+}
+
+func decodeExtendedIpv4TunnelEgress(data *[]byte) (SFlowExtendedIpv4TunnelEgressRecord, error) {
+	rec := SFlowExtendedIpv4TunnelEgressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data)
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended IPv4 Tunnel Ingress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /           Packet IP version 4 Record          /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedIpv4TunnelIngressRecord struct {
+	SFlowBaseFlowRecord
+	SFlowIpv4Record SFlowIpv4Record
+}
+
+func decodeExtendedIpv4TunnelIngress(data *[]byte) (SFlowExtendedIpv4TunnelIngressRecord, error) {
+	rec := SFlowExtendedIpv4TunnelIngressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	rec.SFlowIpv4Record, _ = decodeSFlowIpv4Record(data)
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended IPv6 Tunnel Egress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /           Packet IP version 6 Record          /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedIpv6TunnelEgressRecord struct {
+	SFlowBaseFlowRecord
+	SFlowIpv6Record
+}
+
+func decodeExtendedIpv6TunnelEgress(data *[]byte) (SFlowExtendedIpv6TunnelEgressRecord, error) {
+	rec := SFlowExtendedIpv6TunnelEgressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data)
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended IPv6 Tunnel Ingress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /           Packet IP version 6 Record          /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedIpv6TunnelIngressRecord struct {
+	SFlowBaseFlowRecord
+	SFlowIpv6Record
+}
+
+func decodeExtendedIpv6TunnelIngress(data *[]byte) (SFlowExtendedIpv6TunnelIngressRecord, error) {
+	rec := SFlowExtendedIpv6TunnelIngressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	rec.SFlowIpv6Record, _ = decodeSFlowIpv6Record(data)
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended Decapsulate Egress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               Inner Header Offset             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedDecapsulateEgressRecord struct {
+	SFlowBaseFlowRecord
+	InnerHeaderOffset uint32
+}
+
+func decodeExtendedDecapsulateEgress(data *[]byte) (SFlowExtendedDecapsulateEgressRecord, error) {
+	rec := SFlowExtendedDecapsulateEgressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended Decapsulate Ingress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               Inner Header Offset             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedDecapsulateIngressRecord struct {
+	SFlowBaseFlowRecord
+	InnerHeaderOffset uint32
+}
+
+func decodeExtendedDecapsulateIngress(data *[]byte) (SFlowExtendedDecapsulateIngressRecord, error) {
+	rec := SFlowExtendedDecapsulateIngressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.InnerHeaderOffset = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended VNI Egress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                       VNI                     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedVniEgressRecord struct {
+	SFlowBaseFlowRecord
+	VNI uint32
+}
+
+func decodeExtendedVniEgress(data *[]byte) (SFlowExtendedVniEgressRecord, error) {
+	rec := SFlowExtendedVniEgressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return rec, nil
+}
+
+// **************************************************
+//  Extended VNI Ingress
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                       VNI                     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+type SFlowExtendedVniIngressRecord struct {
+	SFlowBaseFlowRecord
+	VNI uint32
+}
+
+func decodeExtendedVniIngress(data *[]byte) (SFlowExtendedVniIngressRecord, error) {
+	rec := SFlowExtendedVniIngressRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	rec.EnterpriseID, rec.Format = fdf.decode()
+	*data, rec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, rec.VNI = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return rec, nil
+}
+
+// **************************************************
+//  Counter Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  counter length               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                   counter data                /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowBaseCounterRecord struct {
+	EnterpriseID   SFlowEnterpriseID
+	Format         SFlowCounterRecordType
+	FlowDataLength uint32
+}
+
+func (bcr SFlowBaseCounterRecord) GetType() SFlowCounterRecordType {
+	switch bcr.Format {
+	case SFlowTypeGenericInterfaceCounters:
+		return SFlowTypeGenericInterfaceCounters
+	case SFlowTypeEthernetInterfaceCounters:
+		return SFlowTypeEthernetInterfaceCounters
+	case SFlowTypeTokenRingInterfaceCounters:
+		return SFlowTypeTokenRingInterfaceCounters
+	case SFlowType100BaseVGInterfaceCounters:
+		return SFlowType100BaseVGInterfaceCounters
+	case SFlowTypeVLANCounters:
+		return SFlowTypeVLANCounters
+	case SFlowTypeLACPCounters:
+		return SFlowTypeLACPCounters
+	case SFlowTypeProcessorCounters:
+		return SFlowTypeProcessorCounters
+	case SFlowTypeOpenflowPortCounters:
+		return SFlowTypeOpenflowPortCounters
+	case SFlowTypePORTNAMECounters:
+		return SFlowTypePORTNAMECounters
+	case SFLowTypeAPPRESOURCESCounters:
+		return SFLowTypeAPPRESOURCESCounters
+	case SFlowTypeOVSDPCounters:
+		return SFlowTypeOVSDPCounters
+	}
+	unrecognized := fmt.Sprint("Unrecognized counter record type:", bcr.Format)
+	panic(unrecognized)
+}
+
+// **************************************************
+//  Counter Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  counter length               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    IfIndex                    |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    IfType                     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfSpeed                     |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfDirection                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    IfStatus                   |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IFInOctets                  |
+//  |                                               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfInUcastPkts               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  IfInMulticastPkts            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  IfInBroadcastPkts            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    IfInDiscards               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    InInErrors                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  IfInUnknownProtos            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfOutOctets                 |
+//  |                                               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfOutUcastPkts              |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  IfOutMulticastPkts           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  IfOutBroadcastPkts           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   IfOutDiscards               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    IfOUtErrors                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                 IfPromiscouousMode            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowGenericInterfaceCounters struct {
+	SFlowBaseCounterRecord
+	IfIndex            uint32
+	IfType             uint32
+	IfSpeed            uint64
+	IfDirection        uint32
+	IfStatus           uint32
+	IfInOctets         uint64
+	IfInUcastPkts      uint32
+	IfInMulticastPkts  uint32
+	IfInBroadcastPkts  uint32
+	IfInDiscards       uint32
+	IfInErrors         uint32
+	IfInUnknownProtos  uint32
+	IfOutOctets        uint64
+	IfOutUcastPkts     uint32
+	IfOutMulticastPkts uint32
+	IfOutBroadcastPkts uint32
+	IfOutDiscards      uint32
+	IfOutErrors        uint32
+	IfPromiscuousMode  uint32
+}
+
+func decodeGenericInterfaceCounters(data *[]byte) (SFlowGenericInterfaceCounters, error) {
+	gic := SFlowGenericInterfaceCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	gic.EnterpriseID, gic.Format = cdf.decode()
+	*data, gic.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfIndex = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfType = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfSpeed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, gic.IfDirection = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfStatus = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, gic.IfInUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfInUnknownProtos = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfOutOctets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, gic.IfOutUcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfOutMulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfOutBroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfOutDiscards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfOutErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, gic.IfPromiscuousMode = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return gic, nil
+}
+
+// **************************************************
+//  Counter Record
+// **************************************************
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  counter length               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  /                   counter data                /
+//  /                                               /
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowEthernetCounters struct {
+	SFlowBaseCounterRecord
+	AlignmentErrors           uint32
+	FCSErrors                 uint32
+	SingleCollisionFrames     uint32
+	MultipleCollisionFrames   uint32
+	SQETestErrors             uint32
+	DeferredTransmissions     uint32
+	LateCollisions            uint32
+	ExcessiveCollisions       uint32
+	InternalMacTransmitErrors uint32
+	CarrierSenseErrors        uint32
+	FrameTooLongs             uint32
+	InternalMacReceiveErrors  uint32
+	SymbolErrors              uint32
+}
+
+func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) {
+	ec := SFlowEthernetCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	ec.EnterpriseID, ec.Format = cdf.decode()
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.AlignmentErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.FCSErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.SingleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.MultipleCollisionFrames = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.SQETestErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.DeferredTransmissions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.LateCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.ExcessiveCollisions = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.InternalMacTransmitErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.CarrierSenseErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.FrameTooLongs = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.InternalMacReceiveErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	if len(*data) < 4 {
+		return SFlowEthernetCounters{}, errors.New("ethernet counters too small")
+	}
+	*data, ec.SymbolErrors = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return ec, nil
+}
+
+// VLAN Counter
+
+type SFlowVLANCounters struct {
+	SFlowBaseCounterRecord
+	VlanID        uint32
+	Octets        uint64
+	UcastPkts     uint32
+	MulticastPkts uint32
+	BroadcastPkts uint32
+	Discards      uint32
+}
+
+func decodeVLANCounters(data *[]byte) (SFlowVLANCounters, error) {
+	vc := SFlowVLANCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	vc.EnterpriseID, vc.Format = cdf.decode()
+	vc.EnterpriseID, vc.Format = cdf.decode()
+	*data, vc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, vc.VlanID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, vc.Octets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, vc.UcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, vc.MulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, vc.BroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, vc.Discards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return vc, nil
+}
+
+//SFLLACPportState  :  SFlow LACP Port State (All(4) - 32 bit)
+type SFLLACPPortState struct {
+	PortStateAll uint32
+}
+
+//LACPcounters  :  LACP SFlow Counters  ( 64 Bytes )
+type SFlowLACPCounters struct {
+	SFlowBaseCounterRecord
+	ActorSystemID        net.HardwareAddr
+	PartnerSystemID      net.HardwareAddr
+	AttachedAggID        uint32
+	LacpPortState        SFLLACPPortState
+	LACPDUsRx            uint32
+	MarkerPDUsRx         uint32
+	MarkerResponsePDUsRx uint32
+	UnknownRx            uint32
+	IllegalRx            uint32
+	LACPDUsTx            uint32
+	MarkerPDUsTx         uint32
+	MarkerResponsePDUsTx uint32
+}
+
+func decodeLACPCounters(data *[]byte) (SFlowLACPCounters, error) {
+	la := SFlowLACPCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	la.EnterpriseID, la.Format = cdf.decode()
+	*data, la.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.ActorSystemID = (*data)[6:], (*data)[:6]
+	*data = (*data)[2:] // remove padding
+	*data, la.PartnerSystemID = (*data)[6:], (*data)[:6]
+	*data = (*data)[2:] //remove padding
+	*data, la.AttachedAggID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.LacpPortState.PortStateAll = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.LACPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.MarkerPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.MarkerResponsePDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.UnknownRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.IllegalRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.LACPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.MarkerPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, la.MarkerResponsePDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return la, nil
+
+}
+
+// **************************************************
+//  Processor Counter Record
+// **************************************************
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  counter length               |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    FiveSecCpu                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    OneMinCpu                  |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    GiveMinCpu                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                   TotalMemory                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                    FreeMemory                 |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+type SFlowProcessorCounters struct {
+	SFlowBaseCounterRecord
+	FiveSecCpu  uint32 // 5 second average CPU utilization
+	OneMinCpu   uint32 // 1 minute average CPU utilization
+	FiveMinCpu  uint32 // 5 minute average CPU utilization
+	TotalMemory uint64 // total memory (in bytes)
+	FreeMemory  uint64 // free memory (in bytes)
+}
+
+func decodeProcessorCounters(data *[]byte) (SFlowProcessorCounters, error) {
+	pc := SFlowProcessorCounters{}
+	var cdf SFlowCounterDataFormat
+	var high32, low32 uint32
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	pc.EnterpriseID, pc.Format = cdf.decode()
+	*data, pc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	*data, pc.FiveSecCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, pc.OneMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, pc.FiveMinCpu = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	pc.TotalMemory = (uint64(high32) << 32) + uint64(low32)
+	*data, high32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, low32 = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	pc.FreeMemory = (uint64(high32)) + uint64(low32)
+
+	return pc, nil
+}
+
+// SFlowEthernetFrameFlowRecord give additional information
+// about the sampled packet if it's available.
+// An agent may or may not provide this information.
+type SFlowEthernetFrameFlowRecord struct {
+	SFlowBaseFlowRecord
+	FrameLength uint32
+	SrcMac      net.HardwareAddr
+	DstMac      net.HardwareAddr
+	Type        uint32
+}
+
+// Ethernet frame flow records have the following structure:
+
+//  0                      15                      31
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |      20 bit Interprise (0)     |12 bit format |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                  record length                |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |                Source Mac Address             |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |             Destination Mac Address           |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+//  |               Ethernet Packet Type            |
+//  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
+func decodeEthernetFrameFlowRecord(data *[]byte) (SFlowEthernetFrameFlowRecord, error) {
+	es := SFlowEthernetFrameFlowRecord{}
+	var fdf SFlowFlowDataFormat
+
+	*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	es.EnterpriseID, es.Format = fdf.decode()
+	*data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	*data, es.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, es.SrcMac = (*data)[8:], net.HardwareAddr((*data)[:6])
+	*data, es.DstMac = (*data)[8:], net.HardwareAddr((*data)[:6])
+	*data, es.Type = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	return es, nil
+}
+
+//SFlowOpenflowPortCounters  :  OVS-Sflow OpenFlow Port Counter  ( 20 Bytes )
+type SFlowOpenflowPortCounters struct {
+	SFlowBaseCounterRecord
+	DatapathID uint64
+	PortNo     uint32
+}
+
+func decodeOpenflowportCounters(data *[]byte) (SFlowOpenflowPortCounters, error) {
+	ofp := SFlowOpenflowPortCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	ofp.EnterpriseID, ofp.Format = cdf.decode()
+	*data, ofp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, ofp.DatapathID = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, ofp.PortNo = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return ofp, nil
+}
+
+//SFlowAppresourcesCounters  :  OVS_Sflow App Resources Counter ( 48 Bytes )
+type SFlowAppresourcesCounters struct {
+	SFlowBaseCounterRecord
+	UserTime   uint32
+	SystemTime uint32
+	MemUsed    uint64
+	MemMax     uint64
+	FdOpen     uint32
+	FdMax      uint32
+	ConnOpen   uint32
+	ConnMax    uint32
+}
+
+func decodeAppresourcesCounters(data *[]byte) (SFlowAppresourcesCounters, error) {
+	app := SFlowAppresourcesCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	app.EnterpriseID, app.Format = cdf.decode()
+	*data, app.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.UserTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.SystemTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.MemUsed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, app.MemMax = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
+	*data, app.FdOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.FdMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.ConnOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, app.ConnMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return app, nil
+}
+
+//SFlowOVSDPCounters  :  OVS-Sflow DataPath Counter  ( 32 Bytes )
+type SFlowOVSDPCounters struct {
+	SFlowBaseCounterRecord
+	NHit     uint32
+	NMissed  uint32
+	NLost    uint32
+	NMaskHit uint32
+	NFlows   uint32
+	NMasks   uint32
+}
+
+func decodeOVSDPCounters(data *[]byte) (SFlowOVSDPCounters, error) {
+	dp := SFlowOVSDPCounters{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	dp.EnterpriseID, dp.Format = cdf.decode()
+	*data, dp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NMissed = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NLost = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NMaskHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NFlows = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	*data, dp.NMasks = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+
+	return dp, nil
+}
+
+//SFlowPORTNAME  :  OVS-Sflow PORTNAME Counter Sampletype ( 20 Bytes )
+type SFlowPORTNAME struct {
+	SFlowBaseCounterRecord
+	Len uint32
+	Str string
+}
+
+func decodeString(data *[]byte) (len uint32, str string) {
+	*data, len = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	str = string((*data)[:len])
+	if (len % 4) != 0 {
+		len += 4 - len%4
+	}
+	*data = (*data)[len:]
+	return
+}
+
+func decodePortnameCounters(data *[]byte) (SFlowPORTNAME, error) {
+	pn := SFlowPORTNAME{}
+	var cdf SFlowCounterDataFormat
+
+	*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
+	pn.EnterpriseID, pn.Format = cdf.decode()
+	*data, pn.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
+	pn.Len, pn.Str = decodeString(data)
+
+	return pn, nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/sip.go b/go-controller/vendor/github.com/google/gopacket/layers/sip.go
new file mode 100644
index 00000000000..70afdb5c064
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/sip.go
@@ -0,0 +1,542 @@
+// 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 (
+	"bytes"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+
+	"github.com/google/gopacket"
+)
+
+// SIPVersion defines the different versions of the SIP Protocol
+type SIPVersion uint8
+
+// Represents all the versions of SIP protocol
+const (
+	SIPVersion1 SIPVersion = 1
+	SIPVersion2 SIPVersion = 2
+)
+
+func (sv SIPVersion) String() string {
+	switch sv {
+	default:
+		// Defaulting to SIP/2.0
+		return "SIP/2.0"
+	case SIPVersion1:
+		return "SIP/1.0"
+	case SIPVersion2:
+		return "SIP/2.0"
+	}
+}
+
+// GetSIPVersion is used to get SIP version constant
+func GetSIPVersion(version string) (SIPVersion, error) {
+	switch strings.ToUpper(version) {
+	case "SIP/1.0":
+		return SIPVersion1, nil
+	case "SIP/2.0":
+		return SIPVersion2, nil
+	default:
+		return 0, fmt.Errorf("Unknown SIP version: '%s'", version)
+
+	}
+}
+
+// SIPMethod defines the different methods of the SIP Protocol
+// defined in the different RFC's
+type SIPMethod uint16
+
+// Here are all the SIP methods
+const (
+	SIPMethodInvite    SIPMethod = 1  // INVITE	[RFC3261]
+	SIPMethodAck       SIPMethod = 2  // ACK	[RFC3261]
+	SIPMethodBye       SIPMethod = 3  // BYE	[RFC3261]
+	SIPMethodCancel    SIPMethod = 4  // CANCEL	[RFC3261]
+	SIPMethodOptions   SIPMethod = 5  // OPTIONS	[RFC3261]
+	SIPMethodRegister  SIPMethod = 6  // REGISTER	[RFC3261]
+	SIPMethodPrack     SIPMethod = 7  // PRACK	[RFC3262]
+	SIPMethodSubscribe SIPMethod = 8  // SUBSCRIBE	[RFC6665]
+	SIPMethodNotify    SIPMethod = 9  // NOTIFY	[RFC6665]
+	SIPMethodPublish   SIPMethod = 10 // PUBLISH	[RFC3903]
+	SIPMethodInfo      SIPMethod = 11 // INFO	[RFC6086]
+	SIPMethodRefer     SIPMethod = 12 // REFER	[RFC3515]
+	SIPMethodMessage   SIPMethod = 13 // MESSAGE	[RFC3428]
+	SIPMethodUpdate    SIPMethod = 14 // UPDATE	[RFC3311]
+	SIPMethodPing      SIPMethod = 15 // PING	[https://tools.ietf.org/html/draft-fwmiller-ping-03]
+)
+
+func (sm SIPMethod) String() string {
+	switch sm {
+	default:
+		return "Unknown method"
+	case SIPMethodInvite:
+		return "INVITE"
+	case SIPMethodAck:
+		return "ACK"
+	case SIPMethodBye:
+		return "BYE"
+	case SIPMethodCancel:
+		return "CANCEL"
+	case SIPMethodOptions:
+		return "OPTIONS"
+	case SIPMethodRegister:
+		return "REGISTER"
+	case SIPMethodPrack:
+		return "PRACK"
+	case SIPMethodSubscribe:
+		return "SUBSCRIBE"
+	case SIPMethodNotify:
+		return "NOTIFY"
+	case SIPMethodPublish:
+		return "PUBLISH"
+	case SIPMethodInfo:
+		return "INFO"
+	case SIPMethodRefer:
+		return "REFER"
+	case SIPMethodMessage:
+		return "MESSAGE"
+	case SIPMethodUpdate:
+		return "UPDATE"
+	case SIPMethodPing:
+		return "PING"
+	}
+}
+
+// GetSIPMethod returns the constant of a SIP method
+// from its string
+func GetSIPMethod(method string) (SIPMethod, error) {
+	switch strings.ToUpper(method) {
+	case "INVITE":
+		return SIPMethodInvite, nil
+	case "ACK":
+		return SIPMethodAck, nil
+	case "BYE":
+		return SIPMethodBye, nil
+	case "CANCEL":
+		return SIPMethodCancel, nil
+	case "OPTIONS":
+		return SIPMethodOptions, nil
+	case "REGISTER":
+		return SIPMethodRegister, nil
+	case "PRACK":
+		return SIPMethodPrack, nil
+	case "SUBSCRIBE":
+		return SIPMethodSubscribe, nil
+	case "NOTIFY":
+		return SIPMethodNotify, nil
+	case "PUBLISH":
+		return SIPMethodPublish, nil
+	case "INFO":
+		return SIPMethodInfo, nil
+	case "REFER":
+		return SIPMethodRefer, nil
+	case "MESSAGE":
+		return SIPMethodMessage, nil
+	case "UPDATE":
+		return SIPMethodUpdate, nil
+	case "PING":
+		return SIPMethodPing, nil
+	default:
+		return 0, fmt.Errorf("Unknown SIP method: '%s'", method)
+	}
+}
+
+// Here is a correspondance between long header names and short
+// as defined in rfc3261 in section 20
+var compactSipHeadersCorrespondance = map[string]string{
+	"accept-contact":      "a",
+	"allow-events":        "u",
+	"call-id":             "i",
+	"contact":             "m",
+	"content-encoding":    "e",
+	"content-length":      "l",
+	"content-type":        "c",
+	"event":               "o",
+	"from":                "f",
+	"identity":            "y",
+	"refer-to":            "r",
+	"referred-by":         "b",
+	"reject-contact":      "j",
+	"request-disposition": "d",
+	"session-expires":     "x",
+	"subject":             "s",
+	"supported":           "k",
+	"to":                  "t",
+	"via":                 "v",
+}
+
+// SIP object will contains information about decoded SIP packet.
+// -> The SIP Version
+// -> The SIP Headers (in a map[string][]string because of multiple headers with the same name
+// -> The SIP Method
+// -> The SIP Response code (if it's a response)
+// -> The SIP Status line (if it's a response)
+// You can easily know the type of the packet with the IsResponse boolean
+//
+type SIP struct {
+	BaseLayer
+
+	// Base information
+	Version SIPVersion
+	Method  SIPMethod
+	Headers map[string][]string
+
+	// Request
+	RequestURI string
+
+	// Response
+	IsResponse     bool
+	ResponseCode   int
+	ResponseStatus string
+
+	// Private fields
+	cseq             int64
+	contentLength    int64
+	lastHeaderParsed string
+}
+
+// decodeSIP decodes the byte slice into a SIP type. It also
+// setups the application Layer in PacketBuilder.
+func decodeSIP(data []byte, p gopacket.PacketBuilder) error {
+	s := NewSIP()
+	err := s.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(s)
+	p.SetApplicationLayer(s)
+	return nil
+}
+
+// NewSIP instantiates a new empty SIP object
+func NewSIP() *SIP {
+	s := new(SIP)
+	s.Headers = make(map[string][]string)
+	return s
+}
+
+// LayerType returns gopacket.LayerTypeSIP.
+func (s *SIP) LayerType() gopacket.LayerType {
+	return LayerTypeSIP
+}
+
+// Payload returns the base layer payload
+func (s *SIP) Payload() []byte {
+	return s.BaseLayer.Payload
+}
+
+// CanDecode returns the set of layer types that this DecodingLayer can decode
+func (s *SIP) CanDecode() gopacket.LayerClass {
+	return LayerTypeSIP
+}
+
+// NextLayerType returns the layer type contained by this DecodingLayer
+func (s *SIP) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+// DecodeFromBytes decodes the slice into the SIP struct.
+func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	// Init some vars for parsing follow-up
+	var countLines int
+	var line []byte
+	var err error
+	var offset int
+
+	// Iterate on all lines of the SIP Headers
+	// and stop when we reach the SDP (aka when the new line
+	// is at index 0 of the remaining packet)
+	buffer := bytes.NewBuffer(data)
+
+	for {
+
+		// Read next line
+		line, err = buffer.ReadBytes(byte('\n'))
+		if err != nil {
+			if err == io.EOF {
+				if len(bytes.Trim(line, "\r\n")) > 0 {
+					df.SetTruncated()
+				}
+				break
+			} else {
+				return err
+			}
+		}
+		offset += len(line)
+
+		// Trim the new line delimiters
+		line = bytes.Trim(line, "\r\n")
+
+		// Empty line, we hit Body
+		if len(line) == 0 {
+			break
+		}
+
+		// First line is the SIP request/response line
+		// Other lines are headers
+		if countLines == 0 {
+			err = s.ParseFirstLine(line)
+			if err != nil {
+				return err
+			}
+
+		} else {
+			err = s.ParseHeader(line)
+			if err != nil {
+				return err
+			}
+		}
+
+		countLines++
+	}
+	s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
+
+	return nil
+}
+
+// ParseFirstLine will compute the first line of a SIP packet.
+// The first line will tell us if it's a request or a response.
+//
+// Examples of first line of SIP Prococol :
+//
+// 	Request 	: INVITE bob@example.com SIP/2.0
+// 	Response 	: SIP/2.0 200 OK
+// 	Response	: SIP/2.0 501 Not Implemented
+//
+func (s *SIP) ParseFirstLine(firstLine []byte) error {
+
+	var err error
+
+	// Splits line by space
+	splits := strings.SplitN(string(firstLine), " ", 3)
+
+	// We must have at least 3 parts
+	if len(splits) < 3 {
+		return fmt.Errorf("invalid first SIP line: '%s'", string(firstLine))
+	}
+
+	// Determine the SIP packet type
+	if strings.HasPrefix(splits[0], "SIP") {
+
+		// --> Response
+		s.IsResponse = true
+
+		// Validate SIP Version
+		s.Version, err = GetSIPVersion(splits[0])
+		if err != nil {
+			return err
+		}
+
+		// Compute code
+		s.ResponseCode, err = strconv.Atoi(splits[1])
+		if err != nil {
+			return err
+		}
+
+		// Compute status line
+		s.ResponseStatus = splits[2]
+
+	} else {
+
+		// --> Request
+
+		// Validate method
+		s.Method, err = GetSIPMethod(splits[0])
+		if err != nil {
+			return err
+		}
+
+		s.RequestURI = splits[1]
+
+		// Validate SIP Version
+		s.Version, err = GetSIPVersion(splits[2])
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ParseHeader will parse a SIP Header
+// SIP Headers are quite simple, there are colon separated name and value
+// Headers can be spread over multiple lines
+//
+// Examples of header :
+//
+//  CSeq: 1 REGISTER
+//  Via: SIP/2.0/UDP there.com:5060
+//  Authorization:Digest username="UserB",
+//	  realm="MCI WorldCom SIP",
+//    nonce="1cec4341ae6cbe5a359ea9c8e88df84f", opaque="",
+//    uri="sip:ss2.wcom.com", response="71ba27c64bd01de719686aa4590d5824"
+//
+func (s *SIP) ParseHeader(header []byte) (err error) {
+
+	// Ignore empty headers
+	if len(header) == 0 {
+		return
+	}
+
+	// Check if this is the following of last header
+	// RFC 3261 - 7.3.1 - Header Field Format specify that following lines of
+	// multiline headers must begin by SP or TAB
+	if header[0] == '\t' || header[0] == ' ' {
+
+		header = bytes.TrimSpace(header)
+		s.Headers[s.lastHeaderParsed][len(s.Headers[s.lastHeaderParsed])-1] += fmt.Sprintf(" %s", string(header))
+		return
+	}
+
+	// Find the ':' to separate header name and value
+	index := bytes.Index(header, []byte(":"))
+	if index >= 0 {
+
+		headerName := strings.ToLower(string(bytes.Trim(header[:index], " ")))
+		headerValue := string(bytes.Trim(header[index+1:], " "))
+
+		// Add header to object
+		s.Headers[headerName] = append(s.Headers[headerName], headerValue)
+		s.lastHeaderParsed = headerName
+
+		// Compute specific headers
+		err = s.ParseSpecificHeaders(headerName, headerValue)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// ParseSpecificHeaders will parse some specific key values from
+// specific headers like CSeq or Content-Length integer values
+func (s *SIP) ParseSpecificHeaders(headerName string, headerValue string) (err error) {
+
+	switch headerName {
+	case "cseq":
+
+		// CSeq header value is formatted like that :
+		// CSeq: 123 INVITE
+		// We split the value to parse Cseq integer value, and method
+		splits := strings.Split(headerValue, " ")
+		if len(splits) > 1 {
+
+			// Parse Cseq
+			s.cseq, err = strconv.ParseInt(splits[0], 10, 64)
+			if err != nil {
+				return err
+			}
+
+			// Validate method
+			if s.IsResponse {
+				s.Method, err = GetSIPMethod(splits[1])
+				if err != nil {
+					return err
+				}
+			}
+		}
+
+	case "content-length":
+
+		// Parse Content-Length
+		s.contentLength, err = strconv.ParseInt(headerValue, 10, 64)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// GetAllHeaders will return the full headers of the
+// current SIP packets in a map[string][]string
+func (s *SIP) GetAllHeaders() map[string][]string {
+	return s.Headers
+}
+
+// GetHeader will return all the headers with
+// the specified name.
+func (s *SIP) GetHeader(headerName string) []string {
+	headerName = strings.ToLower(headerName)
+	h := make([]string, 0)
+	if _, ok := s.Headers[headerName]; ok {
+		return s.Headers[headerName]
+	}
+	compactHeader := compactSipHeadersCorrespondance[headerName]
+	if _, ok := s.Headers[compactHeader]; ok {
+		return s.Headers[compactHeader]
+	}
+	return h
+}
+
+// GetFirstHeader will return the first header with
+// the specified name. If the current SIP packet has multiple
+// headers with the same name, it returns the first.
+func (s *SIP) GetFirstHeader(headerName string) string {
+	headers := s.GetHeader(headerName)
+	if len(headers) > 0 {
+		return headers[0]
+	}
+	return ""
+}
+
+//
+// Some handy getters for most used SIP headers
+//
+
+// GetAuthorization will return the Authorization
+// header of the current SIP packet
+func (s *SIP) GetAuthorization() string {
+	return s.GetFirstHeader("Authorization")
+}
+
+// GetFrom will return the From
+// header of the current SIP packet
+func (s *SIP) GetFrom() string {
+	return s.GetFirstHeader("From")
+}
+
+// GetTo will return the To
+// header of the current SIP packet
+func (s *SIP) GetTo() string {
+	return s.GetFirstHeader("To")
+}
+
+// GetContact will return the Contact
+// header of the current SIP packet
+func (s *SIP) GetContact() string {
+	return s.GetFirstHeader("Contact")
+}
+
+// GetCallID will return the Call-ID
+// header of the current SIP packet
+func (s *SIP) GetCallID() string {
+	return s.GetFirstHeader("Call-ID")
+}
+
+// GetUserAgent will return the User-Agent
+// header of the current SIP packet
+func (s *SIP) GetUserAgent() string {
+	return s.GetFirstHeader("User-Agent")
+}
+
+// GetContentLength will return the parsed integer
+// Content-Length header of the current SIP packet
+func (s *SIP) GetContentLength() int64 {
+	return s.contentLength
+}
+
+// GetCSeq will return the parsed integer CSeq header
+// header of the current SIP packet
+func (s *SIP) GetCSeq() int64 {
+	return s.cseq
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/stp.go b/go-controller/vendor/github.com/google/gopacket/layers/stp.go
new file mode 100644
index 00000000000..bde7d7c8ef9
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/stp.go
@@ -0,0 +1,27 @@
+// 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 (
+	"github.com/google/gopacket"
+)
+
+// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message.
+type STP struct {
+	BaseLayer
+}
+
+// LayerType returns gopacket.LayerTypeSTP.
+func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP }
+
+func decodeSTP(data []byte, p gopacket.PacketBuilder) error {
+	stp := &STP{}
+	stp.Contents = data[:]
+	// TODO:  parse the STP protocol into actual subfields.
+	p.AddLayer(stp)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tcp.go b/go-controller/vendor/github.com/google/gopacket/layers/tcp.go
new file mode 100644
index 00000000000..bcdeb4b3c35
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tcp.go
@@ -0,0 +1,341 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// TCP is the layer for TCP headers.
+type TCP struct {
+	BaseLayer
+	SrcPort, DstPort                           TCPPort
+	Seq                                        uint32
+	Ack                                        uint32
+	DataOffset                                 uint8
+	FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
+	Window                                     uint16
+	Checksum                                   uint16
+	Urgent                                     uint16
+	sPort, dPort                               []byte
+	Options                                    []TCPOption
+	Padding                                    []byte
+	opts                                       [4]TCPOption
+	tcpipchecksum
+}
+
+// TCPOptionKind represents a TCP option code.
+type TCPOptionKind uint8
+
+const (
+	TCPOptionKindEndList                         = 0
+	TCPOptionKindNop                             = 1
+	TCPOptionKindMSS                             = 2  // len = 4
+	TCPOptionKindWindowScale                     = 3  // len = 3
+	TCPOptionKindSACKPermitted                   = 4  // len = 2
+	TCPOptionKindSACK                            = 5  // len = n
+	TCPOptionKindEcho                            = 6  // len = 6, obsolete
+	TCPOptionKindEchoReply                       = 7  // len = 6, obsolete
+	TCPOptionKindTimestamps                      = 8  // len = 10
+	TCPOptionKindPartialOrderConnectionPermitted = 9  // len = 2, obsolete
+	TCPOptionKindPartialOrderServiceProfile      = 10 // len = 3, obsolete
+	TCPOptionKindCC                              = 11 // obsolete
+	TCPOptionKindCCNew                           = 12 // obsolete
+	TCPOptionKindCCEcho                          = 13 // obsolete
+	TCPOptionKindAltChecksum                     = 14 // len = 3, obsolete
+	TCPOptionKindAltChecksumData                 = 15 // len = n, obsolete
+)
+
+func (k TCPOptionKind) String() string {
+	switch k {
+	case TCPOptionKindEndList:
+		return "EndList"
+	case TCPOptionKindNop:
+		return "NOP"
+	case TCPOptionKindMSS:
+		return "MSS"
+	case TCPOptionKindWindowScale:
+		return "WindowScale"
+	case TCPOptionKindSACKPermitted:
+		return "SACKPermitted"
+	case TCPOptionKindSACK:
+		return "SACK"
+	case TCPOptionKindEcho:
+		return "Echo"
+	case TCPOptionKindEchoReply:
+		return "EchoReply"
+	case TCPOptionKindTimestamps:
+		return "Timestamps"
+	case TCPOptionKindPartialOrderConnectionPermitted:
+		return "PartialOrderConnectionPermitted"
+	case TCPOptionKindPartialOrderServiceProfile:
+		return "PartialOrderServiceProfile"
+	case TCPOptionKindCC:
+		return "CC"
+	case TCPOptionKindCCNew:
+		return "CCNew"
+	case TCPOptionKindCCEcho:
+		return "CCEcho"
+	case TCPOptionKindAltChecksum:
+		return "AltChecksum"
+	case TCPOptionKindAltChecksumData:
+		return "AltChecksumData"
+	default:
+		return fmt.Sprintf("Unknown(%d)", k)
+	}
+}
+
+type TCPOption struct {
+	OptionType   TCPOptionKind
+	OptionLength uint8
+	OptionData   []byte
+}
+
+func (t TCPOption) String() string {
+	hd := hex.EncodeToString(t.OptionData)
+	if len(hd) > 0 {
+		hd = " 0x" + hd
+	}
+	switch t.OptionType {
+	case TCPOptionKindMSS:
+		if len(t.OptionData) >= 2 {
+			return fmt.Sprintf("TCPOption(%s:%v%s)",
+				t.OptionType,
+				binary.BigEndian.Uint16(t.OptionData),
+				hd)
+		}
+
+	case TCPOptionKindTimestamps:
+		if len(t.OptionData) == 8 {
+			return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
+				t.OptionType,
+				binary.BigEndian.Uint32(t.OptionData[:4]),
+				binary.BigEndian.Uint32(t.OptionData[4:8]),
+				hd)
+		}
+	}
+	return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
+}
+
+// LayerType returns gopacket.LayerTypeTCP
+func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var optionLength int
+	for _, o := range t.Options {
+		switch o.OptionType {
+		case 0, 1:
+			optionLength += 1
+		default:
+			optionLength += 2 + len(o.OptionData)
+		}
+	}
+	if opts.FixLengths {
+		if rem := optionLength % 4; rem != 0 {
+			t.Padding = lotsOfZeros[:4-rem]
+		}
+		t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
+	}
+	bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
+	if err != nil {
+		return err
+	}
+	binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort))
+	binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort))
+	binary.BigEndian.PutUint32(bytes[4:], t.Seq)
+	binary.BigEndian.PutUint32(bytes[8:], t.Ack)
+	binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset())
+	binary.BigEndian.PutUint16(bytes[14:], t.Window)
+	binary.BigEndian.PutUint16(bytes[18:], t.Urgent)
+	start := 20
+	for _, o := range t.Options {
+		bytes[start] = byte(o.OptionType)
+		switch o.OptionType {
+		case 0, 1:
+			start++
+		default:
+			if opts.FixLengths {
+				o.OptionLength = uint8(len(o.OptionData) + 2)
+			}
+			bytes[start+1] = o.OptionLength
+			copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
+			start += len(o.OptionData) + 2
+		}
+	}
+	copy(bytes[start:], t.Padding)
+	if opts.ComputeChecksums {
+		// zero out checksum bytes in current serialization.
+		bytes[16] = 0
+		bytes[17] = 0
+		csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
+		if err != nil {
+			return err
+		}
+		t.Checksum = csum
+	}
+	binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
+	return nil
+}
+
+func (t *TCP) ComputeChecksum() (uint16, error) {
+	return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
+}
+
+func (t *TCP) flagsAndOffset() uint16 {
+	f := uint16(t.DataOffset) << 12
+	if t.FIN {
+		f |= 0x0001
+	}
+	if t.SYN {
+		f |= 0x0002
+	}
+	if t.RST {
+		f |= 0x0004
+	}
+	if t.PSH {
+		f |= 0x0008
+	}
+	if t.ACK {
+		f |= 0x0010
+	}
+	if t.URG {
+		f |= 0x0020
+	}
+	if t.ECE {
+		f |= 0x0040
+	}
+	if t.CWR {
+		f |= 0x0080
+	}
+	if t.NS {
+		f |= 0x0100
+	}
+	return f
+}
+
+func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 20 {
+		df.SetTruncated()
+		return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data))
+	}
+	tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
+	tcp.sPort = data[0:2]
+	tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
+	tcp.dPort = data[2:4]
+	tcp.Seq = binary.BigEndian.Uint32(data[4:8])
+	tcp.Ack = binary.BigEndian.Uint32(data[8:12])
+	tcp.DataOffset = data[12] >> 4
+	tcp.FIN = data[13]&0x01 != 0
+	tcp.SYN = data[13]&0x02 != 0
+	tcp.RST = data[13]&0x04 != 0
+	tcp.PSH = data[13]&0x08 != 0
+	tcp.ACK = data[13]&0x10 != 0
+	tcp.URG = data[13]&0x20 != 0
+	tcp.ECE = data[13]&0x40 != 0
+	tcp.CWR = data[13]&0x80 != 0
+	tcp.NS = data[12]&0x01 != 0
+	tcp.Window = binary.BigEndian.Uint16(data[14:16])
+	tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
+	tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
+	if tcp.Options == nil {
+		// Pre-allocate to avoid allocating a slice.
+		tcp.Options = tcp.opts[:0]
+	} else {
+		tcp.Options = tcp.Options[:0]
+	}
+	tcp.Padding = tcp.Padding[:0]
+	if tcp.DataOffset < 5 {
+		return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
+	}
+	dataStart := int(tcp.DataOffset) * 4
+	if dataStart > len(data) {
+		df.SetTruncated()
+		tcp.Payload = nil
+		tcp.Contents = data
+		return errors.New("TCP data offset greater than packet length")
+	}
+	tcp.Contents = data[:dataStart]
+	tcp.Payload = data[dataStart:]
+	// From here on, data points just to the header options.
+	data = data[20:dataStart]
+OPTIONS:
+	for len(data) > 0 {
+		tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
+		opt := &tcp.Options[len(tcp.Options)-1]
+		switch opt.OptionType {
+		case TCPOptionKindEndList: // End of options
+			opt.OptionLength = 1
+			tcp.Padding = data[1:]
+			break OPTIONS
+		case TCPOptionKindNop: // 1 byte padding
+			opt.OptionLength = 1
+		default:
+			if len(data) < 2 {
+				df.SetTruncated()
+				return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data))
+			}
+			opt.OptionLength = data[1]
+			if opt.OptionLength < 2 {
+				return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
+			} else if int(opt.OptionLength) > len(data) {
+				df.SetTruncated()
+				return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
+			}
+			opt.OptionData = data[2:opt.OptionLength]
+		}
+		data = data[opt.OptionLength:]
+	}
+	return nil
+}
+
+func (t *TCP) CanDecode() gopacket.LayerClass {
+	return LayerTypeTCP
+}
+
+func (t *TCP) NextLayerType() gopacket.LayerType {
+	lt := t.DstPort.LayerType()
+	if lt == gopacket.LayerTypePayload {
+		lt = t.SrcPort.LayerType()
+	}
+	return lt
+}
+
+func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
+	tcp := &TCP{}
+	err := tcp.DecodeFromBytes(data, p)
+	p.AddLayer(tcp)
+	p.SetTransportLayer(tcp)
+	if err != nil {
+		return err
+	}
+	if p.DecodeOptions().DecodeStreamsAsDatagrams {
+		return p.NextDecoder(tcp.NextLayerType())
+	} else {
+		return p.NextDecoder(gopacket.LayerTypePayload)
+	}
+}
+
+func (t *TCP) TransportFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
+}
+
+// For testing only
+func (t *TCP) SetInternalPortsForTesting() {
+	t.sPort = make([]byte, 2)
+	t.dPort = make([]byte, 2)
+	binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
+	binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tcpip.go b/go-controller/vendor/github.com/google/gopacket/layers/tcpip.go
new file mode 100644
index 00000000000..64ba51cc75b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tcpip.go
@@ -0,0 +1,104 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// Checksum computation for TCP/UDP.
+type tcpipchecksum struct {
+	pseudoheader tcpipPseudoHeader
+}
+
+type tcpipPseudoHeader interface {
+	pseudoheaderChecksum() (uint32, error)
+}
+
+func (ip *IPv4) pseudoheaderChecksum() (csum uint32, err error) {
+	if err := ip.AddressTo4(); err != nil {
+		return 0, err
+	}
+	csum += (uint32(ip.SrcIP[0]) + uint32(ip.SrcIP[2])) << 8
+	csum += uint32(ip.SrcIP[1]) + uint32(ip.SrcIP[3])
+	csum += (uint32(ip.DstIP[0]) + uint32(ip.DstIP[2])) << 8
+	csum += uint32(ip.DstIP[1]) + uint32(ip.DstIP[3])
+	return csum, nil
+}
+
+func (ip *IPv6) pseudoheaderChecksum() (csum uint32, err error) {
+	if err := ip.AddressTo16(); err != nil {
+		return 0, err
+	}
+	for i := 0; i < 16; i += 2 {
+		csum += uint32(ip.SrcIP[i]) << 8
+		csum += uint32(ip.SrcIP[i+1])
+		csum += uint32(ip.DstIP[i]) << 8
+		csum += uint32(ip.DstIP[i+1])
+	}
+	return csum, nil
+}
+
+// Calculate the TCP/IP checksum defined in rfc1071.  The passed-in csum is any
+// initial checksum data that's already been computed.
+func tcpipChecksum(data []byte, csum uint32) uint16 {
+	// to handle odd lengths, we loop to length - 1, incrementing by 2, then
+	// handle the last byte specifically by checking against the original
+	// length.
+	length := len(data) - 1
+	for i := 0; i < length; i += 2 {
+		// For our test packet, doing this manually is about 25% faster
+		// (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
+		csum += uint32(data[i]) << 8
+		csum += uint32(data[i+1])
+	}
+	if len(data)%2 == 1 {
+		csum += uint32(data[length]) << 8
+	}
+	for csum > 0xffff {
+		csum = (csum >> 16) + (csum & 0xffff)
+	}
+	return ^uint16(csum)
+}
+
+// computeChecksum computes a TCP or UDP checksum.  headerAndPayload is the
+// serialized TCP or UDP header plus its payload, with the checksum zero'd
+// out. headerProtocol is the IP protocol number of the upper-layer header.
+func (c *tcpipchecksum) computeChecksum(headerAndPayload []byte, headerProtocol IPProtocol) (uint16, error) {
+	if c.pseudoheader == nil {
+		return 0, errors.New("TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use")
+	}
+	length := uint32(len(headerAndPayload))
+	csum, err := c.pseudoheader.pseudoheaderChecksum()
+	if err != nil {
+		return 0, err
+	}
+	csum += uint32(headerProtocol)
+	csum += length & 0xffff
+	csum += length >> 16
+	return tcpipChecksum(headerAndPayload, csum), nil
+}
+
+// SetNetworkLayerForChecksum tells this layer which network layer is wrapping it.
+// This is needed for computing the checksum when serializing, since TCP/IP transport
+// layer checksums depends on fields in the IPv4 or IPv6 layer that contains it.
+// The passed in layer must be an *IPv4 or *IPv6.
+func (i *tcpipchecksum) SetNetworkLayerForChecksum(l gopacket.NetworkLayer) error {
+	switch v := l.(type) {
+	case *IPv4:
+		i.pseudoheader = v
+	case *IPv6:
+		i.pseudoheader = v
+	default:
+		return fmt.Errorf("cannot use layer type %v for tcp checksum network layer", l.LayerType())
+	}
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/test_creator.py b/go-controller/vendor/github.com/google/gopacket/layers/test_creator.py
new file mode 100644
index 00000000000..c92d2765a22
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/test_creator.py
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+# Copyright 2012 Google, Inc. All rights reserved.
+
+"""TestCreator creates test templates from pcap files."""
+
+import argparse
+import base64
+import glob
+import re
+import string
+import subprocess
+import sys
+
+
+class Packet(object):
+  """Helper class encapsulating packet from a pcap file."""
+
+  def __init__(self, packet_lines):
+    self.packet_lines = packet_lines
+    self.data = self._DecodeText(packet_lines)
+
+  @classmethod
+  def _DecodeText(cls, packet_lines):
+    packet_bytes = []
+    # First line is timestamp and stuff, skip it.
+    # Format: 0x0010:  0000 0020 3aff 3ffe 0000 0000 0000 0000  ....:.?.........
+
+    for line in packet_lines[1:]:
+      m = re.match(r'\s+0x[a-f\d]+:\s+((?:[\da-f]{2,4}\s)*)', line, re.IGNORECASE)
+      if m is None: continue
+      for hexpart in m.group(1).split():
+        packet_bytes.append(base64.b16decode(hexpart.upper()))
+    return ''.join(packet_bytes)
+
+  def Test(self, name, link_type):
+    """Yields a test using this packet, as a set of lines."""
+    yield '// testPacket%s is the packet:' % name
+    for line in self.packet_lines:
+      yield '//   ' + line
+    yield 'var testPacket%s = []byte{' % name
+    data = list(self.data)
+    while data:
+      linebytes, data = data[:16], data[16:]
+      yield ''.join(['\t'] + ['0x%02x, ' % ord(c) for c in linebytes])
+    yield '}'
+    yield 'func TestPacket%s(t *testing.T) {' % name
+    yield '\tp := gopacket.NewPacket(testPacket%s, LinkType%s, gopacket.Default)' % (name, link_type)
+    yield '\tif p.ErrorLayer() != nil {'
+    yield '\t\tt.Error("Failed to decode packet:", p.ErrorLayer().Error())'
+    yield '\t}'
+    yield '\tcheckLayers(p, []gopacket.LayerType{LayerType%s, FILL_ME_IN_WITH_ACTUAL_LAYERS}, t)' % link_type
+    yield '}'
+    yield 'func BenchmarkDecodePacket%s(b *testing.B) {' % name
+    yield '\tfor i := 0; i < b.N; i++ {'
+    yield '\t\tgopacket.NewPacket(testPacket%s, LinkType%s, gopacket.NoCopy)' % (name, link_type)
+    yield '\t}'
+    yield '}'
+
+
+
+def GetTcpdumpOutput(filename):
+  """Runs tcpdump on the given file, returning output as string."""
+  return subprocess.check_output(
+      ['tcpdump', '-XX', '-s', '0', '-n', '-r', filename])
+
+
+def TcpdumpOutputToPackets(output):
+  """Reads a pcap file with TCPDump, yielding Packet objects."""
+  pdata = []
+  for line in output.splitlines():
+    if line[0] not in string.whitespace and pdata:
+      yield Packet(pdata)
+      pdata = []
+    pdata.append(line)
+  if pdata:
+    yield Packet(pdata)
+
+
+def main():
+  class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
+    def _format_usage(self, usage, actions, groups, prefix=None):
+      header =('TestCreator creates gopacket tests using a pcap file.\n\n'
+               'Tests are written to standard out... they can then be \n'
+               'copied into the file of your choice and modified as \n'
+               'you see.\n\n')
+      return header + argparse.ArgumentDefaultsHelpFormatter._format_usage(
+        self, usage, actions, groups, prefix)
+
+  parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter)
+  parser.add_argument('--link_type', default='Ethernet', help='the link type (default: %(default)s)')
+  parser.add_argument('--name', default='Packet%d', help='the layer type, must have "%d" inside it')
+  parser.add_argument('files', metavar='file.pcap', type=str, nargs='+', help='the files to process')
+
+  args = parser.parse_args()
+
+  for arg in args.files:
+    for path in glob.glob(arg):
+      for i, packet in enumerate(TcpdumpOutputToPackets(GetTcpdumpOutput(path))):
+        print '\n'.join(packet.Test(
+          args.name % i, args.link_type))
+
+if __name__ == '__main__':
+    main()
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tls.go b/go-controller/vendor/github.com/google/gopacket/layers/tls.go
new file mode 100644
index 00000000000..5a155d455a2
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tls.go
@@ -0,0 +1,283 @@
+// 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 (
+	"encoding/binary"
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+// TLSType defines the type of data after the TLS Record
+type TLSType uint8
+
+// TLSType known values.
+const (
+	TLSChangeCipherSpec TLSType = 20
+	TLSAlert            TLSType = 21
+	TLSHandshake        TLSType = 22
+	TLSApplicationData  TLSType = 23
+	TLSUnknown          TLSType = 255
+)
+
+// String shows the register type nicely formatted
+func (tt TLSType) String() string {
+	switch tt {
+	default:
+		return "Unknown"
+	case TLSChangeCipherSpec:
+		return "Change Cipher Spec"
+	case TLSAlert:
+		return "Alert"
+	case TLSHandshake:
+		return "Handshake"
+	case TLSApplicationData:
+		return "Application Data"
+	}
+}
+
+// TLSVersion represents the TLS version in numeric format
+type TLSVersion uint16
+
+// Strings shows the TLS version nicely formatted
+func (tv TLSVersion) String() string {
+	switch tv {
+	default:
+		return "Unknown"
+	case 0x0200:
+		return "SSL 2.0"
+	case 0x0300:
+		return "SSL 3.0"
+	case 0x0301:
+		return "TLS 1.0"
+	case 0x0302:
+		return "TLS 1.1"
+	case 0x0303:
+		return "TLS 1.2"
+	case 0x0304:
+		return "TLS 1.3"
+	}
+}
+
+// TLS is specified in RFC 5246
+//
+//  TLS Record Protocol
+//  0  1  2  3  4  5  6  7  8
+//  +--+--+--+--+--+--+--+--+
+//  |     Content Type      |
+//  +--+--+--+--+--+--+--+--+
+//  |    Version (major)    |
+//  +--+--+--+--+--+--+--+--+
+//  |    Version (minor)    |
+//  +--+--+--+--+--+--+--+--+
+//  |        Length         |
+//  +--+--+--+--+--+--+--+--+
+//  |        Length         |
+//  +--+--+--+--+--+--+--+--+
+
+// TLS is actually a slide of TLSrecord structures
+type TLS struct {
+	BaseLayer
+
+	// TLS Records
+	ChangeCipherSpec []TLSChangeCipherSpecRecord
+	Handshake        []TLSHandshakeRecord
+	AppData          []TLSAppDataRecord
+	Alert            []TLSAlertRecord
+}
+
+// TLSRecordHeader contains all the information that each TLS Record types should have
+type TLSRecordHeader struct {
+	ContentType TLSType
+	Version     TLSVersion
+	Length      uint16
+}
+
+// LayerType returns gopacket.LayerTypeTLS.
+func (t *TLS) LayerType() gopacket.LayerType { return LayerTypeTLS }
+
+// decodeTLS decodes the byte slice into a TLS type. It also
+// setups the application Layer in PacketBuilder.
+func decodeTLS(data []byte, p gopacket.PacketBuilder) error {
+	t := &TLS{}
+	err := t.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+	p.AddLayer(t)
+	p.SetApplicationLayer(t)
+	return nil
+}
+
+// DecodeFromBytes decodes the slice into the TLS struct.
+func (t *TLS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	t.BaseLayer.Contents = data
+	t.BaseLayer.Payload = nil
+
+	t.ChangeCipherSpec = t.ChangeCipherSpec[:0]
+	t.Handshake = t.Handshake[:0]
+	t.AppData = t.AppData[:0]
+	t.Alert = t.Alert[:0]
+
+	return t.decodeTLSRecords(data, df)
+}
+
+func (t *TLS) decodeTLSRecords(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 5 {
+		df.SetTruncated()
+		return errors.New("TLS record too short")
+	}
+
+	// since there are no further layers, the baselayer's content is
+	// pointing to this layer
+	// TODO: Consider removing this
+	t.BaseLayer = BaseLayer{Contents: data[:len(data)]}
+
+	var h TLSRecordHeader
+	h.ContentType = TLSType(data[0])
+	h.Version = TLSVersion(binary.BigEndian.Uint16(data[1:3]))
+	h.Length = binary.BigEndian.Uint16(data[3:5])
+
+	if h.ContentType.String() == "Unknown" {
+		return errors.New("Unknown TLS record type")
+	}
+
+	hl := 5 // header length
+	tl := hl + int(h.Length)
+	if len(data) < tl {
+		df.SetTruncated()
+		return errors.New("TLS packet length mismatch")
+	}
+
+	switch h.ContentType {
+	default:
+		return errors.New("Unknown TLS record type")
+	case TLSChangeCipherSpec:
+		var r TLSChangeCipherSpecRecord
+		e := r.decodeFromBytes(h, data[hl:tl], df)
+		if e != nil {
+			return e
+		}
+		t.ChangeCipherSpec = append(t.ChangeCipherSpec, r)
+	case TLSAlert:
+		var r TLSAlertRecord
+		e := r.decodeFromBytes(h, data[hl:tl], df)
+		if e != nil {
+			return e
+		}
+		t.Alert = append(t.Alert, r)
+	case TLSHandshake:
+		var r TLSHandshakeRecord
+		e := r.decodeFromBytes(h, data[hl:tl], df)
+		if e != nil {
+			return e
+		}
+		t.Handshake = append(t.Handshake, r)
+	case TLSApplicationData:
+		var r TLSAppDataRecord
+		e := r.decodeFromBytes(h, data[hl:tl], df)
+		if e != nil {
+			return e
+		}
+		t.AppData = append(t.AppData, r)
+	}
+
+	if len(data) == tl {
+		return nil
+	}
+	return t.decodeTLSRecords(data[tl:len(data)], df)
+}
+
+// CanDecode implements gopacket.DecodingLayer.
+func (t *TLS) CanDecode() gopacket.LayerClass {
+	return LayerTypeTLS
+}
+
+// NextLayerType implements gopacket.DecodingLayer.
+func (t *TLS) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// Payload returns nil, since TLS encrypted payload is inside TLSAppDataRecord
+func (t *TLS) Payload() []byte {
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+func (t *TLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	totalLength := 0
+	for _, record := range t.ChangeCipherSpec {
+		if opts.FixLengths {
+			record.Length = 1
+		}
+		totalLength += 5 + 1 // length of header + record
+	}
+	for range t.Handshake {
+		totalLength += 5
+		// TODO
+	}
+	for _, record := range t.AppData {
+		if opts.FixLengths {
+			record.Length = uint16(len(record.Payload))
+		}
+		totalLength += 5 + len(record.Payload)
+	}
+	for _, record := range t.Alert {
+		if len(record.EncryptedMsg) == 0 {
+			if opts.FixLengths {
+				record.Length = 2
+			}
+			totalLength += 5 + 2
+		} else {
+			if opts.FixLengths {
+				record.Length = uint16(len(record.EncryptedMsg))
+			}
+			totalLength += 5 + len(record.EncryptedMsg)
+		}
+	}
+	data, err := b.PrependBytes(totalLength)
+	if err != nil {
+		return err
+	}
+	off := 0
+	for _, record := range t.ChangeCipherSpec {
+		off = encodeHeader(record.TLSRecordHeader, data, off)
+		data[off] = byte(record.Message)
+		off++
+	}
+	for _, record := range t.Handshake {
+		off = encodeHeader(record.TLSRecordHeader, data, off)
+		// TODO
+	}
+	for _, record := range t.AppData {
+		off = encodeHeader(record.TLSRecordHeader, data, off)
+		copy(data[off:], record.Payload)
+		off += len(record.Payload)
+	}
+	for _, record := range t.Alert {
+		off = encodeHeader(record.TLSRecordHeader, data, off)
+		if len(record.EncryptedMsg) == 0 {
+			data[off] = byte(record.Level)
+			data[off+1] = byte(record.Description)
+			off += 2
+		} else {
+			copy(data[off:], record.EncryptedMsg)
+			off += len(record.EncryptedMsg)
+		}
+	}
+	return nil
+}
+
+func encodeHeader(header TLSRecordHeader, data []byte, offset int) int {
+	data[offset] = byte(header.ContentType)
+	binary.BigEndian.PutUint16(data[offset+1:], uint16(header.Version))
+	binary.BigEndian.PutUint16(data[offset+3:], header.Length)
+
+	return offset + 5
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tls_alert.go b/go-controller/vendor/github.com/google/gopacket/layers/tls_alert.go
new file mode 100644
index 00000000000..0c5aee02180
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tls_alert.go
@@ -0,0 +1,165 @@
+// 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 (
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// TLSAlertLevel defines the alert level data type
+type TLSAlertLevel uint8
+
+// TLSAlertDescr defines the alert descrption data type
+type TLSAlertDescr uint8
+
+const (
+	TLSAlertWarning      TLSAlertLevel = 1
+	TLSAlertFatal        TLSAlertLevel = 2
+	TLSAlertUnknownLevel TLSAlertLevel = 255
+
+	TLSAlertCloseNotify               TLSAlertDescr = 0
+	TLSAlertUnexpectedMessage         TLSAlertDescr = 10
+	TLSAlertBadRecordMac              TLSAlertDescr = 20
+	TLSAlertDecryptionFailedRESERVED  TLSAlertDescr = 21
+	TLSAlertRecordOverflow            TLSAlertDescr = 22
+	TLSAlertDecompressionFailure      TLSAlertDescr = 30
+	TLSAlertHandshakeFailure          TLSAlertDescr = 40
+	TLSAlertNoCertificateRESERVED     TLSAlertDescr = 41
+	TLSAlertBadCertificate            TLSAlertDescr = 42
+	TLSAlertUnsupportedCertificate    TLSAlertDescr = 43
+	TLSAlertCertificateRevoked        TLSAlertDescr = 44
+	TLSAlertCertificateExpired        TLSAlertDescr = 45
+	TLSAlertCertificateUnknown        TLSAlertDescr = 46
+	TLSAlertIllegalParameter          TLSAlertDescr = 47
+	TLSAlertUnknownCa                 TLSAlertDescr = 48
+	TLSAlertAccessDenied              TLSAlertDescr = 49
+	TLSAlertDecodeError               TLSAlertDescr = 50
+	TLSAlertDecryptError              TLSAlertDescr = 51
+	TLSAlertExportRestrictionRESERVED TLSAlertDescr = 60
+	TLSAlertProtocolVersion           TLSAlertDescr = 70
+	TLSAlertInsufficientSecurity      TLSAlertDescr = 71
+	TLSAlertInternalError             TLSAlertDescr = 80
+	TLSAlertUserCanceled              TLSAlertDescr = 90
+	TLSAlertNoRenegotiation           TLSAlertDescr = 100
+	TLSAlertUnsupportedExtension      TLSAlertDescr = 110
+	TLSAlertUnknownDescription        TLSAlertDescr = 255
+)
+
+//  TLS Alert
+//  0  1  2  3  4  5  6  7  8
+//  +--+--+--+--+--+--+--+--+
+//  |         Level         |
+//  +--+--+--+--+--+--+--+--+
+//  |      Description      |
+//  +--+--+--+--+--+--+--+--+
+
+// TLSAlertRecord contains all the information that each Alert Record type should have
+type TLSAlertRecord struct {
+	TLSRecordHeader
+
+	Level       TLSAlertLevel
+	Description TLSAlertDescr
+
+	EncryptedMsg []byte
+}
+
+// DecodeFromBytes decodes the slice into the TLS struct.
+func (t *TLSAlertRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error {
+	// TLS Record Header
+	t.ContentType = h.ContentType
+	t.Version = h.Version
+	t.Length = h.Length
+
+	if len(data) < 2 {
+		df.SetTruncated()
+		return errors.New("TLS Alert packet too short")
+	}
+
+	if t.Length == 2 {
+		t.Level = TLSAlertLevel(data[0])
+		t.Description = TLSAlertDescr(data[1])
+	} else {
+		t.Level = TLSAlertUnknownLevel
+		t.Description = TLSAlertUnknownDescription
+		t.EncryptedMsg = data
+	}
+
+	return nil
+}
+
+// Strings shows the TLS alert level nicely formatted
+func (al TLSAlertLevel) String() string {
+	switch al {
+	default:
+		return fmt.Sprintf("Unknown(%d)", al)
+	case TLSAlertWarning:
+		return "Warning"
+	case TLSAlertFatal:
+		return "Fatal"
+	}
+}
+
+// Strings shows the TLS alert description nicely formatted
+func (ad TLSAlertDescr) String() string {
+	switch ad {
+	default:
+		return "Unknown"
+	case TLSAlertCloseNotify:
+		return "close_notify"
+	case TLSAlertUnexpectedMessage:
+		return "unexpected_message"
+	case TLSAlertBadRecordMac:
+		return "bad_record_mac"
+	case TLSAlertDecryptionFailedRESERVED:
+		return "decryption_failed_RESERVED"
+	case TLSAlertRecordOverflow:
+		return "record_overflow"
+	case TLSAlertDecompressionFailure:
+		return "decompression_failure"
+	case TLSAlertHandshakeFailure:
+		return "handshake_failure"
+	case TLSAlertNoCertificateRESERVED:
+		return "no_certificate_RESERVED"
+	case TLSAlertBadCertificate:
+		return "bad_certificate"
+	case TLSAlertUnsupportedCertificate:
+		return "unsupported_certificate"
+	case TLSAlertCertificateRevoked:
+		return "certificate_revoked"
+	case TLSAlertCertificateExpired:
+		return "certificate_expired"
+	case TLSAlertCertificateUnknown:
+		return "certificate_unknown"
+	case TLSAlertIllegalParameter:
+		return "illegal_parameter"
+	case TLSAlertUnknownCa:
+		return "unknown_ca"
+	case TLSAlertAccessDenied:
+		return "access_denied"
+	case TLSAlertDecodeError:
+		return "decode_error"
+	case TLSAlertDecryptError:
+		return "decrypt_error"
+	case TLSAlertExportRestrictionRESERVED:
+		return "export_restriction_RESERVED"
+	case TLSAlertProtocolVersion:
+		return "protocol_version"
+	case TLSAlertInsufficientSecurity:
+		return "insufficient_security"
+	case TLSAlertInternalError:
+		return "internal_error"
+	case TLSAlertUserCanceled:
+		return "user_canceled"
+	case TLSAlertNoRenegotiation:
+		return "no_renegotiation"
+	case TLSAlertUnsupportedExtension:
+		return "unsupported_extension"
+	}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tls_appdata.go b/go-controller/vendor/github.com/google/gopacket/layers/tls_appdata.go
new file mode 100644
index 00000000000..dedd1d587b7
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tls_appdata.go
@@ -0,0 +1,34 @@
+// 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 (
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+// TLSAppDataRecord contains all the information that each AppData Record types should have
+type TLSAppDataRecord struct {
+	TLSRecordHeader
+	Payload []byte
+}
+
+// DecodeFromBytes decodes the slice into the TLS struct.
+func (t *TLSAppDataRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error {
+	// TLS Record Header
+	t.ContentType = h.ContentType
+	t.Version = h.Version
+	t.Length = h.Length
+
+	if len(data) != int(t.Length) {
+		return errors.New("TLS Application Data length mismatch")
+	}
+
+	t.Payload = data
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tls_cipherspec.go b/go-controller/vendor/github.com/google/gopacket/layers/tls_cipherspec.go
new file mode 100644
index 00000000000..8f3dc62ba55
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tls_cipherspec.go
@@ -0,0 +1,64 @@
+// 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 (
+	"errors"
+
+	"github.com/google/gopacket"
+)
+
+// TLSchangeCipherSpec defines the message value inside ChangeCipherSpec Record
+type TLSchangeCipherSpec uint8
+
+const (
+	TLSChangecipherspecMessage TLSchangeCipherSpec = 1
+	TLSChangecipherspecUnknown TLSchangeCipherSpec = 255
+)
+
+//  TLS Change Cipher Spec
+//  0  1  2  3  4  5  6  7  8
+//  +--+--+--+--+--+--+--+--+
+//  |        Message        |
+//  +--+--+--+--+--+--+--+--+
+
+// TLSChangeCipherSpecRecord defines the type of data inside ChangeCipherSpec Record
+type TLSChangeCipherSpecRecord struct {
+	TLSRecordHeader
+
+	Message TLSchangeCipherSpec
+}
+
+// DecodeFromBytes decodes the slice into the TLS struct.
+func (t *TLSChangeCipherSpecRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error {
+	// TLS Record Header
+	t.ContentType = h.ContentType
+	t.Version = h.Version
+	t.Length = h.Length
+
+	if len(data) != 1 {
+		df.SetTruncated()
+		return errors.New("TLS Change Cipher Spec record incorrect length")
+	}
+
+	t.Message = TLSchangeCipherSpec(data[0])
+	if t.Message != TLSChangecipherspecMessage {
+		t.Message = TLSChangecipherspecUnknown
+	}
+
+	return nil
+}
+
+// String shows the message value nicely formatted
+func (ccs TLSchangeCipherSpec) String() string {
+	switch ccs {
+	default:
+		return "Unknown"
+	case TLSChangecipherspecMessage:
+		return "Change Cipher Spec Message"
+	}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/tls_handshake.go b/go-controller/vendor/github.com/google/gopacket/layers/tls_handshake.go
new file mode 100644
index 00000000000..e45e2c7cbc5
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/tls_handshake.go
@@ -0,0 +1,28 @@
+// 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 (
+	"github.com/google/gopacket"
+)
+
+// TLSHandshakeRecord defines the structure of a Handshare Record
+type TLSHandshakeRecord struct {
+	TLSRecordHeader
+}
+
+// DecodeFromBytes decodes the slice into the TLS struct.
+func (t *TLSHandshakeRecord) decodeFromBytes(h TLSRecordHeader, data []byte, df gopacket.DecodeFeedback) error {
+	// TLS Record Header
+	t.ContentType = h.ContentType
+	t.Version = h.Version
+	t.Length = h.Length
+
+	// TODO
+
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/udp.go b/go-controller/vendor/github.com/google/gopacket/layers/udp.go
new file mode 100644
index 00000000000..97e81c69fcc
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/udp.go
@@ -0,0 +1,133 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+// UDP is the layer for UDP headers.
+type UDP struct {
+	BaseLayer
+	SrcPort, DstPort UDPPort
+	Length           uint16
+	Checksum         uint16
+	sPort, dPort     []byte
+	tcpipchecksum
+}
+
+// LayerType returns gopacket.LayerTypeUDP
+func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP }
+
+func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		df.SetTruncated()
+		return fmt.Errorf("Invalid UDP header. Length %d less than 8", len(data))
+	}
+	udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2]))
+	udp.sPort = data[0:2]
+	udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4]))
+	udp.dPort = data[2:4]
+	udp.Length = binary.BigEndian.Uint16(data[4:6])
+	udp.Checksum = binary.BigEndian.Uint16(data[6:8])
+	udp.BaseLayer = BaseLayer{Contents: data[:8]}
+	switch {
+	case udp.Length >= 8:
+		hlen := int(udp.Length)
+		if hlen > len(data) {
+			df.SetTruncated()
+			hlen = len(data)
+		}
+		udp.Payload = data[8:hlen]
+	case udp.Length == 0: // Jumbogram, use entire rest of data
+		udp.Payload = data[8:]
+	default:
+		return fmt.Errorf("UDP packet too small: %d bytes", udp.Length)
+	}
+	return nil
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (u *UDP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	var jumbo bool
+
+	payload := b.Bytes()
+	if _, ok := u.pseudoheader.(*IPv6); ok {
+		if len(payload)+8 > 65535 {
+			jumbo = true
+		}
+	}
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+	binary.BigEndian.PutUint16(bytes, uint16(u.SrcPort))
+	binary.BigEndian.PutUint16(bytes[2:], uint16(u.DstPort))
+	if opts.FixLengths {
+		if jumbo {
+			u.Length = 0
+		} else {
+			u.Length = uint16(len(payload)) + 8
+		}
+	}
+	binary.BigEndian.PutUint16(bytes[4:], u.Length)
+	if opts.ComputeChecksums {
+		// zero out checksum bytes
+		bytes[6] = 0
+		bytes[7] = 0
+		csum, err := u.computeChecksum(b.Bytes(), IPProtocolUDP)
+		if err != nil {
+			return err
+		}
+		u.Checksum = csum
+	}
+	binary.BigEndian.PutUint16(bytes[6:], u.Checksum)
+	return nil
+}
+
+func (u *UDP) CanDecode() gopacket.LayerClass {
+	return LayerTypeUDP
+}
+
+// NextLayerType use the destination port to select the
+// right next decoder. It tries first to decode via the
+// destination port, then the source port.
+func (u *UDP) NextLayerType() gopacket.LayerType {
+	if lt := u.DstPort.LayerType(); lt != gopacket.LayerTypePayload {
+		return lt
+	}
+	return u.SrcPort.LayerType()
+}
+
+func decodeUDP(data []byte, p gopacket.PacketBuilder) error {
+	udp := &UDP{}
+	err := udp.DecodeFromBytes(data, p)
+	p.AddLayer(udp)
+	p.SetTransportLayer(udp)
+	if err != nil {
+		return err
+	}
+	return p.NextDecoder(udp.NextLayerType())
+}
+
+func (u *UDP) TransportFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointUDPPort, u.sPort, u.dPort)
+}
+
+// For testing only
+func (u *UDP) SetInternalPortsForTesting() {
+	u.sPort = make([]byte, 2)
+	u.dPort = make([]byte, 2)
+	binary.BigEndian.PutUint16(u.sPort, uint16(u.SrcPort))
+	binary.BigEndian.PutUint16(u.dPort, uint16(u.DstPort))
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/udplite.go b/go-controller/vendor/github.com/google/gopacket/layers/udplite.go
new file mode 100644
index 00000000000..7d84c51463b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/udplite.go
@@ -0,0 +1,44 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"github.com/google/gopacket"
+)
+
+// UDPLite is the layer for UDP-Lite headers (rfc 3828).
+type UDPLite struct {
+	BaseLayer
+	SrcPort, DstPort UDPLitePort
+	ChecksumCoverage uint16
+	Checksum         uint16
+	sPort, dPort     []byte
+}
+
+// LayerType returns gopacket.LayerTypeUDPLite
+func (u *UDPLite) LayerType() gopacket.LayerType { return LayerTypeUDPLite }
+
+func decodeUDPLite(data []byte, p gopacket.PacketBuilder) error {
+	udp := &UDPLite{
+		SrcPort:          UDPLitePort(binary.BigEndian.Uint16(data[0:2])),
+		sPort:            data[0:2],
+		DstPort:          UDPLitePort(binary.BigEndian.Uint16(data[2:4])),
+		dPort:            data[2:4],
+		ChecksumCoverage: binary.BigEndian.Uint16(data[4:6]),
+		Checksum:         binary.BigEndian.Uint16(data[6:8]),
+		BaseLayer:        BaseLayer{data[:8], data[8:]},
+	}
+	p.AddLayer(udp)
+	p.SetTransportLayer(udp)
+	return p.NextDecoder(gopacket.LayerTypePayload)
+}
+
+func (u *UDPLite) TransportFlow() gopacket.Flow {
+	return gopacket.NewFlow(EndpointUDPLitePort, u.sPort, u.dPort)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/usb.go b/go-controller/vendor/github.com/google/gopacket/layers/usb.go
new file mode 100644
index 00000000000..d611e0fd060
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/usb.go
@@ -0,0 +1,292 @@
+// Copyright 2014 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"github.com/google/gopacket"
+)
+
+type USBEventType uint8
+
+const (
+	USBEventTypeSubmit   USBEventType = 'S'
+	USBEventTypeComplete USBEventType = 'C'
+	USBEventTypeError    USBEventType = 'E'
+)
+
+func (a USBEventType) String() string {
+	switch a {
+	case USBEventTypeSubmit:
+		return "SUBMIT"
+	case USBEventTypeComplete:
+		return "COMPLETE"
+	case USBEventTypeError:
+		return "ERROR"
+	default:
+		return "Unknown event type"
+	}
+}
+
+type USBRequestBlockSetupRequest uint8
+
+const (
+	USBRequestBlockSetupRequestGetStatus        USBRequestBlockSetupRequest = 0x00
+	USBRequestBlockSetupRequestClearFeature     USBRequestBlockSetupRequest = 0x01
+	USBRequestBlockSetupRequestSetFeature       USBRequestBlockSetupRequest = 0x03
+	USBRequestBlockSetupRequestSetAddress       USBRequestBlockSetupRequest = 0x05
+	USBRequestBlockSetupRequestGetDescriptor    USBRequestBlockSetupRequest = 0x06
+	USBRequestBlockSetupRequestSetDescriptor    USBRequestBlockSetupRequest = 0x07
+	USBRequestBlockSetupRequestGetConfiguration USBRequestBlockSetupRequest = 0x08
+	USBRequestBlockSetupRequestSetConfiguration USBRequestBlockSetupRequest = 0x09
+	USBRequestBlockSetupRequestSetIdle          USBRequestBlockSetupRequest = 0x0a
+)
+
+func (a USBRequestBlockSetupRequest) String() string {
+	switch a {
+	case USBRequestBlockSetupRequestGetStatus:
+		return "GET_STATUS"
+	case USBRequestBlockSetupRequestClearFeature:
+		return "CLEAR_FEATURE"
+	case USBRequestBlockSetupRequestSetFeature:
+		return "SET_FEATURE"
+	case USBRequestBlockSetupRequestSetAddress:
+		return "SET_ADDRESS"
+	case USBRequestBlockSetupRequestGetDescriptor:
+		return "GET_DESCRIPTOR"
+	case USBRequestBlockSetupRequestSetDescriptor:
+		return "SET_DESCRIPTOR"
+	case USBRequestBlockSetupRequestGetConfiguration:
+		return "GET_CONFIGURATION"
+	case USBRequestBlockSetupRequestSetConfiguration:
+		return "SET_CONFIGURATION"
+	case USBRequestBlockSetupRequestSetIdle:
+		return "SET_IDLE"
+	default:
+		return "UNKNOWN"
+	}
+}
+
+type USBTransportType uint8
+
+const (
+	USBTransportTypeTransferIn  USBTransportType = 0x80 // Indicates send or receive
+	USBTransportTypeIsochronous USBTransportType = 0x00 // Isochronous transfers occur continuously and periodically. They typically contain time sensitive information, such as an audio or video stream.
+	USBTransportTypeInterrupt   USBTransportType = 0x01 // Interrupt transfers are typically non-periodic, small device "initiated" communication requiring bounded latency, such as pointing devices or keyboards.
+	USBTransportTypeControl     USBTransportType = 0x02 // Control transfers are typically used for command and status operations.
+	USBTransportTypeBulk        USBTransportType = 0x03 // Bulk transfers can be used for large bursty data, using all remaining available bandwidth, no guarantees on bandwidth or latency, such as file transfers.
+)
+
+type USBDirectionType uint8
+
+const (
+	USBDirectionTypeUnknown USBDirectionType = iota
+	USBDirectionTypeIn
+	USBDirectionTypeOut
+)
+
+func (a USBDirectionType) String() string {
+	switch a {
+	case USBDirectionTypeIn:
+		return "In"
+	case USBDirectionTypeOut:
+		return "Out"
+	default:
+		return "Unknown direction type"
+	}
+}
+
+// The reference at http://www.beyondlogic.org/usbnutshell/usb1.shtml contains more information about the protocol.
+type USB struct {
+	BaseLayer
+	ID             uint64
+	EventType      USBEventType
+	TransferType   USBTransportType
+	Direction      USBDirectionType
+	EndpointNumber uint8
+	DeviceAddress  uint8
+	BusID          uint16
+	TimestampSec   int64
+	TimestampUsec  int32
+	Setup          bool
+	Data           bool
+	Status         int32
+	UrbLength      uint32
+	UrbDataLength  uint32
+
+	UrbInterval            uint32
+	UrbStartFrame          uint32
+	UrbCopyOfTransferFlags uint32
+	IsoNumDesc             uint32
+}
+
+func (u *USB) LayerType() gopacket.LayerType { return LayerTypeUSB }
+
+func (m *USB) NextLayerType() gopacket.LayerType {
+	if m.Setup {
+		return LayerTypeUSBRequestBlockSetup
+	} else if m.Data {
+	}
+
+	return m.TransferType.LayerType()
+}
+
+func decodeUSB(data []byte, p gopacket.PacketBuilder) error {
+	d := &USB{}
+
+	return decodingLayerDecoder(d, data, p)
+}
+
+func (m *USB) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 40 {
+		df.SetTruncated()
+		return errors.New("USB < 40 bytes")
+	}
+	m.ID = binary.LittleEndian.Uint64(data[0:8])
+	m.EventType = USBEventType(data[8])
+	m.TransferType = USBTransportType(data[9])
+
+	m.EndpointNumber = data[10] & 0x7f
+	if data[10]&uint8(USBTransportTypeTransferIn) > 0 {
+		m.Direction = USBDirectionTypeIn
+	} else {
+		m.Direction = USBDirectionTypeOut
+	}
+
+	m.DeviceAddress = data[11]
+	m.BusID = binary.LittleEndian.Uint16(data[12:14])
+
+	if uint(data[14]) == 0 {
+		m.Setup = true
+	}
+
+	if uint(data[15]) == 0 {
+		m.Data = true
+	}
+
+	m.TimestampSec = int64(binary.LittleEndian.Uint64(data[16:24]))
+	m.TimestampUsec = int32(binary.LittleEndian.Uint32(data[24:28]))
+	m.Status = int32(binary.LittleEndian.Uint32(data[28:32]))
+	m.UrbLength = binary.LittleEndian.Uint32(data[32:36])
+	m.UrbDataLength = binary.LittleEndian.Uint32(data[36:40])
+
+	m.Contents = data[:40]
+	m.Payload = data[40:]
+
+	if m.Setup {
+		m.Payload = data[40:]
+	} else if m.Data {
+		m.Payload = data[uint32(len(data))-m.UrbDataLength:]
+	}
+
+	// if 64 bit, dissect_linux_usb_pseudo_header_ext
+	if false {
+		m.UrbInterval = binary.LittleEndian.Uint32(data[40:44])
+		m.UrbStartFrame = binary.LittleEndian.Uint32(data[44:48])
+		m.UrbDataLength = binary.LittleEndian.Uint32(data[48:52])
+		m.IsoNumDesc = binary.LittleEndian.Uint32(data[52:56])
+		m.Contents = data[:56]
+		m.Payload = data[56:]
+	}
+
+	// crc5 or crc16
+	// eop (end of packet)
+
+	return nil
+}
+
+type USBRequestBlockSetup struct {
+	BaseLayer
+	RequestType uint8
+	Request     USBRequestBlockSetupRequest
+	Value       uint16
+	Index       uint16
+	Length      uint16
+}
+
+func (u *USBRequestBlockSetup) LayerType() gopacket.LayerType { return LayerTypeUSBRequestBlockSetup }
+
+func (m *USBRequestBlockSetup) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func (m *USBRequestBlockSetup) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.RequestType = data[0]
+	m.Request = USBRequestBlockSetupRequest(data[1])
+	m.Value = binary.LittleEndian.Uint16(data[2:4])
+	m.Index = binary.LittleEndian.Uint16(data[4:6])
+	m.Length = binary.LittleEndian.Uint16(data[6:8])
+	m.Contents = data[:8]
+	m.Payload = data[8:]
+	return nil
+}
+
+func decodeUSBRequestBlockSetup(data []byte, p gopacket.PacketBuilder) error {
+	d := &USBRequestBlockSetup{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type USBControl struct {
+	BaseLayer
+}
+
+func (u *USBControl) LayerType() gopacket.LayerType { return LayerTypeUSBControl }
+
+func (m *USBControl) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func (m *USBControl) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+func decodeUSBControl(data []byte, p gopacket.PacketBuilder) error {
+	d := &USBControl{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type USBInterrupt struct {
+	BaseLayer
+}
+
+func (u *USBInterrupt) LayerType() gopacket.LayerType { return LayerTypeUSBInterrupt }
+
+func (m *USBInterrupt) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func (m *USBInterrupt) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+func decodeUSBInterrupt(data []byte, p gopacket.PacketBuilder) error {
+	d := &USBInterrupt{}
+	return decodingLayerDecoder(d, data, p)
+}
+
+type USBBulk struct {
+	BaseLayer
+}
+
+func (u *USBBulk) LayerType() gopacket.LayerType { return LayerTypeUSBBulk }
+
+func (m *USBBulk) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypePayload
+}
+
+func (m *USBBulk) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	m.Contents = data
+	return nil
+}
+
+func decodeUSBBulk(data []byte, p gopacket.PacketBuilder) error {
+	d := &USBBulk{}
+	return decodingLayerDecoder(d, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/vrrp.go b/go-controller/vendor/github.com/google/gopacket/layers/vrrp.go
new file mode 100644
index 00000000000..ffaafe6a772
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/vrrp.go
@@ -0,0 +1,156 @@
+// Copyright 2016 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"net"
+
+	"github.com/google/gopacket"
+)
+
+/*
+	This layer provides decoding for Virtual Router Redundancy Protocol (VRRP) v2.
+	https://tools.ietf.org/html/rfc3768#section-5
+    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
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |Version| Type  | Virtual Rtr ID|   Priority    | Count IP Addrs|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |   Auth Type   |   Adver Int   |          Checksum             |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                         IP Address (1)                        |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                            .                                  |
+   |                            .                                  |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                         IP Address (n)                        |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                     Authentication Data (1)                   |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                     Authentication Data (2)                   |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+type VRRPv2Type uint8
+type VRRPv2AuthType uint8
+
+const (
+	VRRPv2Advertisement VRRPv2Type = 0x01 // router advertisement
+)
+
+// String conversions for VRRP message types
+func (v VRRPv2Type) String() string {
+	switch v {
+	case VRRPv2Advertisement:
+		return "VRRPv2 Advertisement"
+	default:
+		return ""
+	}
+}
+
+const (
+	VRRPv2AuthNoAuth    VRRPv2AuthType = 0x00 // No Authentication
+	VRRPv2AuthReserved1 VRRPv2AuthType = 0x01 // Reserved field 1
+	VRRPv2AuthReserved2 VRRPv2AuthType = 0x02 // Reserved field 2
+)
+
+func (v VRRPv2AuthType) String() string {
+	switch v {
+	case VRRPv2AuthNoAuth:
+		return "No Authentication"
+	case VRRPv2AuthReserved1:
+		return "Reserved"
+	case VRRPv2AuthReserved2:
+		return "Reserved"
+	default:
+		return ""
+	}
+}
+
+// VRRPv2 represents an VRRP v2 message.
+type VRRPv2 struct {
+	BaseLayer
+	Version      uint8          // The version field specifies the VRRP protocol version of this packet (v2)
+	Type         VRRPv2Type     // The type field specifies the type of this VRRP packet.  The only type defined in v2 is ADVERTISEMENT
+	VirtualRtrID uint8          // identifies the virtual router this packet is reporting status for
+	Priority     uint8          // specifies the sending VRRP router's priority for the virtual router (100 = default)
+	CountIPAddr  uint8          // The number of IP addresses contained in this VRRP advertisement.
+	AuthType     VRRPv2AuthType // identifies the authentication method being utilized
+	AdverInt     uint8          // The Advertisement interval indicates the time interval (in seconds) between ADVERTISEMENTS.  The default is 1 second
+	Checksum     uint16         // used to detect data corruption in the VRRP message.
+	IPAddress    []net.IP       // one or more IP addresses associated with the virtual router. Specified in the CountIPAddr field.
+}
+
+// LayerType returns LayerTypeVRRP for VRRP v2 message.
+func (v *VRRPv2) LayerType() gopacket.LayerType { return LayerTypeVRRP }
+
+func (v *VRRPv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+
+	v.BaseLayer = BaseLayer{Contents: data[:len(data)]}
+	v.Version = data[0] >> 4 // high nibble == VRRP version. We're expecting v2
+
+	v.Type = VRRPv2Type(data[0] & 0x0F) // low nibble == VRRP type. Expecting 1 (advertisement)
+	if v.Type != 1 {
+		// rfc3768: A packet with unknown type MUST be discarded.
+		return errors.New("Unrecognized VRRPv2 type field.")
+	}
+
+	v.VirtualRtrID = data[1]
+	v.Priority = data[2]
+
+	v.CountIPAddr = data[3]
+	if v.CountIPAddr < 1 {
+		return errors.New("VRRPv2 number of IP addresses is not valid.")
+	}
+
+	v.AuthType = VRRPv2AuthType(data[4])
+	v.AdverInt = uint8(data[5])
+	v.Checksum = binary.BigEndian.Uint16(data[6:8])
+
+	// populate the IPAddress field. The number of addresses is specified in the v.CountIPAddr field
+	// offset references the starting byte containing the list of ip addresses
+	offset := 8
+	for i := uint8(0); i < v.CountIPAddr; i++ {
+		v.IPAddress = append(v.IPAddress, data[offset:offset+4])
+		offset += 4
+	}
+
+	//	any trailing packets here may be authentication data and *should* be ignored in v2 as per RFC
+	//
+	//			5.3.10.  Authentication Data
+	//
+	//			The authentication string is currently only used to maintain
+	//			backwards compatibility with RFC 2338.  It SHOULD be set to zero on
+	//	   		transmission and ignored on reception.
+	return nil
+}
+
+// CanDecode specifies the layer type in which we are attempting to unwrap.
+func (v *VRRPv2) CanDecode() gopacket.LayerClass {
+	return LayerTypeVRRP
+}
+
+// NextLayerType specifies the next layer that should be decoded. VRRP does not contain any further payload, so we set to 0
+func (v *VRRPv2) NextLayerType() gopacket.LayerType {
+	return gopacket.LayerTypeZero
+}
+
+// The VRRP packet does not include payload data. Setting byte slice to nil
+func (v *VRRPv2) Payload() []byte {
+	return nil
+}
+
+// decodeVRRP will parse VRRP v2
+func decodeVRRP(data []byte, p gopacket.PacketBuilder) error {
+	if len(data) < 8 {
+		return errors.New("Not a valid VRRP packet. Packet length is too small.")
+	}
+	v := &VRRPv2{}
+	return decodingLayerDecoder(v, data, p)
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers/vxlan.go b/go-controller/vendor/github.com/google/gopacket/layers/vxlan.go
new file mode 100644
index 00000000000..e479cd81f3e
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers/vxlan.go
@@ -0,0 +1,123 @@
+// Copyright 2016 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+package layers
+
+import (
+	"encoding/binary"
+	"errors"
+	"fmt"
+
+	"github.com/google/gopacket"
+)
+
+//  VXLAN is specifed in RFC 7348 https://tools.ietf.org/html/rfc7348
+//  G, D, A, Group Policy ID from https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00
+//  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
+//  0             8               16              24              32
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |G|R|R|R|I|R|R|R|R|D|R|R|A|R|R|R|       Group Policy ID         |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |     24 bit VXLAN Network Identifier           |   Reserved    |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+// VXLAN is a VXLAN packet header
+type VXLAN struct {
+	BaseLayer
+	ValidIDFlag      bool   // 'I' bit per RFC 7348
+	VNI              uint32 // 'VXLAN Network Identifier' 24 bits per RFC 7348
+	GBPExtension     bool   // 'G' bit per Group Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00
+	GBPDontLearn     bool   // 'D' bit per Group Policy
+	GBPApplied       bool   // 'A' bit per Group Policy
+	GBPGroupPolicyID uint16 // 'Group Policy ID' 16 bits per Group Policy
+}
+
+// LayerType returns LayerTypeVXLAN
+func (vx *VXLAN) LayerType() gopacket.LayerType { return LayerTypeVXLAN }
+
+// CanDecode returns the layer type this DecodingLayer can decode
+func (vx *VXLAN) CanDecode() gopacket.LayerClass {
+	return LayerTypeVXLAN
+}
+
+// NextLayerType retuns the next layer we should see after vxlan
+func (vx *VXLAN) NextLayerType() gopacket.LayerType {
+	return LayerTypeEthernet
+}
+
+// DecodeFromBytes takes a byte buffer and decodes
+func (vx *VXLAN) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
+	if len(data) < 8 {
+		return errors.New("vxlan packet too small")
+	}
+	// VNI is a 24bit number, Uint32 requires 32 bits
+	var buf [4]byte
+	copy(buf[1:], data[4:7])
+
+	// RFC 7348 https://tools.ietf.org/html/rfc7348
+	vx.ValidIDFlag = data[0]&0x08 > 0        // 'I' bit per RFC7348
+	vx.VNI = binary.BigEndian.Uint32(buf[:]) // VXLAN Network Identifier per RFC7348
+
+	// Group Based Policy https://tools.ietf.org/html/draft-smith-vxlan-group-policy-00
+	vx.GBPExtension = data[0]&0x80 > 0                       // 'G' bit per the group policy draft
+	vx.GBPDontLearn = data[1]&0x40 > 0                       // 'D' bit - the egress VTEP MUST NOT learn the source address of the encapsulated frame.
+	vx.GBPApplied = data[1]&0x80 > 0                         // 'A' bit - indicates that the group policy has already been applied to this packet.
+	vx.GBPGroupPolicyID = binary.BigEndian.Uint16(data[2:4]) // Policy ID as per the group policy draft
+
+	// Layer information
+	const vxlanLength = 8
+	vx.Contents = data[:vxlanLength]
+	vx.Payload = data[vxlanLength:]
+
+	return nil
+
+}
+
+func decodeVXLAN(data []byte, p gopacket.PacketBuilder) error {
+	vx := &VXLAN{}
+	err := vx.DecodeFromBytes(data, p)
+	if err != nil {
+		return err
+	}
+
+	p.AddLayer(vx)
+	return p.NextDecoder(LinkTypeEthernet)
+}
+
+// SerializeTo writes the serialized form of this layer into the
+// SerializationBuffer, implementing gopacket.SerializableLayer.
+// See the docs for gopacket.SerializableLayer for more info.
+func (vx *VXLAN) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
+	bytes, err := b.PrependBytes(8)
+	if err != nil {
+		return err
+	}
+
+	// PrependBytes does not guarantee that bytes are zeroed.  Setting flags via OR requires that they start off at zero
+	bytes[0] = 0
+	bytes[1] = 0
+
+	if vx.ValidIDFlag {
+		bytes[0] |= 0x08
+	}
+	if vx.GBPExtension {
+		bytes[0] |= 0x80
+	}
+	if vx.GBPDontLearn {
+		bytes[1] |= 0x40
+	}
+	if vx.GBPApplied {
+		bytes[1] |= 0x80
+	}
+
+	binary.BigEndian.PutUint16(bytes[2:4], vx.GBPGroupPolicyID)
+	if vx.VNI >= 1<<24 {
+		return fmt.Errorf("Virtual Network Identifier = %x exceeds max for 24-bit uint", vx.VNI)
+	}
+	binary.BigEndian.PutUint32(bytes[4:8], vx.VNI<<8)
+	return nil
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layers_decoder.go b/go-controller/vendor/github.com/google/gopacket/layers_decoder.go
new file mode 100644
index 00000000000..8c1f108cfc6
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layers_decoder.go
@@ -0,0 +1,101 @@
+// Copyright 2019 The GoPacket Authors. All rights reserved.
+
+package gopacket
+
+// Created by gen.go, don't edit manually
+// Generated at 2019-06-18 11:37:31.308731293 +0600 +06 m=+0.000842599
+
+// LayersDecoder returns DecodingLayerFunc for specified
+// DecodingLayerContainer, LayerType value to start decoding with and
+// some DecodeFeedback.
+func LayersDecoder(dl DecodingLayerContainer, first LayerType, df DecodeFeedback) DecodingLayerFunc {
+	firstDec, ok := dl.Decoder(first)
+	if !ok {
+		return func([]byte, *[]LayerType) (LayerType, error) {
+			return first, nil
+		}
+	}
+	if dlc, ok := dl.(DecodingLayerSparse); ok {
+		return func(data []byte, decoded *[]LayerType) (LayerType, error) {
+			*decoded = (*decoded)[:0] // Truncated decoded layers.
+			typ := first
+			decoder := firstDec
+			for {
+				if err := decoder.DecodeFromBytes(data, df); err != nil {
+					return LayerTypeZero, err
+				}
+				*decoded = append(*decoded, typ)
+				typ = decoder.NextLayerType()
+				if data = decoder.LayerPayload(); len(data) == 0 {
+					break
+				}
+				if decoder, ok = dlc.Decoder(typ); !ok {
+					return typ, nil
+				}
+			}
+			return LayerTypeZero, nil
+		}
+	}
+	if dlc, ok := dl.(DecodingLayerArray); ok {
+		return func(data []byte, decoded *[]LayerType) (LayerType, error) {
+			*decoded = (*decoded)[:0] // Truncated decoded layers.
+			typ := first
+			decoder := firstDec
+			for {
+				if err := decoder.DecodeFromBytes(data, df); err != nil {
+					return LayerTypeZero, err
+				}
+				*decoded = append(*decoded, typ)
+				typ = decoder.NextLayerType()
+				if data = decoder.LayerPayload(); len(data) == 0 {
+					break
+				}
+				if decoder, ok = dlc.Decoder(typ); !ok {
+					return typ, nil
+				}
+			}
+			return LayerTypeZero, nil
+		}
+	}
+	if dlc, ok := dl.(DecodingLayerMap); ok {
+		return func(data []byte, decoded *[]LayerType) (LayerType, error) {
+			*decoded = (*decoded)[:0] // Truncated decoded layers.
+			typ := first
+			decoder := firstDec
+			for {
+				if err := decoder.DecodeFromBytes(data, df); err != nil {
+					return LayerTypeZero, err
+				}
+				*decoded = append(*decoded, typ)
+				typ = decoder.NextLayerType()
+				if data = decoder.LayerPayload(); len(data) == 0 {
+					break
+				}
+				if decoder, ok = dlc.Decoder(typ); !ok {
+					return typ, nil
+				}
+			}
+			return LayerTypeZero, nil
+		}
+	}
+	dlc := dl
+	return func(data []byte, decoded *[]LayerType) (LayerType, error) {
+		*decoded = (*decoded)[:0] // Truncated decoded layers.
+		typ := first
+		decoder := firstDec
+		for {
+			if err := decoder.DecodeFromBytes(data, df); err != nil {
+				return LayerTypeZero, err
+			}
+			*decoded = append(*decoded, typ)
+			typ = decoder.NextLayerType()
+			if data = decoder.LayerPayload(); len(data) == 0 {
+				break
+			}
+			if decoder, ok = dlc.Decoder(typ); !ok {
+				return typ, nil
+			}
+		}
+		return LayerTypeZero, nil
+	}
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/layertype.go b/go-controller/vendor/github.com/google/gopacket/layertype.go
new file mode 100644
index 00000000000..3abfee1e9ba
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/layertype.go
@@ -0,0 +1,111 @@
+// 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"
+	"strconv"
+)
+
+// LayerType is a unique identifier for each type of layer.  This enumeration
+// does not match with any externally available numbering scheme... it's solely
+// usable/useful within this library as a means for requesting layer types
+// (see Packet.Layer) and determining which types of layers have been decoded.
+//
+// New LayerTypes may be created by calling gopacket.RegisterLayerType.
+type LayerType int64
+
+// LayerTypeMetadata contains metadata associated with each LayerType.
+type LayerTypeMetadata struct {
+	// Name is the string returned by each layer type's String method.
+	Name string
+	// Decoder is the decoder to use when the layer type is passed in as a
+	// Decoder.
+	Decoder Decoder
+}
+
+type layerTypeMetadata struct {
+	inUse bool
+	LayerTypeMetadata
+}
+
+// DecodersByLayerName maps layer names to decoders for those layers.
+// This allows users to specify decoders by name to a program and have that
+// program pick the correct decoder accordingly.
+var DecodersByLayerName = map[string]Decoder{}
+
+const maxLayerType = 2000
+
+var ltMeta [maxLayerType]layerTypeMetadata
+var ltMetaMap = map[LayerType]layerTypeMetadata{}
+
+// RegisterLayerType creates a new layer type and registers it globally.
+// The number passed in must be unique, or a runtime panic will occur.  Numbers
+// 0-999 are reserved for the gopacket library.  Numbers 1000-1999 should be
+// used for common application-specific types, and are very fast.  Any other
+// number (negative or >= 2000) may be used for uncommon application-specific
+// types, and are somewhat slower (they require a map lookup over an array
+// index).
+func RegisterLayerType(num int, meta LayerTypeMetadata) LayerType {
+	if 0 <= num && num < maxLayerType {
+		if ltMeta[num].inUse {
+			panic("Layer type already exists")
+		}
+	} else {
+		if ltMetaMap[LayerType(num)].inUse {
+			panic("Layer type already exists")
+		}
+	}
+	return OverrideLayerType(num, meta)
+}
+
+// OverrideLayerType acts like RegisterLayerType, except that if the layer type
+// has already been registered, it overrides the metadata with the passed-in
+// metadata intead of panicing.
+func OverrideLayerType(num int, meta LayerTypeMetadata) LayerType {
+	if 0 <= num && num < maxLayerType {
+		ltMeta[num] = layerTypeMetadata{
+			inUse:             true,
+			LayerTypeMetadata: meta,
+		}
+	} else {
+		ltMetaMap[LayerType(num)] = layerTypeMetadata{
+			inUse:             true,
+			LayerTypeMetadata: meta,
+		}
+	}
+	DecodersByLayerName[meta.Name] = meta.Decoder
+	return LayerType(num)
+}
+
+// Decode decodes the given data using the decoder registered with the layer
+// type.
+func (t LayerType) Decode(data []byte, c PacketBuilder) error {
+	var d Decoder
+	if 0 <= int(t) && int(t) < maxLayerType {
+		d = ltMeta[int(t)].Decoder
+	} else {
+		d = ltMetaMap[t].Decoder
+	}
+	if d != nil {
+		return d.Decode(data, c)
+	}
+	return fmt.Errorf("Layer type %v has no associated decoder", t)
+}
+
+// String returns the string associated with this layer type.
+func (t LayerType) String() (s string) {
+	if 0 <= int(t) && int(t) < maxLayerType {
+		s = ltMeta[int(t)].Name
+	} else {
+		s = ltMetaMap[t].Name
+	}
+	if s == "" {
+		s = strconv.Itoa(int(t))
+	}
+	return
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/packet.go b/go-controller/vendor/github.com/google/gopacket/packet.go
new file mode 100644
index 00000000000..3a7c4b3d80b
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/packet.go
@@ -0,0 +1,864 @@
+// 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"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"os"
+	"reflect"
+	"runtime/debug"
+	"strings"
+	"syscall"
+	"time"
+)
+
+// CaptureInfo provides standardized information about a packet captured off
+// the wire or read from a file.
+type CaptureInfo struct {
+	// Timestamp is the time the packet was captured, if that is known.
+	Timestamp time.Time
+	// CaptureLength is the total number of bytes read off of the wire.
+	CaptureLength int
+	// Length is the size of the original packet.  Should always be >=
+	// CaptureLength.
+	Length int
+	// InterfaceIndex
+	InterfaceIndex int
+	// The packet source can place ancillary data of various types here.
+	// For example, the afpacket source can report the VLAN of captured
+	// packets this way.
+	AncillaryData []interface{}
+}
+
+// PacketMetadata contains metadata for a packet.
+type PacketMetadata struct {
+	CaptureInfo
+	// Truncated is true if packet decoding logic detects that there are fewer
+	// bytes in the packet than are detailed in various headers (for example, if
+	// the number of bytes in the IPv4 contents/payload is less than IPv4.Length).
+	// This is also set automatically for packets captured off the wire if
+	// CaptureInfo.CaptureLength < CaptureInfo.Length.
+	Truncated bool
+}
+
+// Packet is the primary object used by gopacket.  Packets are created by a
+// Decoder's Decode call.  A packet is made up of a set of Data, which
+// is broken into a number of Layers as it is decoded.
+type Packet interface {
+	//// Functions for outputting the packet as a human-readable string:
+	//// ------------------------------------------------------------------
+	// String returns a human-readable string representation of the packet.
+	// It uses LayerString on each layer to output the layer.
+	String() string
+	// Dump returns a verbose human-readable string representation of the packet,
+	// including a hex dump of all layers.  It uses LayerDump on each layer to
+	// output the layer.
+	Dump() string
+
+	//// Functions for accessing arbitrary packet layers:
+	//// ------------------------------------------------------------------
+	// Layers returns all layers in this packet, computing them as necessary
+	Layers() []Layer
+	// Layer returns the first layer in this packet of the given type, or nil
+	Layer(LayerType) Layer
+	// LayerClass returns the first layer in this packet of the given class,
+	// or nil.
+	LayerClass(LayerClass) Layer
+
+	//// Functions for accessing specific types of packet layers.  These functions
+	//// return the first layer of each type found within the packet.
+	//// ------------------------------------------------------------------
+	// LinkLayer returns the first link layer in the packet
+	LinkLayer() LinkLayer
+	// NetworkLayer returns the first network layer in the packet
+	NetworkLayer() NetworkLayer
+	// TransportLayer returns the first transport layer in the packet
+	TransportLayer() TransportLayer
+	// ApplicationLayer returns the first application layer in the packet
+	ApplicationLayer() ApplicationLayer
+	// ErrorLayer is particularly useful, since it returns nil if the packet
+	// was fully decoded successfully, and non-nil if an error was encountered
+	// in decoding and the packet was only partially decoded.  Thus, its output
+	// can be used to determine if the entire packet was able to be decoded.
+	ErrorLayer() ErrorLayer
+
+	//// Functions for accessing data specific to the packet:
+	//// ------------------------------------------------------------------
+	// Data returns the set of bytes that make up this entire packet.
+	Data() []byte
+	// Metadata returns packet metadata associated with this packet.
+	Metadata() *PacketMetadata
+}
+
+// packet contains all the information we need to fulfill the Packet interface,
+// and its two "subclasses" (yes, no such thing in Go, bear with me),
+// eagerPacket and lazyPacket, provide eager and lazy decoding logic around the
+// various functions needed to access this information.
+type packet struct {
+	// data contains the entire packet data for a packet
+	data []byte
+	// initialLayers is space for an initial set of layers already created inside
+	// the packet.
+	initialLayers [6]Layer
+	// layers contains each layer we've already decoded
+	layers []Layer
+	// last is the last layer added to the packet
+	last Layer
+	// metadata is the PacketMetadata for this packet
+	metadata PacketMetadata
+
+	decodeOptions DecodeOptions
+
+	// Pointers to the various important layers
+	link        LinkLayer
+	network     NetworkLayer
+	transport   TransportLayer
+	application ApplicationLayer
+	failure     ErrorLayer
+}
+
+func (p *packet) SetTruncated() {
+	p.metadata.Truncated = true
+}
+
+func (p *packet) SetLinkLayer(l LinkLayer) {
+	if p.link == nil {
+		p.link = l
+	}
+}
+
+func (p *packet) SetNetworkLayer(l NetworkLayer) {
+	if p.network == nil {
+		p.network = l
+	}
+}
+
+func (p *packet) SetTransportLayer(l TransportLayer) {
+	if p.transport == nil {
+		p.transport = l
+	}
+}
+
+func (p *packet) SetApplicationLayer(l ApplicationLayer) {
+	if p.application == nil {
+		p.application = l
+	}
+}
+
+func (p *packet) SetErrorLayer(l ErrorLayer) {
+	if p.failure == nil {
+		p.failure = l
+	}
+}
+
+func (p *packet) AddLayer(l Layer) {
+	p.layers = append(p.layers, l)
+	p.last = l
+}
+
+func (p *packet) DumpPacketData() {
+	fmt.Fprint(os.Stderr, p.packetDump())
+	os.Stderr.Sync()
+}
+
+func (p *packet) Metadata() *PacketMetadata {
+	return &p.metadata
+}
+
+func (p *packet) Data() []byte {
+	return p.data
+}
+
+func (p *packet) DecodeOptions() *DecodeOptions {
+	return &p.decodeOptions
+}
+
+func (p *packet) addFinalDecodeError(err error, stack []byte) {
+	fail := &DecodeFailure{err: err, stack: stack}
+	if p.last == nil {
+		fail.data = p.data
+	} else {
+		fail.data = p.last.LayerPayload()
+	}
+	p.AddLayer(fail)
+	p.SetErrorLayer(fail)
+}
+
+func (p *packet) recoverDecodeError() {
+	if !p.decodeOptions.SkipDecodeRecovery {
+		if r := recover(); r != nil {
+			p.addFinalDecodeError(fmt.Errorf("%v", r), debug.Stack())
+		}
+	}
+}
+
+// LayerString outputs an individual layer as a string.  The layer is output
+// in a single line, with no trailing newline.  This function is specifically
+// designed to do the right thing for most layers... it follows the following
+// rules:
+//  * If the Layer has a String function, just output that.
+//  * Otherwise, output all exported fields in the layer, recursing into
+//    exported slices and structs.
+// NOTE:  This is NOT THE SAME AS fmt's "%#v".  %#v will output both exported
+// and unexported fields... many times packet layers contain unexported stuff
+// that would just mess up the output of the layer, see for example the
+// Payload layer and it's internal 'data' field, which contains a large byte
+// array that would really mess up formatting.
+func LayerString(l Layer) string {
+	return fmt.Sprintf("%v\t%s", l.LayerType(), layerString(reflect.ValueOf(l), false, false))
+}
+
+// Dumper dumps verbose information on a value.  If a layer type implements
+// Dumper, then its LayerDump() string will include the results in its output.
+type Dumper interface {
+	Dump() string
+}
+
+// LayerDump outputs a very verbose string representation of a layer.  Its
+// output is a concatenation of LayerString(l) and hex.Dump(l.LayerContents()).
+// It contains newlines and ends with a newline.
+func LayerDump(l Layer) string {
+	var b bytes.Buffer
+	b.WriteString(LayerString(l))
+	b.WriteByte('\n')
+	if d, ok := l.(Dumper); ok {
+		dump := d.Dump()
+		if dump != "" {
+			b.WriteString(dump)
+			if dump[len(dump)-1] != '\n' {
+				b.WriteByte('\n')
+			}
+		}
+	}
+	b.WriteString(hex.Dump(l.LayerContents()))
+	return b.String()
+}
+
+// layerString outputs, recursively, a layer in a "smart" way.  See docs for
+// LayerString for more details.
+//
+// Params:
+//   i - value to write out
+//   anonymous:  if we're currently recursing an anonymous member of a struct
+//   writeSpace:  if we've already written a value in a struct, and need to
+//     write a space before writing more.  This happens when we write various
+//     anonymous values, and need to keep writing more.
+func layerString(v reflect.Value, anonymous bool, writeSpace bool) string {
+	// Let String() functions take precedence.
+	if v.CanInterface() {
+		if s, ok := v.Interface().(fmt.Stringer); ok {
+			return s.String()
+		}
+	}
+	// Reflect, and spit out all the exported fields as key=value.
+	switch v.Type().Kind() {
+	case reflect.Interface, reflect.Ptr:
+		if v.IsNil() {
+			return "nil"
+		}
+		r := v.Elem()
+		return layerString(r, anonymous, writeSpace)
+	case reflect.Struct:
+		var b bytes.Buffer
+		typ := v.Type()
+		if !anonymous {
+			b.WriteByte('{')
+		}
+		for i := 0; i < v.NumField(); i++ {
+			// Check if this is upper-case.
+			ftype := typ.Field(i)
+			f := v.Field(i)
+			if ftype.Anonymous {
+				anonStr := layerString(f, true, writeSpace)
+				writeSpace = writeSpace || anonStr != ""
+				b.WriteString(anonStr)
+			} else if ftype.PkgPath == "" { // exported
+				if writeSpace {
+					b.WriteByte(' ')
+				}
+				writeSpace = true
+				fmt.Fprintf(&b, "%s=%s", typ.Field(i).Name, layerString(f, false, writeSpace))
+			}
+		}
+		if !anonymous {
+			b.WriteByte('}')
+		}
+		return b.String()
+	case reflect.Slice:
+		var b bytes.Buffer
+		b.WriteByte('[')
+		if v.Len() > 4 {
+			fmt.Fprintf(&b, "..%d..", v.Len())
+		} else {
+			for j := 0; j < v.Len(); j++ {
+				if j != 0 {
+					b.WriteString(", ")
+				}
+				b.WriteString(layerString(v.Index(j), false, false))
+			}
+		}
+		b.WriteByte(']')
+		return b.String()
+	}
+	return fmt.Sprintf("%v", v.Interface())
+}
+
+const (
+	longBytesLength = 128
+)
+
+// LongBytesGoString returns a string representation of the byte slice shortened
+// using the format '<type>{<truncated slice> ... (<n> bytes)}' if it
+// exceeds a predetermined length. Can be used to avoid filling the display with
+// very long byte strings.
+func LongBytesGoString(buf []byte) string {
+	if len(buf) < longBytesLength {
+		return fmt.Sprintf("%#v", buf)
+	}
+	s := fmt.Sprintf("%#v", buf[:longBytesLength-1])
+	s = strings.TrimSuffix(s, "}")
+	return fmt.Sprintf("%s ... (%d bytes)}", s, len(buf))
+}
+
+func baseLayerString(value reflect.Value) string {
+	t := value.Type()
+	content := value.Field(0)
+	c := make([]byte, content.Len())
+	for i := range c {
+		c[i] = byte(content.Index(i).Uint())
+	}
+	payload := value.Field(1)
+	p := make([]byte, payload.Len())
+	for i := range p {
+		p[i] = byte(payload.Index(i).Uint())
+	}
+	return fmt.Sprintf("%s{Contents:%s, Payload:%s}", t.String(),
+		LongBytesGoString(c),
+		LongBytesGoString(p))
+}
+
+func layerGoString(i interface{}, b *bytes.Buffer) {
+	if s, ok := i.(fmt.GoStringer); ok {
+		b.WriteString(s.GoString())
+		return
+	}
+
+	var v reflect.Value
+	var ok bool
+	if v, ok = i.(reflect.Value); !ok {
+		v = reflect.ValueOf(i)
+	}
+	switch v.Kind() {
+	case reflect.Ptr, reflect.Interface:
+		if v.Kind() == reflect.Ptr {
+			b.WriteByte('&')
+		}
+		layerGoString(v.Elem().Interface(), b)
+	case reflect.Struct:
+		t := v.Type()
+		b.WriteString(t.String())
+		b.WriteByte('{')
+		for i := 0; i < v.NumField(); i++ {
+			if i > 0 {
+				b.WriteString(", ")
+			}
+			if t.Field(i).Name == "BaseLayer" {
+				fmt.Fprintf(b, "BaseLayer:%s", baseLayerString(v.Field(i)))
+			} else if v.Field(i).Kind() == reflect.Struct {
+				fmt.Fprintf(b, "%s:", t.Field(i).Name)
+				layerGoString(v.Field(i), b)
+			} else if v.Field(i).Kind() == reflect.Ptr {
+				b.WriteByte('&')
+				layerGoString(v.Field(i), b)
+			} else {
+				fmt.Fprintf(b, "%s:%#v", t.Field(i).Name, v.Field(i))
+			}
+		}
+		b.WriteByte('}')
+	default:
+		fmt.Fprintf(b, "%#v", i)
+	}
+}
+
+// LayerGoString returns a representation of the layer in Go syntax,
+// taking care to shorten "very long" BaseLayer byte slices
+func LayerGoString(l Layer) string {
+	b := new(bytes.Buffer)
+	layerGoString(l, b)
+	return b.String()
+}
+
+func (p *packet) packetString() string {
+	var b bytes.Buffer
+	fmt.Fprintf(&b, "PACKET: %d bytes", len(p.Data()))
+	if p.metadata.Truncated {
+		b.WriteString(", truncated")
+	}
+	if p.metadata.Length > 0 {
+		fmt.Fprintf(&b, ", wire length %d cap length %d", p.metadata.Length, p.metadata.CaptureLength)
+	}
+	if !p.metadata.Timestamp.IsZero() {
+		fmt.Fprintf(&b, " @ %v", p.metadata.Timestamp)
+	}
+	b.WriteByte('\n')
+	for i, l := range p.layers {
+		fmt.Fprintf(&b, "- Layer %d (%02d bytes) = %s\n", i+1, len(l.LayerContents()), LayerString(l))
+	}
+	return b.String()
+}
+
+func (p *packet) packetDump() string {
+	var b bytes.Buffer
+	fmt.Fprintf(&b, "-- FULL PACKET DATA (%d bytes) ------------------------------------\n%s", len(p.data), hex.Dump(p.data))
+	for i, l := range p.layers {
+		fmt.Fprintf(&b, "--- Layer %d ---\n%s", i+1, LayerDump(l))
+	}
+	return b.String()
+}
+
+// eagerPacket is a packet implementation that does eager decoding.  Upon
+// initial construction, it decodes all the layers it can from packet data.
+// eagerPacket implements Packet and PacketBuilder.
+type eagerPacket struct {
+	packet
+}
+
+var errNilDecoder = errors.New("NextDecoder passed nil decoder, probably an unsupported decode type")
+
+func (p *eagerPacket) NextDecoder(next Decoder) error {
+	if next == nil {
+		return errNilDecoder
+	}
+	if p.last == nil {
+		return errors.New("NextDecoder called, but no layers added yet")
+	}
+	d := p.last.LayerPayload()
+	if len(d) == 0 {
+		return nil
+	}
+	// Since we're eager, immediately call the next decoder.
+	return next.Decode(d, p)
+}
+func (p *eagerPacket) initialDecode(dec Decoder) {
+	defer p.recoverDecodeError()
+	err := dec.Decode(p.data, p)
+	if err != nil {
+		p.addFinalDecodeError(err, nil)
+	}
+}
+func (p *eagerPacket) LinkLayer() LinkLayer {
+	return p.link
+}
+func (p *eagerPacket) NetworkLayer() NetworkLayer {
+	return p.network
+}
+func (p *eagerPacket) TransportLayer() TransportLayer {
+	return p.transport
+}
+func (p *eagerPacket) ApplicationLayer() ApplicationLayer {
+	return p.application
+}
+func (p *eagerPacket) ErrorLayer() ErrorLayer {
+	return p.failure
+}
+func (p *eagerPacket) Layers() []Layer {
+	return p.layers
+}
+func (p *eagerPacket) Layer(t LayerType) Layer {
+	for _, l := range p.layers {
+		if l.LayerType() == t {
+			return l
+		}
+	}
+	return nil
+}
+func (p *eagerPacket) LayerClass(lc LayerClass) Layer {
+	for _, l := range p.layers {
+		if lc.Contains(l.LayerType()) {
+			return l
+		}
+	}
+	return nil
+}
+func (p *eagerPacket) String() string { return p.packetString() }
+func (p *eagerPacket) Dump() string   { return p.packetDump() }
+
+// lazyPacket does lazy decoding on its packet data.  On construction it does
+// no initial decoding.  For each function call, it decodes only as many layers
+// as are necessary to compute the return value for that function.
+// lazyPacket implements Packet and PacketBuilder.
+type lazyPacket struct {
+	packet
+	next Decoder
+}
+
+func (p *lazyPacket) NextDecoder(next Decoder) error {
+	if next == nil {
+		return errNilDecoder
+	}
+	p.next = next
+	return nil
+}
+func (p *lazyPacket) decodeNextLayer() {
+	if p.next == nil {
+		return
+	}
+	d := p.data
+	if p.last != nil {
+		d = p.last.LayerPayload()
+	}
+	next := p.next
+	p.next = nil
+	// We've just set p.next to nil, so if we see we have no data, this should be
+	// the final call we get to decodeNextLayer if we return here.
+	if len(d) == 0 {
+		return
+	}
+	defer p.recoverDecodeError()
+	err := next.Decode(d, p)
+	if err != nil {
+		p.addFinalDecodeError(err, nil)
+	}
+}
+func (p *lazyPacket) LinkLayer() LinkLayer {
+	for p.link == nil && p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.link
+}
+func (p *lazyPacket) NetworkLayer() NetworkLayer {
+	for p.network == nil && p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.network
+}
+func (p *lazyPacket) TransportLayer() TransportLayer {
+	for p.transport == nil && p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.transport
+}
+func (p *lazyPacket) ApplicationLayer() ApplicationLayer {
+	for p.application == nil && p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.application
+}
+func (p *lazyPacket) ErrorLayer() ErrorLayer {
+	for p.failure == nil && p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.failure
+}
+func (p *lazyPacket) Layers() []Layer {
+	for p.next != nil {
+		p.decodeNextLayer()
+	}
+	return p.layers
+}
+func (p *lazyPacket) Layer(t LayerType) Layer {
+	for _, l := range p.layers {
+		if l.LayerType() == t {
+			return l
+		}
+	}
+	numLayers := len(p.layers)
+	for p.next != nil {
+		p.decodeNextLayer()
+		for _, l := range p.layers[numLayers:] {
+			if l.LayerType() == t {
+				return l
+			}
+		}
+		numLayers = len(p.layers)
+	}
+	return nil
+}
+func (p *lazyPacket) LayerClass(lc LayerClass) Layer {
+	for _, l := range p.layers {
+		if lc.Contains(l.LayerType()) {
+			return l
+		}
+	}
+	numLayers := len(p.layers)
+	for p.next != nil {
+		p.decodeNextLayer()
+		for _, l := range p.layers[numLayers:] {
+			if lc.Contains(l.LayerType()) {
+				return l
+			}
+		}
+		numLayers = len(p.layers)
+	}
+	return nil
+}
+func (p *lazyPacket) String() string { p.Layers(); return p.packetString() }
+func (p *lazyPacket) Dump() string   { p.Layers(); return p.packetDump() }
+
+// DecodeOptions tells gopacket how to decode a packet.
+type DecodeOptions struct {
+	// Lazy decoding decodes the minimum number of layers needed to return data
+	// for a packet at each function call.  Be careful using this with concurrent
+	// packet processors, as each call to packet.* could mutate the packet, and
+	// two concurrent function calls could interact poorly.
+	Lazy bool
+	// NoCopy decoding doesn't copy its input buffer into storage that's owned by
+	// the packet.  If you can guarantee that the bytes underlying the slice
+	// passed into NewPacket aren't going to be modified, this can be faster.  If
+	// there's any chance that those bytes WILL be changed, this will invalidate
+	// your packets.
+	NoCopy bool
+	// SkipDecodeRecovery skips over panic recovery during packet decoding.
+	// Normally, when packets decode, if a panic occurs, that panic is captured
+	// by a recover(), and a DecodeFailure layer is added to the packet detailing
+	// the issue.  If this flag is set, panics are instead allowed to continue up
+	// the stack.
+	SkipDecodeRecovery bool
+	// DecodeStreamsAsDatagrams enables routing of application-level layers in the TCP
+	// decoder. If true, we should try to decode layers after TCP in single packets.
+	// This is disabled by default because the reassembly package drives the decoding
+	// of TCP payload data after reassembly.
+	DecodeStreamsAsDatagrams bool
+}
+
+// Default decoding provides the safest (but slowest) method for decoding
+// packets.  It eagerly processes all layers (so it's concurrency-safe) and it
+// copies its input buffer upon creation of the packet (so the packet remains
+// valid if the underlying slice is modified.  Both of these take time,
+// though, so beware.  If you can guarantee that the packet will only be used
+// by one goroutine at a time, set Lazy decoding.  If you can guarantee that
+// the underlying slice won't change, set NoCopy decoding.
+var Default = DecodeOptions{}
+
+// Lazy is a DecodeOptions with just Lazy set.
+var Lazy = DecodeOptions{Lazy: true}
+
+// NoCopy is a DecodeOptions with just NoCopy set.
+var NoCopy = DecodeOptions{NoCopy: true}
+
+// DecodeStreamsAsDatagrams is a DecodeOptions with just DecodeStreamsAsDatagrams set.
+var DecodeStreamsAsDatagrams = DecodeOptions{DecodeStreamsAsDatagrams: true}
+
+// NewPacket creates a new Packet object from a set of bytes.  The
+// firstLayerDecoder tells it how to interpret the first layer from the bytes,
+// future layers will be generated from that first layer automatically.
+func NewPacket(data []byte, firstLayerDecoder Decoder, options DecodeOptions) Packet {
+	if !options.NoCopy {
+		dataCopy := make([]byte, len(data))
+		copy(dataCopy, data)
+		data = dataCopy
+	}
+	if options.Lazy {
+		p := &lazyPacket{
+			packet: packet{data: data, decodeOptions: options},
+			next:   firstLayerDecoder,
+		}
+		p.layers = p.initialLayers[:0]
+		// Crazy craziness:
+		// If the following return statemet is REMOVED, and Lazy is FALSE, then
+		// eager packet processing becomes 17% FASTER.  No, there is no logical
+		// explanation for this.  However, it's such a hacky micro-optimization that
+		// we really can't rely on it.  It appears to have to do with the size the
+		// compiler guesses for this function's stack space, since one symptom is
+		// that with the return statement in place, we more than double calls to
+		// runtime.morestack/runtime.lessstack.  We'll hope the compiler gets better
+		// over time and we get this optimization for free.  Until then, we'll have
+		// to live with slower packet processing.
+		return p
+	}
+	p := &eagerPacket{
+		packet: packet{data: data, decodeOptions: options},
+	}
+	p.layers = p.initialLayers[:0]
+	p.initialDecode(firstLayerDecoder)
+	return p
+}
+
+// PacketDataSource is an interface for some source of packet data.  Users may
+// create their own implementations, or use the existing implementations in
+// gopacket/pcap (libpcap, allows reading from live interfaces or from
+// pcap files) or gopacket/pfring (PF_RING, allows reading from live
+// interfaces).
+type PacketDataSource interface {
+	// ReadPacketData returns the next packet available from this data source.
+	// It returns:
+	//  data:  The bytes of an individual packet.
+	//  ci:  Metadata about the capture
+	//  err:  An error encountered while reading packet data.  If err != nil,
+	//    then data/ci will be ignored.
+	ReadPacketData() (data []byte, ci CaptureInfo, err error)
+}
+
+// ConcatFinitePacketDataSources returns a PacketDataSource that wraps a set
+// of internal PacketDataSources, each of which will stop with io.EOF after
+// reading a finite number of packets.  The returned PacketDataSource will
+// return all packets from the first finite source, followed by all packets from
+// the second, etc.  Once all finite sources have returned io.EOF, the returned
+// source will as well.
+func ConcatFinitePacketDataSources(pds ...PacketDataSource) PacketDataSource {
+	c := concat(pds)
+	return &c
+}
+
+type concat []PacketDataSource
+
+func (c *concat) ReadPacketData() (data []byte, ci CaptureInfo, err error) {
+	for len(*c) > 0 {
+		data, ci, err = (*c)[0].ReadPacketData()
+		if err == io.EOF {
+			*c = (*c)[1:]
+			continue
+		}
+		return
+	}
+	return nil, CaptureInfo{}, io.EOF
+}
+
+// ZeroCopyPacketDataSource is an interface to pull packet data from sources
+// that allow data to be returned without copying to a user-controlled buffer.
+// It's very similar to PacketDataSource, except that the caller must be more
+// careful in how the returned buffer is handled.
+type ZeroCopyPacketDataSource interface {
+	// ZeroCopyReadPacketData returns the next packet available from this data source.
+	// It returns:
+	//  data:  The bytes of an individual packet.  Unlike with
+	//    PacketDataSource's ReadPacketData, the slice returned here points
+	//    to a buffer owned by the data source.  In particular, the bytes in
+	//    this buffer may be changed by future calls to
+	//    ZeroCopyReadPacketData.  Do not use the returned buffer after
+	//    subsequent ZeroCopyReadPacketData calls.
+	//  ci:  Metadata about the capture
+	//  err:  An error encountered while reading packet data.  If err != nil,
+	//    then data/ci will be ignored.
+	ZeroCopyReadPacketData() (data []byte, ci CaptureInfo, err error)
+}
+
+// PacketSource reads in packets from a PacketDataSource, decodes them, and
+// returns them.
+//
+// There are currently two different methods for reading packets in through
+// a PacketSource:
+//
+// Reading With Packets Function
+//
+// This method is the most convenient and easiest to code, but lacks
+// flexibility.  Packets returns a 'chan Packet', then asynchronously writes
+// packets into that channel.  Packets uses a blocking channel, and closes
+// it if an io.EOF is returned by the underlying PacketDataSource.  All other
+// PacketDataSource errors are ignored and discarded.
+//  for packet := range packetSource.Packets() {
+//    ...
+//  }
+//
+// Reading With NextPacket Function
+//
+// This method is the most flexible, and exposes errors that may be
+// encountered by the underlying PacketDataSource.  It's also the fastest
+// in a tight loop, since it doesn't have the overhead of a channel
+// read/write.  However, it requires the user to handle errors, most
+// importantly the io.EOF error in cases where packets are being read from
+// a file.
+//  for {
+//    packet, err := packetSource.NextPacket()
+//    if err == io.EOF {
+//      break
+//    } else if err != nil {
+//      log.Println("Error:", err)
+//      continue
+//    }
+//    handlePacket(packet)  // Do something with each packet.
+//  }
+type PacketSource struct {
+	source  PacketDataSource
+	decoder Decoder
+	// DecodeOptions is the set of options to use for decoding each piece
+	// of packet data.  This can/should be changed by the user to reflect the
+	// way packets should be decoded.
+	DecodeOptions
+	c chan Packet
+}
+
+// NewPacketSource creates a packet data source.
+func NewPacketSource(source PacketDataSource, decoder Decoder) *PacketSource {
+	return &PacketSource{
+		source:  source,
+		decoder: decoder,
+	}
+}
+
+// NextPacket returns the next decoded packet from the PacketSource.  On error,
+// it returns a nil packet and a non-nil error.
+func (p *PacketSource) NextPacket() (Packet, error) {
+	data, ci, err := p.source.ReadPacketData()
+	if err != nil {
+		return nil, err
+	}
+	packet := NewPacket(data, p.decoder, p.DecodeOptions)
+	m := packet.Metadata()
+	m.CaptureInfo = ci
+	m.Truncated = m.Truncated || ci.CaptureLength < ci.Length
+	return packet, nil
+}
+
+// packetsToChannel reads in all packets from the packet source and sends them
+// to the given channel. This routine terminates when a non-temporary error
+// is returned by NextPacket().
+func (p *PacketSource) packetsToChannel() {
+	defer close(p.c)
+	for {
+		packet, err := p.NextPacket()
+		if err == nil {
+			p.c <- packet
+			continue
+		}
+
+		// Immediately retry for temporary network errors
+		if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
+			continue
+		}
+
+		// Immediately retry for EAGAIN
+		if err == syscall.EAGAIN {
+			continue
+		}
+
+		// Immediately break for known unrecoverable errors
+		if err == io.EOF || err == io.ErrUnexpectedEOF ||
+			err == io.ErrNoProgress || err == io.ErrClosedPipe || err == io.ErrShortBuffer ||
+			err == syscall.EBADF ||
+			strings.Contains(err.Error(), "use of closed file") {
+			break
+		}
+
+		// Sleep briefly and try again
+		time.Sleep(time.Millisecond * time.Duration(5))
+	}
+}
+
+// Packets returns a channel of packets, allowing easy iterating over
+// packets.  Packets will be asynchronously read in from the underlying
+// PacketDataSource and written to the returned channel.  If the underlying
+// PacketDataSource returns an io.EOF error, the channel will be closed.
+// If any other error is encountered, it is ignored.
+//
+//  for packet := range packetSource.Packets() {
+//    handlePacket(packet)  // Do something with each packet.
+//  }
+//
+// If called more than once, returns the same channel.
+func (p *PacketSource) Packets() chan Packet {
+	if p.c == nil {
+		p.c = make(chan Packet, 1000)
+		go p.packetsToChannel()
+	}
+	return p.c
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/parser.go b/go-controller/vendor/github.com/google/gopacket/parser.go
new file mode 100644
index 00000000000..4a4676f1cf4
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/parser.go
@@ -0,0 +1,350 @@
+// 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"
+)
+
+// A container for single LayerType->DecodingLayer mapping.
+type decodingLayerElem struct {
+	typ LayerType
+	dec DecodingLayer
+}
+
+// DecodingLayer is an interface for packet layers that can decode themselves.
+//
+// The important part of DecodingLayer is that they decode themselves in-place.
+// Calling DecodeFromBytes on a DecodingLayer totally resets the entire layer to
+// the new state defined by the data passed in.  A returned error leaves the
+// DecodingLayer in an unknown intermediate state, thus its fields should not be
+// trusted.
+//
+// Because the DecodingLayer is resetting its own fields, a call to
+// DecodeFromBytes should normally not require any memory allocation.
+type DecodingLayer interface {
+	// DecodeFromBytes resets the internal state of this layer to the state
+	// defined by the passed-in bytes.  Slices in the DecodingLayer may
+	// reference the passed-in data, so care should be taken to copy it
+	// first should later modification of data be required before the
+	// DecodingLayer is discarded.
+	DecodeFromBytes(data []byte, df DecodeFeedback) error
+	// CanDecode returns the set of LayerTypes this DecodingLayer can
+	// decode.  For Layers that are also DecodingLayers, this will most
+	// often be that Layer's LayerType().
+	CanDecode() LayerClass
+	// NextLayerType returns the LayerType which should be used to decode
+	// the LayerPayload.
+	NextLayerType() LayerType
+	// LayerPayload is the set of bytes remaining to decode after a call to
+	// DecodeFromBytes.
+	LayerPayload() []byte
+}
+
+// DecodingLayerFunc decodes given packet and stores decoded LayerType
+// values into specified slice. Returns either first encountered
+// unsupported LayerType value or decoding error. In case of success,
+// returns (LayerTypeZero, nil).
+type DecodingLayerFunc func([]byte, *[]LayerType) (LayerType, error)
+
+// DecodingLayerContainer stores all DecodingLayer-s and serves as a
+// searching tool for DecodingLayerParser.
+type DecodingLayerContainer interface {
+	// Put adds new DecodingLayer to container. The new instance of
+	// the same DecodingLayerContainer is returned so it may be
+	// implemented as a value receiver.
+	Put(DecodingLayer) DecodingLayerContainer
+	// Decoder returns DecodingLayer to decode given LayerType and
+	// true if it was found. If no decoder found, return false.
+	Decoder(LayerType) (DecodingLayer, bool)
+	// LayersDecoder returns DecodingLayerFunc which decodes given
+	// packet, starting with specified LayerType and DecodeFeedback.
+	LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc
+}
+
+// DecodingLayerSparse is a sparse array-based implementation of
+// DecodingLayerContainer. Each DecodingLayer is addressed in an
+// allocated slice by LayerType value itself. Though this is the
+// fastest container it may be memory-consuming if used with big
+// LayerType values.
+type DecodingLayerSparse []DecodingLayer
+
+// Put implements DecodingLayerContainer interface.
+func (dl DecodingLayerSparse) Put(d DecodingLayer) DecodingLayerContainer {
+	maxLayerType := LayerType(len(dl) - 1)
+	for _, typ := range d.CanDecode().LayerTypes() {
+		if typ > maxLayerType {
+			maxLayerType = typ
+		}
+	}
+
+	if extra := maxLayerType - LayerType(len(dl)) + 1; extra > 0 {
+		dl = append(dl, make([]DecodingLayer, extra)...)
+	}
+
+	for _, typ := range d.CanDecode().LayerTypes() {
+		dl[typ] = d
+	}
+	return dl
+}
+
+// LayersDecoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerSparse) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
+	return LayersDecoder(dl, first, df)
+}
+
+// Decoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerSparse) Decoder(typ LayerType) (DecodingLayer, bool) {
+	if int64(typ) < int64(len(dl)) {
+		decoder := dl[typ]
+		return decoder, decoder != nil
+	}
+	return nil, false
+}
+
+// DecodingLayerArray is an array-based implementation of
+// DecodingLayerContainer. Each DecodingLayer is searched linearly in
+// an allocated slice in one-by-one fashion.
+type DecodingLayerArray []decodingLayerElem
+
+// Put implements DecodingLayerContainer interface.
+func (dl DecodingLayerArray) Put(d DecodingLayer) DecodingLayerContainer {
+TYPES:
+	for _, typ := range d.CanDecode().LayerTypes() {
+		for i := range dl {
+			if dl[i].typ == typ {
+				dl[i].dec = d
+				continue TYPES
+			}
+		}
+		dl = append(dl, decodingLayerElem{typ, d})
+	}
+	return dl
+}
+
+// Decoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerArray) Decoder(typ LayerType) (DecodingLayer, bool) {
+	for i := range dl {
+		if dl[i].typ == typ {
+			return dl[i].dec, true
+		}
+	}
+	return nil, false
+}
+
+// LayersDecoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerArray) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
+	return LayersDecoder(dl, first, df)
+}
+
+// DecodingLayerMap is an map-based implementation of
+// DecodingLayerContainer. Each DecodingLayer is searched in a map
+// hashed by LayerType value.
+type DecodingLayerMap map[LayerType]DecodingLayer
+
+// Put implements DecodingLayerContainer interface.
+func (dl DecodingLayerMap) Put(d DecodingLayer) DecodingLayerContainer {
+	for _, typ := range d.CanDecode().LayerTypes() {
+		if dl == nil {
+			dl = make(map[LayerType]DecodingLayer)
+		}
+		dl[typ] = d
+	}
+	return dl
+}
+
+// Decoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerMap) Decoder(typ LayerType) (DecodingLayer, bool) {
+	d, ok := dl[typ]
+	return d, ok
+}
+
+// LayersDecoder implements DecodingLayerContainer interface.
+func (dl DecodingLayerMap) LayersDecoder(first LayerType, df DecodeFeedback) DecodingLayerFunc {
+	return LayersDecoder(dl, first, df)
+}
+
+// Static code check.
+var (
+	_ = []DecodingLayerContainer{
+		DecodingLayerSparse(nil),
+		DecodingLayerMap(nil),
+		DecodingLayerArray(nil),
+	}
+)
+
+// DecodingLayerParser parses a given set of layer types.  See DecodeLayers for
+// more information on how DecodingLayerParser should be used.
+type DecodingLayerParser struct {
+	// DecodingLayerParserOptions is the set of options available to the
+	// user to define the parser's behavior.
+	DecodingLayerParserOptions
+	dlc   DecodingLayerContainer
+	first LayerType
+	df    DecodeFeedback
+
+	decodeFunc DecodingLayerFunc
+
+	// Truncated is set when a decode layer detects that the packet has been
+	// truncated.
+	Truncated bool
+}
+
+// AddDecodingLayer adds a decoding layer to the parser.  This adds support for
+// the decoding layer's CanDecode layers to the parser... should they be
+// encountered, they'll be parsed.
+func (l *DecodingLayerParser) AddDecodingLayer(d DecodingLayer) {
+	l.SetDecodingLayerContainer(l.dlc.Put(d))
+}
+
+// SetTruncated is used by DecodingLayers to set the Truncated boolean in the
+// DecodingLayerParser.  Users should simply read Truncated after calling
+// DecodeLayers.
+func (l *DecodingLayerParser) SetTruncated() {
+	l.Truncated = true
+}
+
+// NewDecodingLayerParser creates a new DecodingLayerParser and adds in all
+// of the given DecodingLayers with AddDecodingLayer.
+//
+// Each call to DecodeLayers will attempt to decode the given bytes first by
+// treating them as a 'first'-type layer, then by using NextLayerType on
+// subsequently decoded layers to find the next relevant decoder.  Should a
+// deoder not be available for the layer type returned by NextLayerType,
+// decoding will stop.
+//
+// NewDecodingLayerParser uses DecodingLayerMap container by
+// default.
+func NewDecodingLayerParser(first LayerType, decoders ...DecodingLayer) *DecodingLayerParser {
+	dlp := &DecodingLayerParser{first: first}
+	dlp.df = dlp // Cast this once to the interface
+	// default container
+	dlc := DecodingLayerContainer(DecodingLayerMap(make(map[LayerType]DecodingLayer)))
+	for _, d := range decoders {
+		dlc = dlc.Put(d)
+	}
+
+	dlp.SetDecodingLayerContainer(dlc)
+	return dlp
+}
+
+// SetDecodingLayerContainer specifies container with decoders. This
+// call replaces all decoders already registered in given instance of
+// DecodingLayerParser.
+func (l *DecodingLayerParser) SetDecodingLayerContainer(dlc DecodingLayerContainer) {
+	l.dlc = dlc
+	l.decodeFunc = l.dlc.LayersDecoder(l.first, l.df)
+}
+
+// DecodeLayers decodes as many layers as possible from the given data.  It
+// initially treats the data as layer type 'typ', then uses NextLayerType on
+// each subsequent decoded layer until it gets to a layer type it doesn't know
+// how to parse.
+//
+// For each layer successfully decoded, DecodeLayers appends the layer type to
+// the decoded slice.  DecodeLayers truncates the 'decoded' slice initially, so
+// there's no need to empty it yourself.
+//
+// This decoding method is about an order of magnitude faster than packet
+// decoding, because it only decodes known layers that have already been
+// allocated.  This means it doesn't need to allocate each layer it returns...
+// instead it overwrites the layers that already exist.
+//
+// Example usage:
+//    func main() {
+//      var eth layers.Ethernet
+//      var ip4 layers.IPv4
+//      var ip6 layers.IPv6
+//      var tcp layers.TCP
+//      var udp layers.UDP
+//      var payload gopacket.Payload
+//      parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ip6, &tcp, &udp, &payload)
+//      var source gopacket.PacketDataSource = getMyDataSource()
+//      decodedLayers := make([]gopacket.LayerType, 0, 10)
+//      for {
+//        data, _, err := source.ReadPacketData()
+//        if err != nil {
+//          fmt.Println("Error reading packet data: ", err)
+//          continue
+//        }
+//        fmt.Println("Decoding packet")
+//        err = parser.DecodeLayers(data, &decodedLayers)
+//        for _, typ := range decodedLayers {
+//          fmt.Println("  Successfully decoded layer type", typ)
+//          switch typ {
+//            case layers.LayerTypeEthernet:
+//              fmt.Println("    Eth ", eth.SrcMAC, eth.DstMAC)
+//            case layers.LayerTypeIPv4:
+//              fmt.Println("    IP4 ", ip4.SrcIP, ip4.DstIP)
+//            case layers.LayerTypeIPv6:
+//              fmt.Println("    IP6 ", ip6.SrcIP, ip6.DstIP)
+//            case layers.LayerTypeTCP:
+//              fmt.Println("    TCP ", tcp.SrcPort, tcp.DstPort)
+//            case layers.LayerTypeUDP:
+//              fmt.Println("    UDP ", udp.SrcPort, udp.DstPort)
+//          }
+//        }
+//        if decodedLayers.Truncated {
+//          fmt.Println("  Packet has been truncated")
+//        }
+//        if err != nil {
+//          fmt.Println("  Error encountered:", err)
+//        }
+//      }
+//    }
+//
+// If DecodeLayers is unable to decode the next layer type, it will return the
+// error UnsupportedLayerType.
+func (l *DecodingLayerParser) DecodeLayers(data []byte, decoded *[]LayerType) (err error) {
+	l.Truncated = false
+	if !l.IgnorePanic {
+		defer panicToError(&err)
+	}
+	typ, err := l.decodeFunc(data, decoded)
+	if typ != LayerTypeZero {
+		// no decoder
+		if l.IgnoreUnsupported {
+			return nil
+		}
+		return UnsupportedLayerType(typ)
+	}
+	return err
+}
+
+// UnsupportedLayerType is returned by DecodingLayerParser if DecodeLayers
+// encounters a layer type that the DecodingLayerParser has no decoder for.
+type UnsupportedLayerType LayerType
+
+// Error implements the error interface, returning a string to say that the
+// given layer type is unsupported.
+func (e UnsupportedLayerType) Error() string {
+	return fmt.Sprintf("No decoder for layer type %v", LayerType(e))
+}
+
+func panicToError(e *error) {
+	if r := recover(); r != nil {
+		*e = fmt.Errorf("panic: %v", r)
+	}
+}
+
+// DecodingLayerParserOptions provides options to affect the behavior of a given
+// DecodingLayerParser.
+type DecodingLayerParserOptions struct {
+	// IgnorePanic determines whether a DecodingLayerParser should stop
+	// panics on its own (by returning them as an error from DecodeLayers)
+	// or should allow them to raise up the stack.  Handling errors does add
+	// latency to the process of decoding layers, but is much safer for
+	// callers.  IgnorePanic defaults to false, thus if the caller does
+	// nothing decode panics will be returned as errors.
+	IgnorePanic bool
+	// IgnoreUnsupported will stop parsing and return a nil error when it
+	// encounters a layer it doesn't have a parser for, instead of returning an
+	// UnsupportedLayerType error.  If this is true, it's up to the caller to make
+	// sure that all expected layers have been parsed (by checking the decoded
+	// slice).
+	IgnoreUnsupported bool
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/time.go b/go-controller/vendor/github.com/google/gopacket/time.go
new file mode 100644
index 00000000000..6d116cdfbc7
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/time.go
@@ -0,0 +1,72 @@
+// 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 gopacket
+
+import (
+	"fmt"
+	"math"
+	"time"
+)
+
+// TimestampResolution represents the resolution of timestamps in Base^Exponent.
+type TimestampResolution struct {
+	Base, Exponent int
+}
+
+func (t TimestampResolution) String() string {
+	return fmt.Sprintf("%d^%d", t.Base, t.Exponent)
+}
+
+// ToDuration returns the smallest representable time difference as a time.Duration
+func (t TimestampResolution) ToDuration() time.Duration {
+	if t.Base == 0 {
+		return 0
+	}
+	if t.Exponent == 0 {
+		return time.Second
+	}
+	switch t.Base {
+	case 10:
+		return time.Duration(math.Pow10(t.Exponent + 9))
+	case 2:
+		if t.Exponent < 0 {
+			return time.Second >> uint(-t.Exponent)
+		}
+		return time.Second << uint(t.Exponent)
+	default:
+		// this might loose precision
+		return time.Duration(float64(time.Second) * math.Pow(float64(t.Base), float64(t.Exponent)))
+	}
+}
+
+// TimestampResolutionInvalid represents an invalid timestamp resolution
+var TimestampResolutionInvalid = TimestampResolution{}
+
+// TimestampResolutionMillisecond is a resolution of 10^-3s
+var TimestampResolutionMillisecond = TimestampResolution{10, -3}
+
+// TimestampResolutionMicrosecond is a resolution of 10^-6s
+var TimestampResolutionMicrosecond = TimestampResolution{10, -6}
+
+// TimestampResolutionNanosecond is a resolution of 10^-9s
+var TimestampResolutionNanosecond = TimestampResolution{10, -9}
+
+// TimestampResolutionNTP is the resolution of NTP timestamps which is 2^-32 ≈ 233 picoseconds
+var TimestampResolutionNTP = TimestampResolution{2, -32}
+
+// TimestampResolutionCaptureInfo is the resolution used in CaptureInfo, which his currently nanosecond
+var TimestampResolutionCaptureInfo = TimestampResolutionNanosecond
+
+// PacketSourceResolution is an interface for packet data sources that
+// support reporting the timestamp resolution of the aqcuired timestamps.
+// Returned timestamps will always have NanosecondTimestampResolution due
+// to the use of time.Time, but scaling might have occured if acquired
+// timestamps have a different resolution.
+type PacketSourceResolution interface {
+	// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
+	Resolution() TimestampResolution
+}
diff --git a/go-controller/vendor/github.com/google/gopacket/writer.go b/go-controller/vendor/github.com/google/gopacket/writer.go
new file mode 100644
index 00000000000..5d303dc4a78
--- /dev/null
+++ b/go-controller/vendor/github.com/google/gopacket/writer.go
@@ -0,0 +1,232 @@
+// 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"
+)
+
+// SerializableLayer allows its implementations to be written out as a set of bytes,
+// so those bytes may be sent on the wire or otherwise used by the caller.
+// SerializableLayer is implemented by certain Layer types, and can be encoded to
+// bytes using the LayerWriter object.
+type SerializableLayer interface {
+	// SerializeTo writes this layer to a slice, growing that slice if necessary
+	// to make it fit the layer's data.
+	//  Args:
+	//   b:  SerializeBuffer to write this layer on to.  When called, b.Bytes()
+	//     is the payload this layer should wrap, if any.  Note that this
+	//     layer can either prepend itself (common), append itself
+	//     (uncommon), or both (sometimes padding or footers are required at
+	//     the end of packet data). It's also possible (though probably very
+	//     rarely needed) to overwrite any bytes in the current payload.
+	//     After this call, b.Bytes() should return the byte encoding of
+	//     this layer wrapping the original b.Bytes() payload.
+	//   opts:  options to use while writing out data.
+	//  Returns:
+	//   error if a problem was encountered during encoding.  If an error is
+	//   returned, the bytes in data should be considered invalidated, and
+	//   not used.
+	//
+	// SerializeTo calls SHOULD entirely ignore LayerContents and
+	// LayerPayload.  It just serializes based on struct fields, neither
+	// modifying nor using contents/payload.
+	SerializeTo(b SerializeBuffer, opts SerializeOptions) error
+	// LayerType returns the type of the layer that is being serialized to the buffer
+	LayerType() LayerType
+}
+
+// SerializeOptions provides options for behaviors that SerializableLayers may want to
+// implement.
+type SerializeOptions struct {
+	// FixLengths determines whether, during serialization, layers should fix
+	// the values for any length field that depends on the payload.
+	FixLengths bool
+	// ComputeChecksums determines whether, during serialization, layers
+	// should recompute checksums based on their payloads.
+	ComputeChecksums bool
+}
+
+// SerializeBuffer is a helper used by gopacket for writing out packet layers.
+// SerializeBuffer starts off as an empty []byte.  Subsequent calls to PrependBytes
+// return byte slices before the current Bytes(), AppendBytes returns byte
+// slices after.
+//
+// Byte slices returned by PrependBytes/AppendBytes are NOT zero'd out, so if
+// you want to make sure they're all zeros, set them as such.
+//
+// SerializeBuffer is specifically designed to handle packet writing, where unlike
+// with normal writes it's easier to start writing at the inner-most layer and
+// work out, meaning that we often need to prepend bytes.  This runs counter to
+// typical writes to byte slices using append(), where we only write at the end
+// of the buffer.
+//
+// It can be reused via Clear.  Note, however, that a Clear call will invalidate the
+// byte slices returned by any previous Bytes() call (the same buffer is
+// reused).
+//
+//  1) Reusing a write buffer is generally much faster than creating a new one,
+//     and with the default implementation it avoids additional memory allocations.
+//  2) If a byte slice from a previous Bytes() call will continue to be used,
+//     it's better to create a new SerializeBuffer.
+//
+// The Clear method is specifically designed to minimize memory allocations for
+// similar later workloads on the SerializeBuffer.  IE: if you make a set of
+// Prepend/Append calls, then clear, then make the same calls with the same
+// sizes, the second round (and all future similar rounds) shouldn't allocate
+// any new memory.
+type SerializeBuffer interface {
+	// Bytes returns the contiguous set of bytes collected so far by Prepend/Append
+	// calls.  The slice returned by Bytes will be modified by future Clear calls,
+	// so if you're planning on clearing this SerializeBuffer, you may want to copy
+	// Bytes somewhere safe first.
+	Bytes() []byte
+	// PrependBytes returns a set of bytes which prepends the current bytes in this
+	// buffer.  These bytes start in an indeterminate state, so they should be
+	// overwritten by the caller.  The caller must only call PrependBytes if they
+	// know they're going to immediately overwrite all bytes returned.
+	PrependBytes(num int) ([]byte, error)
+	// AppendBytes returns a set of bytes which appends the current bytes in this
+	// buffer.  These bytes start in an indeterminate state, so they should be
+	// overwritten by the caller.  The caller must only call AppendBytes if they
+	// know they're going to immediately overwrite all bytes returned.
+	AppendBytes(num int) ([]byte, error)
+	// Clear resets the SerializeBuffer to a new, empty buffer.  After a call to clear,
+	// the byte slice returned by any previous call to Bytes() for this buffer
+	// should be considered invalidated.
+	Clear() error
+	// Layers returns all the Layers that have been successfully serialized into this buffer
+	// already.
+	Layers() []LayerType
+	// PushLayer adds the current Layer to the list of Layers that have been serialized
+	// into this buffer.
+	PushLayer(LayerType)
+}
+
+type serializeBuffer struct {
+	data                []byte
+	start               int
+	prepended, appended int
+	layers              []LayerType
+}
+
+// NewSerializeBuffer creates a new instance of the default implementation of
+// the SerializeBuffer interface.
+func NewSerializeBuffer() SerializeBuffer {
+	return &serializeBuffer{}
+}
+
+// NewSerializeBufferExpectedSize creates a new buffer for serialization, optimized for an
+// expected number of bytes prepended/appended.  This tends to decrease the
+// number of memory allocations made by the buffer during writes.
+func NewSerializeBufferExpectedSize(expectedPrependLength, expectedAppendLength int) SerializeBuffer {
+	return &serializeBuffer{
+		data:      make([]byte, expectedPrependLength, expectedPrependLength+expectedAppendLength),
+		start:     expectedPrependLength,
+		prepended: expectedPrependLength,
+		appended:  expectedAppendLength,
+	}
+}
+
+func (w *serializeBuffer) Bytes() []byte {
+	return w.data[w.start:]
+}
+
+func (w *serializeBuffer) PrependBytes(num int) ([]byte, error) {
+	if num < 0 {
+		panic("num < 0")
+	}
+	if w.start < num {
+		toPrepend := w.prepended
+		if toPrepend < num {
+			toPrepend = num
+		}
+		w.prepended += toPrepend
+		length := cap(w.data) + toPrepend
+		newData := make([]byte, length)
+		newStart := w.start + toPrepend
+		copy(newData[newStart:], w.data[w.start:])
+		w.start = newStart
+		w.data = newData[:toPrepend+len(w.data)]
+	}
+	w.start -= num
+	return w.data[w.start : w.start+num], nil
+}
+
+func (w *serializeBuffer) AppendBytes(num int) ([]byte, error) {
+	if num < 0 {
+		panic("num < 0")
+	}
+	initialLength := len(w.data)
+	if cap(w.data)-initialLength < num {
+		toAppend := w.appended
+		if toAppend < num {
+			toAppend = num
+		}
+		w.appended += toAppend
+		newData := make([]byte, cap(w.data)+toAppend)
+		copy(newData[w.start:], w.data[w.start:])
+		w.data = newData[:initialLength]
+	}
+	// Grow the buffer.  We know it'll be under capacity given above.
+	w.data = w.data[:initialLength+num]
+	return w.data[initialLength:], nil
+}
+
+func (w *serializeBuffer) Clear() error {
+	w.start = w.prepended
+	w.data = w.data[:w.start]
+	w.layers = w.layers[:0]
+	return nil
+}
+
+func (w *serializeBuffer) Layers() []LayerType {
+	return w.layers
+}
+
+func (w *serializeBuffer) PushLayer(l LayerType) {
+	w.layers = append(w.layers, l)
+}
+
+// SerializeLayers clears the given write buffer, then writes all layers into it so
+// they correctly wrap each other.  Note that by clearing the buffer, it
+// invalidates all slices previously returned by w.Bytes()
+//
+// Example:
+//   buf := gopacket.NewSerializeBuffer()
+//   opts := gopacket.SerializeOptions{}
+//   gopacket.SerializeLayers(buf, opts, a, b, c)
+//   firstPayload := buf.Bytes()  // contains byte representation of a(b(c))
+//   gopacket.SerializeLayers(buf, opts, d, e, f)
+//   secondPayload := buf.Bytes()  // contains byte representation of d(e(f)). firstPayload is now invalidated, since the SerializeLayers call Clears buf.
+func SerializeLayers(w SerializeBuffer, opts SerializeOptions, layers ...SerializableLayer) error {
+	w.Clear()
+	for i := len(layers) - 1; i >= 0; i-- {
+		layer := layers[i]
+		err := layer.SerializeTo(w, opts)
+		if err != nil {
+			return err
+		}
+		w.PushLayer(layer.LayerType())
+	}
+	return nil
+}
+
+// SerializePacket is a convenience function that calls SerializeLayers
+// on packet's Layers().
+// It returns an error if one of the packet layers is not a SerializableLayer.
+func SerializePacket(buf SerializeBuffer, opts SerializeOptions, packet Packet) error {
+	sls := []SerializableLayer{}
+	for _, layer := range packet.Layers() {
+		sl, ok := layer.(SerializableLayer)
+		if !ok {
+			return fmt.Errorf("layer %s is not serializable", layer.LayerType().String())
+		}
+		sls = append(sls, sl)
+	}
+	return SerializeLayers(buf, opts, sls...)
+}
diff --git a/go-controller/vendor/modules.txt b/go-controller/vendor/modules.txt
index 6af1617d6b8..62500883ad9 100644
--- a/go-controller/vendor/modules.txt
+++ b/go-controller/vendor/modules.txt
@@ -152,6 +152,10 @@ github.com/google/go-cmp/cmp/internal/value
 ## explicit; go 1.12
 github.com/google/gofuzz
 github.com/google/gofuzz/bytesource
+# github.com/google/gopacket v1.1.19
+## explicit; go 1.12
+github.com/google/gopacket
+github.com/google/gopacket/layers
 # github.com/google/uuid v1.6.0
 ## explicit
 github.com/google/uuid

From d94b26555c562619d8427b454d38c3188b06a19c Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Tue, 6 Aug 2024 10:54:37 +0200
Subject: [PATCH 09/48] Add observability docs

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 docs/images/ovnkube-observ.png          | Bin 0 -> 61002 bytes
 docs/observability/ovn-observability.md | 131 ++++++++++++++++++++++++
 mkdocs.yml                              |   1 +
 3 files changed, 132 insertions(+)
 create mode 100644 docs/images/ovnkube-observ.png
 create mode 100644 docs/observability/ovn-observability.md

diff --git a/docs/images/ovnkube-observ.png b/docs/images/ovnkube-observ.png
new file mode 100644
index 0000000000000000000000000000000000000000..c8644d368b3cb6b07d520b9f1519ec15d05615d8
GIT binary patch
literal 61002
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

literal 0
HcmV?d00001

diff --git a/docs/observability/ovn-observability.md b/docs/observability/ovn-observability.md
new file mode 100644
index 00000000000..0507b9e7252
--- /dev/null
+++ b/docs/observability/ovn-observability.md
@@ -0,0 +1,131 @@
+# Observability
+
+## Introduction
+
+Observability feature uses OVN sampling functionality to generate samples with requested metadata when
+specific OVS flows are matched. To see the generated samples, a binary called `ovnkube-observ` is used.
+This binary allows printing the samples to stdout or writing them to a file.
+
+Currently, supports observability for:
+- Network Policy
+- (Baseline) Admin Network Policy
+- Egress firewall
+- UDN isolation
+- Multicast ACLs
+
+More features are planned to be added in the future. 
+
+## Motivation
+
+Networking observability is an important feature to verify the expected networking behavior in a cluster and
+to debug existing problems.
+Ovn-kubernetes makes use of many abstraction layers (through NBDB, logical flows, openflow flows and datapath flows) 
+that translate kubernetes feature into very specific rules that apply 
+to each packet in the network. Therefore, even though there are ways to see what OVS/OVN is doing with a particular packet, 
+there is no way to know why.
+
+We aim to solve this problem by providing a way for ovn-kubernetes to generate packet samples enriched with metadata 
+that can be easily correlated back to kubernetes objects or other human-readable pieces of information that provide 
+insights of what ovn-kubernetes is doing with a packet and why.
+
+### User-Stories/Use-Cases
+
+- As a user I want to make sure that the network policies/egress firewalls/etc. are correctly enforced in my cluster.
+- As a cluster admin I want to check why some traffic is allowed or dropped.
+
+## How to enable this feature on an OVN-Kubernetes cluster?
+
+To enable this feature, use `--observability` flag with `kind.sh` script or `--enable-observability` flag with `ovnkube` binary.
+
+To see the samples, use `ovnkube-observ` binary, use `-h` to see allowed flags.
+
+This feature requires OVS 3.4 and linux kernel 6.11.
+
+As of Aug 2024, the kernel need to be built from the source, therefore to try this feature you need to:
+- rebuild the kernel with the current master branch from [Linus' tree](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git)
+  - to rebuild on fedora: https://docs.fedoraproject.org/en-US/quick-docs/kernel-build-custom/#_building_a_vanilla_upstream_kernel
+- Build an ovn-kubernetes image that uses the latest OVS/OVN code:
+`OVS_BRANCH=main make -C dist/images fedora-dev`
+- Start kind with that image, use `-ov localhost/ovn-kube-fedora-dev:latest` flag with `kind.sh` script.
+
+## Workflow Description
+
+- Observability is enabled by setting the `--enable-observability` flag in the `ovnkube` binary.
+- For now all mentioned features are enabled by this flag at the same time.
+- `ovnkube-observ` binary is used to see the samples. Samples are only generated when the real traffic matching the ACLs
+is sent through the OVS. An example output is:
+```
+OVN-K message: Allowed by default allow from local node policy, direction ingress
+src=10.129.2.2, dst=10.129.2.5
+```
+
+## Implementation Details
+
+### User facing API Changes
+
+No API changes were done.
+
+### OVN sampling details
+
+OVN has 3 main db tables that are used for sampling:
+- `Sample_collector`: This table is used to define the sampling collector. It defines the sampling rate and collectorID, 
+which is used to set up collectors in the OVS. 
+- `Sampling_app`: This table is used to set `ID`s for existing OVN sampling applications, that are sent together with the samples.
+- `Sample`: This table is used to define required samples and point to the collectors. 
+Every sample has `Metadata` that is sent together with the sample.
+
+Samples are attached to the other db tables, for now only to ACLs.
+A sample is generated when a packet matches the ACL. Every Sample contains `Sampling_app.ID` and `Sample.Metadata`,
+that is decoded by `go-controller/observability-lib`.
+
+### OVN-Kubernetes Implementation Details
+
+`Sample_collector` and `Sampling_app` are created or cleaned up when the observability is enabled/disabled on startup.
+When one of the supported objects (for example, network policy) is created, ovn-kuberentes generates an nbdb `Sample` for it.
+
+To decode the samples into human-readable information, `go-controller/observability-lib` is used. It finds `Sample`
+by the attached `Sample.Metadata` and then gets corresponding db object based on `Sampling_add.ID` and `Sample.UUID`.
+The message is then constructed using db object `external_ids`.
+
+### Full stack architecture
+
+![ovnkube-observ](../images/ovnkube-observ.png)
+
+The diagram shows how all involved components (kernel, OVS, OVN, ovn-kubernetes) are connected.
+
+## Best Practices
+
+TDB
+
+## Future Items
+
+Add more features support, for example, egress IP or load balancing.
+
+## Known Limitations
+
+Current version of `ovnkube-observ` only works in OVN-IC mode, as it requires `nbdb` to be available locally via unix socket.
+In the future non-IC will also be supported with provided `nbdb` address and certificates.
+
+Only default network observability is supported for now, secondary-network observability will be added later.
+
+Sample ID for ACL is stored in conntrack when the new session is established and is never updated until the session is closed.
+That means, some samples may be removed from nbdb, but still be present in the generated samples. It implies:
+- ACL-based sampling only affects newly established connections: if a session was already established before the sampling was enabled,
+the session will not be sampled.
+- If a session is established with enabled sampling, disabling sampling won't affect that session, and it will continue
+generating samples until the session is closed.
+- If the sample was removed from nbdb (e.g. when sampling is disabled for a given connection or when ACL is updated on network policy
+update or delete) generated samples won't be decoded, as required data is not present in nbdb anymore.
+
+Due to OVN limitations, some samples can only be generated on the first packet of a connection.
+This applies to 
+- egress firewall, as it doesn't submit a flow to conntrack.
+- multiple ACLs on the same direction, as only last-tier ACL will be submitted to conntrack. For now this applies to 
+  - ANP + network policy
+  - ANP + BANP 
+  
+  in both cases ANP will have only first-packet sample.
+
+## References
+
+NONE
diff --git a/mkdocs.yml b/mkdocs.yml
index f537dc5c978..f53eac1ae45 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -132,6 +132,7 @@ nav:
   - Observability:
     - Metrics: observability/metrics.md
     - SDN Dashboard: observability/sdn-dashboard.md
+    - OVN observability: observability/ovn-observability.md
   - Enhancement Proposals:
     # - FeatureName: okeps/<filename.md>
     - Template: okeps/okep-4368-template.md

From 5d2310e77f875d42c2c1a3f3ff14514ddfdb1d74 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Wed, 21 Aug 2024 11:34:44 +0200
Subject: [PATCH 10/48] Free disk for dualstack conversion job.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 .github/workflows/test.yml | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 47a2a7fa84f..a236b49fa09 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -617,6 +617,17 @@ jobs:
         echo "GOPATH=$GOPATH" >> $GITHUB_ENV
         echo "$GOPATH/bin" >> $GITHUB_PATH
 
+    - name: Free up disk space
+      run: |
+        sudo rm -rf /usr/local/lib/android/sdk
+        sudo apt-get update
+        sudo eatmydata apt-get purge --auto-remove -y \
+          azure-cli aspnetcore-* dotnet-* ghc-* firefox \
+          google-chrome-stable \
+          llvm-* microsoft-edge-stable mono-* \
+          msbuild mysql-server-core-* php-* php7* \
+          powershell temurin-* zulu-*
+
     - name: Disable ufw
       # For IPv6 and Dualstack, ufw (Uncomplicated Firewall) should be disabled.
       # Not needed for KIND deployments, so just disable all the time.

From 24b4749fdbdf887a8896bcf0d4b57fc2fa83a497 Mon Sep 17 00:00:00 2001
From: Martin Kennelly <mkennell@redhat.com>
Date: Wed, 4 Sep 2024 13:46:09 +0100
Subject: [PATCH 11/48] UDN LGW: ensure masq chain exists before adding rules

Prior to this PR, we may try to insert a rule to jump
to a chain that doesn't exist.

Signed-off-by: Martin Kennelly <mkennell@redhat.com>
---
 go-controller/pkg/node/gateway_iptables.go | 12 ++++++++++++
 go-controller/pkg/node/gateway_localnet.go |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/go-controller/pkg/node/gateway_iptables.go b/go-controller/pkg/node/gateway_iptables.go
index df7a3f0ba5e..39dea4842a2 100644
--- a/go-controller/pkg/node/gateway_iptables.go
+++ b/go-controller/pkg/node/gateway_iptables.go
@@ -79,6 +79,18 @@ func deleteIptRules(rules []nodeipt.Rule) error {
 	return nodeipt.DelRules(rules)
 }
 
+// ensureChain ensures that a chain exists within a table
+func ensureChain(table, chain string) error {
+	for _, proto := range clusterIPTablesProtocols() {
+		ipt, err := util.GetIPTablesHelper(proto)
+		if err != nil {
+			return fmt.Errorf("failed to get IPTables helper to add UDN chain: %v", err)
+		}
+		addChaintoTable(ipt, table, chain)
+	}
+	return nil
+}
+
 func getGatewayInitRules(chain string, proto iptables.Protocol) []nodeipt.Rule {
 	iptRules := []nodeipt.Rule{}
 	if chain == egressservice.Chain {
diff --git a/go-controller/pkg/node/gateway_localnet.go b/go-controller/pkg/node/gateway_localnet.go
index 543cda0be58..87e27bb3884 100644
--- a/go-controller/pkg/node/gateway_localnet.go
+++ b/go-controller/pkg/node/gateway_localnet.go
@@ -26,6 +26,11 @@ func newLocalGateway(nodeName string, hostSubnets []*net.IPNet, gwNextHops []net
 	klog.Info("Creating new local gateway")
 	gw := &gateway{}
 
+	if util.IsNetworkSegmentationSupportEnabled() {
+		if err := ensureChain("nat", iptableUDNMasqueradeChain); err != nil {
+			return nil, fmt.Errorf("failed to ensure chain %s in NAT table: %w", iptableUDNMasqueradeChain, err)
+		}
+	}
 	for _, hostSubnet := range hostSubnets {
 		// local gateway mode uses mp0 as default path for all ingress traffic into OVN
 		var nextHop *net.IPNet

From 4ef14cdb0ddcb028df833c590c66e267c977bc43 Mon Sep 17 00:00:00 2001
From: Jacob Tanenbaum <jtanenba@redhat.com>
Date: Wed, 14 Aug 2024 15:47:24 -0400
Subject: [PATCH 12/48] validate the ipfamily for network attachments

ensure that user defined networks are using ipfamilies that the cluster
supports

Signed-off-by: Jacob Tanenbaum <jtanenba@redhat.com>
---
 .../pkg/clustermanager/pod/allocator_test.go  |  2 +
 .../secondary_network_unit_test.go            | 40 ++++-----
 .../template/net-attach-def-template_test.go  |  4 +
 .../pkg/cni/udn/primary_network_test.go       |  3 +
 go-controller/pkg/node/gateway_udn_test.go    | 18 +++-
 .../ovn/controller/services/lb_config_test.go | 12 ++-
 .../services/services_controller_test.go      | 14 +++-
 .../pkg/ovn/controller/services/utils_test.go |  5 +-
 .../pkg/ovn/topology/topologyfactory_test.go  |  4 +
 go-controller/pkg/util/multi_network.go       | 21 ++++-
 go-controller/pkg/util/multi_network_test.go  | 84 +++++++++++++++++++
 11 files changed, 177 insertions(+), 30 deletions(-)

diff --git a/go-controller/pkg/clustermanager/pod/allocator_test.go b/go-controller/pkg/clustermanager/pod/allocator_test.go
index e244f1af6e3..c41df6f06aa 100644
--- a/go-controller/pkg/clustermanager/pod/allocator_test.go
+++ b/go-controller/pkg/clustermanager/pod/allocator_test.go
@@ -569,6 +569,8 @@ func TestPodAllocator_reconcileForNAD(t *testing.T) {
 
 			config.OVNKubernetesFeature.EnableInterconnect = tt.idAllocation
 
+			// config.IPv4Mode needs to be set so that the ipv4 of the userdefined primary networks can match the running cluster
+			config.IPv4Mode = true
 			netInfo, err := util.NewNetInfo(netConf)
 			if err != nil {
 				t.Fatalf("Invalid netConf")
diff --git a/go-controller/pkg/clustermanager/secondary_network_unit_test.go b/go-controller/pkg/clustermanager/secondary_network_unit_test.go
index 2e2628d56c3..977a03aeb87 100644
--- a/go-controller/pkg/clustermanager/secondary_network_unit_test.go
+++ b/go-controller/pkg/clustermanager/secondary_network_unit_test.go
@@ -114,6 +114,7 @@ var _ = ginkgo.Describe("Cluster Controller Manager", func() {
 			)
 
 			ginkgo.BeforeEach(func() {
+
 				fakeClient = &util.OVNClusterManagerClientset{
 					KubeClient:            fake.NewSimpleClientset(&v1.NodeList{Items: nodes()}),
 					IPAMClaimsClient:      fakeipamclaimclient.NewSimpleClientset(),
@@ -674,24 +675,6 @@ var _ = ginkgo.Describe("Cluster Controller Manager", func() {
 				netInfo    util.NetInfo
 			)
 
-			ginkgo.BeforeEach(func() {
-				var err error
-				netInfo, err = util.NewNetInfo(
-					&ovncnitypes.NetConf{
-						NetConf:  types.NetConf{Name: "blue"},
-						Role:     ovntypes.NetworkRolePrimary,
-						Subnets:  subnets,
-						Topology: ovntypes.Layer2Topology,
-					})
-				gomega.Expect(err).NotTo(gomega.HaveOccurred())
-
-				fakeClient = &util.OVNClusterManagerClientset{
-					KubeClient:            fake.NewSimpleClientset(&v1.NodeList{Items: nodes()}),
-					IPAMClaimsClient:      fakeipamclaimclient.NewSimpleClientset(),
-					NetworkAttchDefClient: fakenadclient.NewSimpleClientset(),
-				}
-			})
-
 			ginkgo.It("Automatically reserves IPs for the GW (.1) and mgmt port (.2)", func() {
 				app.Action = func(ctx *cli.Context) error {
 					gomega.Expect(
@@ -701,6 +684,20 @@ var _ = ginkgo.Describe("Cluster Controller Manager", func() {
 						)).To(gomega.Succeed())
 
 					var err error
+					netInfo, err = util.NewNetInfo(
+						&ovncnitypes.NetConf{
+							NetConf:  types.NetConf{Name: "blue"},
+							Role:     ovntypes.NetworkRolePrimary,
+							Subnets:  subnets,
+							Topology: ovntypes.Layer2Topology,
+						})
+					gomega.Expect(err).NotTo(gomega.HaveOccurred())
+
+					fakeClient = &util.OVNClusterManagerClientset{
+						KubeClient:            fake.NewSimpleClientset(&v1.NodeList{Items: nodes()}),
+						IPAMClaimsClient:      fakeipamclaimclient.NewSimpleClientset(),
+						NetworkAttchDefClient: fakenadclient.NewSimpleClientset(),
+					}
 					f, err = factory.NewClusterManagerWatchFactory(fakeClient)
 					gomega.Expect(err).NotTo(gomega.HaveOccurred())
 					gomega.Expect(f.Start()).NotTo(gomega.HaveOccurred())
@@ -740,7 +737,12 @@ var _ = ginkgo.Describe("Cluster Controller Manager", func() {
 					return nil
 				}
 
-				gomega.Expect(app.Run([]string{app.Name})).To(gomega.Succeed())
+				gomega.Expect(app.Run([]string{
+					app.Name,
+					// define the cluster as dualstack so the user defined primary network matches the ip family
+					"--cluster-subnets=10.128.0.0/14,fd00:10:244::/48",
+					"--k8s-service-cidrs=172.16.1.0/24,fd02::/112",
+				})).To(gomega.Succeed())
 			})
 
 		})
diff --git a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go
index bad077d8df1..31b9d0a0bc9 100644
--- a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go
+++ b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go
@@ -10,6 +10,7 @@ import (
 
 	netv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
 
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	udnv1 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/userdefinednetwork/v1"
 )
 
@@ -211,6 +212,9 @@ var _ = Describe("NetAttachDefTemplate", func() {
 				Spec: netv1.NetworkAttachmentDefinitionSpec{Config: expectedNadNetConf},
 			}
 
+			// must be defined so the primary user defined network can match the ip families of the underlying cluster
+			config.IPv4Mode = true
+			config.IPv6Mode = true
 			nad, err := RenderNetAttachDefManifest(testUdn)
 			Expect(err).NotTo(HaveOccurred())
 			Expect(nad.TypeMeta).To(Equal(expectedNAD.TypeMeta))
diff --git a/go-controller/pkg/cni/udn/primary_network_test.go b/go-controller/pkg/cni/udn/primary_network_test.go
index 71bd146a0f2..21e400655c1 100644
--- a/go-controller/pkg/cni/udn/primary_network_test.go
+++ b/go-controller/pkg/cni/udn/primary_network_test.go
@@ -177,6 +177,9 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.description, func(t *testing.T) {
 			g := NewWithT(t)
+			// needs to be set so the primary user defined networks can use ipfamilies supported by the underlying cluster
+			config.IPv4Mode = true
+			config.IPv6Mode = true
 			nadLister := v1nadmocks.NetworkAttachmentDefinitionLister{}
 			nadNamespaceLister := v1nadmocks.NetworkAttachmentDefinitionNamespaceLister{}
 			nadLister.On("NetworkAttachmentDefinitions", tt.namespace).Return(&nadNamespaceLister)
diff --git a/go-controller/pkg/node/gateway_udn_test.go b/go-controller/pkg/node/gateway_udn_test.go
index 058590ef4f3..29d4cc98149 100644
--- a/go-controller/pkg/node/gateway_udn_test.go
+++ b/go-controller/pkg/node/gateway_udn_test.go
@@ -311,6 +311,9 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
 		}
 		nad := ovntest.GenerateNAD(netName, "rednad", "greenamespace",
 			types.Layer3Topology, "100.128.0.0/16/24,ae70::66/60", types.NetworkRolePrimary)
+		// must be defined so that the primary user defined network can match the ip families of the underlying cluster
+		config.IPv4Mode = true
+		config.IPv6Mode = true
 		netInfo, err := util.ParseNADInfo(nad)
 		Expect(err).NotTo(HaveOccurred())
 		udnGateway, err := NewUserDefinedNetworkGateway(netInfo, 3, node, factoryMock.NodeCoreInformer().Lister(),
@@ -380,6 +383,9 @@ var _ = Describe("UserDefinedNetworkGateway", func() {
 		}
 		nad := ovntest.GenerateNAD(netName, "rednad", "greenamespace",
 			types.Layer2Topology, "100.128.0.0/16,ae70::66/60", types.NetworkRolePrimary)
+		// must be defined so that the primary user defined network can match the ip families of the underlying cluster
+		config.IPv4Mode = true
+		config.IPv6Mode = true
 		netInfo, err := util.ParseNADInfo(nad)
 		Expect(err).NotTo(HaveOccurred())
 		udnGateway, err := NewUserDefinedNetworkGateway(netInfo, 3, node, factoryMock.NodeCoreInformer().Lister(),
@@ -920,8 +926,18 @@ func TestConstructUDNVRFIPRules(t *testing.T) {
 			g := gomega.NewWithT(t)
 			config.IPv4Mode = test.v4mode
 			config.IPv6Mode = test.v6mode
+			cidr := ""
+			if config.IPv4Mode {
+				cidr = "100.128.0.0/16/24"
+
+			}
+			if config.IPv4Mode && config.IPv6Mode {
+				cidr += ",ae70::66/60"
+			} else if config.IPv6Mode {
+				cidr = "ae70::66/60"
+			}
 			nad := ovntest.GenerateNAD("bluenet", "rednad", "greenamespace",
-				types.Layer3Topology, "100.128.0.0/16/24,ae70::66/60", types.NetworkRolePrimary)
+				types.Layer3Topology, cidr, types.NetworkRolePrimary)
 			netInfo, err := util.ParseNADInfo(nad)
 			g.Expect(err).NotTo(HaveOccurred())
 			udnGateway, err := NewUserDefinedNetworkGateway(netInfo, 3, nil, nil, nil, nil, nil, &gateway{})
diff --git a/go-controller/pkg/ovn/controller/services/lb_config_test.go b/go-controller/pkg/ovn/controller/services/lb_config_test.go
index 4f5e5211b69..a99a7eb5181 100644
--- a/go-controller/pkg/ovn/controller/services/lb_config_test.go
+++ b/go-controller/pkg/ovn/controller/services/lb_config_test.go
@@ -1200,8 +1200,10 @@ func Test_buildClusterLBs(t *testing.T) {
 	namespace := "testns"
 
 	oldGwMode := globalconfig.Gateway.Mode
+	oldIPv4Mode := globalconfig.IPv4Mode
 	defer func() {
 		globalconfig.Gateway.Mode = oldGwMode
+		globalconfig.IPv4Mode = oldIPv4Mode
 	}()
 	globalconfig.Gateway.Mode = globalconfig.GatewayModeShared
 
@@ -1217,7 +1219,9 @@ func Test_buildClusterLBs(t *testing.T) {
 	defaultGroups := []string{types.ClusterLBGroupName}
 	defaultOpts := LBOpts{Reject: true}
 
-	UDNNetInfo := getSampleUDNNetInfo(namespace)
+	globalconfig.IPv4Mode = true
+	UDNNetInfo, err := getSampleUDNNetInfo(namespace)
+	assert.Equal(t, err, nil)
 	UDNGroups := []string{UDNNetInfo.GetNetworkScopedLoadBalancerGroupName(types.ClusterLBGroupName)}
 
 	tc := []struct {
@@ -1455,7 +1459,9 @@ func Test_buildPerNodeLBs(t *testing.T) {
 	oldClusterSubnet := globalconfig.Default.ClusterSubnets
 	oldGwMode := globalconfig.Gateway.Mode
 	oldServiceCIDRs := globalconfig.Kubernetes.ServiceCIDRs
+	oldIPv4Mode := globalconfig.IPv4Mode
 	defer func() {
+		globalconfig.IPv4Mode = oldIPv4Mode
 		globalconfig.Gateway.Mode = oldGwMode
 		globalconfig.Default.ClusterSubnets = oldClusterSubnet
 		globalconfig.Kubernetes.ServiceCIDRs = oldServiceCIDRs
@@ -1468,11 +1474,13 @@ func Test_buildPerNodeLBs(t *testing.T) {
 	_, svcCIDRv6, _ := net.ParseCIDR("fd92::0/80")
 
 	globalconfig.Kubernetes.ServiceCIDRs = []*net.IPNet{svcCIDRv4}
+	globalconfig.IPv4Mode = true
 
 	name := "foo"
 	namespace := "testns"
 
-	UDNNetInfo := getSampleUDNNetInfo(namespace)
+	UDNNetInfo, err := getSampleUDNNetInfo(namespace)
+	assert.Equal(t, nil, err)
 
 	defaultService := &v1.Service{
 		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
diff --git a/go-controller/pkg/ovn/controller/services/services_controller_test.go b/go-controller/pkg/ovn/controller/services/services_controller_test.go
index a816a0c2718..697affbbc31 100644
--- a/go-controller/pkg/ovn/controller/services/services_controller_test.go
+++ b/go-controller/pkg/ovn/controller/services/services_controller_test.go
@@ -134,8 +134,9 @@ func (c *serviceController) close() {
 	c.libovsdbCleanup.Cleanup()
 }
 
-func getSampleUDNNetInfo(namespace string) util.NetInfo {
-	netInfo, _ := util.NewNetInfo(&ovncnitypes.NetConf{
+func getSampleUDNNetInfo(namespace string) (util.NetInfo, error) {
+	// requires that config.IPv4Mode = true
+	netInfo, err := util.NewNetInfo(&ovncnitypes.NetConf{
 		Topology:   "layer3",
 		NADName:    fmt.Sprintf("%s/nad1", namespace),
 		MTU:        1400,
@@ -144,7 +145,7 @@ func getSampleUDNNetInfo(namespace string) util.NetInfo {
 		NetConf:    cnitypes.NetConf{Name: "tenant-red", Type: "ovn-k8s-cni-overlay"},
 		JoinSubnet: "100.66.0.0/16",
 	})
-	return netInfo
+	return netInfo, err
 }
 
 func addSampleNAD(client *util.OVNKubeControllerClientset, namespace, networkName string) error {
@@ -198,22 +199,27 @@ func TestSyncServices(t *testing.T) {
 		initialLrGroups = []string{types.ClusterLBGroupName, types.ClusterRouterLBGroupName}
 
 		udnNetworkName = "tenant-red"
-		udnNetInfo     = getSampleUDNNetInfo(ns)
 	)
 	// setup global config
 	oldGateway := globalconfig.Gateway.Mode
 	oldClusterSubnet := globalconfig.Default.ClusterSubnets
 	globalconfig.Kubernetes.OVNEmptyLbEvents = true
 	globalconfig.IPv4Mode = true
+	globalconfig.IPv6Mode = true
 	defer func() {
 		globalconfig.Kubernetes.OVNEmptyLbEvents = false
 		globalconfig.IPv4Mode = false
+		globalconfig.IPv6Mode = false
 		globalconfig.Gateway.Mode = oldGateway
 		globalconfig.Default.ClusterSubnets = oldClusterSubnet
 	}()
 	_, cidr4, _ := net.ParseCIDR("10.128.0.0/16")
 	_, cidr6, _ := net.ParseCIDR("fe00:0:0:0:5555::0/64")
 	globalconfig.Default.ClusterSubnets = []globalconfig.CIDRNetworkEntry{{cidr4, 26}, {cidr6, 26}}
+	udnNetInfo, err := getSampleUDNNetInfo(ns)
+	if err != nil {
+		t.Fatalf("Error creating UDNNetInfo: %v", err)
+	}
 
 	// define node configs
 	nodeAInfo := getNodeInfo(nodeA, []string{nodeAHostAddress}, nil)
diff --git a/go-controller/pkg/ovn/controller/services/utils_test.go b/go-controller/pkg/ovn/controller/services/utils_test.go
index 6b302b0c8ee..3b5def1a4c5 100644
--- a/go-controller/pkg/ovn/controller/services/utils_test.go
+++ b/go-controller/pkg/ovn/controller/services/utils_test.go
@@ -3,6 +3,7 @@ package services
 import (
 	"testing"
 
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
 
@@ -16,7 +17,9 @@ func TestExternalIDsForLoadBalancer(t *testing.T) {
 	name := "svc-ab23"
 	namespace := "ns"
 	defaultNetInfo := util.DefaultNetInfo{}
-	UDNNetInfo := getSampleUDNNetInfo(namespace)
+	config.IPv4Mode = true
+	UDNNetInfo, err := getSampleUDNNetInfo(namespace)
+	assert.Equal(t, err, nil)
 	assert.Equal(t,
 		map[string]string{
 			types.LoadBalancerKindExternalID:  "Service",
diff --git a/go-controller/pkg/ovn/topology/topologyfactory_test.go b/go-controller/pkg/ovn/topology/topologyfactory_test.go
index 5105aa6c703..e21d17587dd 100644
--- a/go-controller/pkg/ovn/topology/topologyfactory_test.go
+++ b/go-controller/pkg/ovn/topology/topologyfactory_test.go
@@ -11,6 +11,7 @@ import (
 	libovsdbclient "github.com/ovn-org/libovsdb/client"
 
 	ovncnitypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/cni/types"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
 	libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb"
 	ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
@@ -33,6 +34,9 @@ var _ = Describe("Topology factory", func() {
 
 	When("the original OVN DBs are empty", func() {
 		BeforeEach(func() {
+			// required so that NewNetInfo can properly determine the IP families the cluster supports
+			config.IPv4Mode = true
+			config.IPv6Mode = true
 			initialNBDB := []libovsdbtest.TestData{}
 			initialSBDB := []libovsdbtest.TestData{}
 			dbSetup := libovsdbtest.TestSetup{
diff --git a/go-controller/pkg/util/multi_network.go b/go-controller/pkg/util/multi_network.go
index 39095f9ba88..e4710fbb26d 100644
--- a/go-controller/pkg/util/multi_network.go
+++ b/go-controller/pkg/util/multi_network.go
@@ -700,17 +700,32 @@ func NewNetInfo(netconf *ovncnitypes.NetConf) (NetInfo, error) {
 	if netconf.Name == types.DefaultNetworkName {
 		return &DefaultNetInfo{}, nil
 	}
+	var ni NetInfo
+	var err error
 	switch netconf.Topology {
 	case types.Layer3Topology:
-		return newLayer3NetConfInfo(netconf)
+		ni, err = newLayer3NetConfInfo(netconf)
 	case types.Layer2Topology:
-		return newLayer2NetConfInfo(netconf)
+		ni, err = newLayer2NetConfInfo(netconf)
 	case types.LocalnetTopology:
-		return newLocalnetNetConfInfo(netconf)
+		ni, err = newLocalnetNetConfInfo(netconf)
 	default:
 		// other topology NAD can be supported later
 		return nil, fmt.Errorf("topology %s not supported", netconf.Topology)
 	}
+	if err != nil {
+		return nil, err
+	}
+	if ni.IsPrimaryNetwork() && ni.IsSecondary() {
+		ipv4Mode, ipv6Mode := ni.IPMode()
+		if ipv4Mode && !config.IPv4Mode {
+			return nil, fmt.Errorf("network %s is attempting to use ipv4 subnets but the cluster does not support ipv4", ni.GetNetworkName())
+		}
+		if ipv6Mode && !config.IPv6Mode {
+			return nil, fmt.Errorf("network %s is attempting to use ipv6 subnets but the cluster does not support ipv6", ni.GetNetworkName())
+		}
+	}
+	return ni, nil
 }
 
 // ParseNADInfo parses config in NAD spec and return a NetAttachDefInfo object for secondary networks
diff --git a/go-controller/pkg/util/multi_network_test.go b/go-controller/pkg/util/multi_network_test.go
index c6c0fdd62ef..c5d7dca2fdf 100644
--- a/go-controller/pkg/util/multi_network_test.go
+++ b/go-controller/pkg/util/multi_network_test.go
@@ -964,6 +964,90 @@ func TestSubnetOverlapCheck(t *testing.T) {
 	}
 }
 
+func TestNewNetInfo(t *testing.T) {
+	type testConfig struct {
+		desc          string
+		subnets       string
+		ipv4Cluster   bool
+		ipv6Cluster   bool
+		expectedError error
+	}
+
+	tests := []testConfig{
+		{
+			desc:        "ipv4 primary network in ipv4 cluster",
+			subnets:     "192.168.200.0/16",
+			ipv4Cluster: true,
+		},
+		{
+			desc:          "ipv4 primary network in ipv6 cluster",
+			subnets:       "192.168.200.0/16",
+			ipv6Cluster:   true,
+			expectedError: fmt.Errorf("network l3-network is attempting to use ipv4 subnets but the cluster does not support ipv4"),
+		},
+		{
+			desc:        "ipv4 primary network in dualstack cluster",
+			subnets:     "192.168.200.0/16",
+			ipv4Cluster: true,
+			ipv6Cluster: true,
+		},
+		{
+			desc:          "ipv6 primary network in ipv4 cluster",
+			subnets:       "fda6::/48",
+			ipv4Cluster:   true,
+			expectedError: fmt.Errorf("network l3-network is attempting to use ipv6 subnets but the cluster does not support ipv6"),
+		},
+		{
+			desc:        "ipv6 primary network in ipv6 cluster",
+			subnets:     "fda6::/48",
+			ipv6Cluster: true,
+		},
+		{
+			desc:        "ipv6 primary network in dualstack cluster",
+			subnets:     "fda6::/48",
+			ipv4Cluster: true,
+			ipv6Cluster: true,
+		},
+		{
+			desc:          "dualstack primary network in ipv4 cluster",
+			subnets:       "192.168.200.0/16, fda6::/48",
+			ipv4Cluster:   true,
+			expectedError: fmt.Errorf("network l3-network is attempting to use ipv6 subnets but the cluster does not support ipv6"),
+		},
+		{
+			desc:          "dualstack primary network in ipv6 cluster",
+			subnets:       "192.168.200.0/16, fda6::/48",
+			ipv6Cluster:   true,
+			expectedError: fmt.Errorf("network l3-network is attempting to use ipv4 subnets but the cluster does not support ipv4"),
+		},
+		{
+			desc:        "dualstack primary network in dualstack cluster",
+			subnets:     "192.168.200.0/16, fda6::/48",
+			ipv4Cluster: true,
+			ipv6Cluster: true,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			inputNetConf := &ovncnitypes.NetConf{
+				NetConf:  cnitypes.NetConf{Name: "l3-network"},
+				Topology: ovntypes.Layer3Topology,
+				Role:     ovntypes.NetworkRolePrimary,
+				Subnets:  test.subnets,
+			}
+			config.IPv4Mode = test.ipv4Cluster
+			config.IPv6Mode = test.ipv6Cluster
+			g := gomega.NewWithT(t)
+			_, err := NewNetInfo(inputNetConf)
+			if test.expectedError == nil {
+				g.Expect(err).To(gomega.BeNil())
+			} else {
+				g.Expect(err).To(gomega.MatchError(test.expectedError.Error()))
+			}
+		})
+	}
+}
+
 func applyNADDefaults(nad *nadv1.NetworkAttachmentDefinition) *nadv1.NetworkAttachmentDefinition {
 	const (
 		name      = "nad1"

From 4d471bc881fe6dc282cdbd3ffd1e8b22bdf3f5eb Mon Sep 17 00:00:00 2001
From: Jacob Tanenbaum <jtanenba@redhat.com>
Date: Thu, 15 Aug 2024 13:52:59 -0400
Subject: [PATCH 13/48] udn/nad e2e testing ip family aware

currently the udn/nad primary network e2e testing does nothing to check
to state of the cluster before creating the network. This makes it
possible to test primary networks with ip families that the underlying
cluster does not support which is not possible.

This commit ensures that e2e testing will only create primary networks
that conform to the cluster being tested

Signed-off-by: Jacob Tanenbaum <jtanenba@redhat.com>
---
 test/e2e/multihoming.go                       |  1 +
 test/e2e/multihoming_utils.go                 | 16 +++++
 test/e2e/network_segmentation.go              | 71 +++++++++++++------
 ...work_segmentation_endpointslices_mirror.go | 20 +++---
 4 files changed, 78 insertions(+), 30 deletions(-)

diff --git a/test/e2e/multihoming.go b/test/e2e/multihoming.go
index 112b816d120..15fc2be76d1 100644
--- a/test/e2e/multihoming.go
+++ b/test/e2e/multihoming.go
@@ -644,6 +644,7 @@ var _ = Describe("Multi Homing", func() {
 		Context("localnet OVN-K secondary network", func() {
 			const (
 				clientPodName          = "client-pod"
+				nodeHostnameKey        = "kubernetes.io/hostname"
 				servicePort            = 9000
 				dockerNetworkName      = "underlay"
 				underlayServiceIP      = "60.128.0.1"
diff --git a/test/e2e/multihoming_utils.go b/test/e2e/multihoming_utils.go
index aff5c0eddbb..874fcc4eb43 100644
--- a/test/e2e/multihoming_utils.go
+++ b/test/e2e/multihoming_utils.go
@@ -25,6 +25,22 @@ func netCIDR(netCIDR string, netPrefixLengthPerNode int) string {
 	return fmt.Sprintf("%s/%d", netCIDR, netPrefixLengthPerNode)
 }
 
+// takes ipv4 and ipv6 cidrs and returns the correct type for the cluster under test
+func correctCIDRFamily(ipv4CIDR, ipv6CIDR string) string {
+	// dual stack cluster
+	if isIPv6Supported() && isIPv4Supported() {
+		return strings.Join([]string{ipv4CIDR, ipv6CIDR}, ",")
+	}
+	// is an ipv6 only cluster
+	if isIPv6Supported() {
+		return ipv6CIDR
+	}
+
+	//ipv4 only cluster
+	return ipv4CIDR
+
+}
+
 func getNetCIDRSubnet(netCIDR string) (string, error) {
 	subStrings := strings.Split(netCIDR, "/")
 	if len(subStrings) == 3 {
diff --git a/test/e2e/network_segmentation.go b/test/e2e/network_segmentation.go
index fdea4133829..b2aeebfb1ac 100644
--- a/test/e2e/network_segmentation.go
+++ b/test/e2e/network_segmentation.go
@@ -110,11 +110,11 @@ var _ = Describe("Network Segmentation", func() {
 						}
 					},
 					Entry(
-						"two pods connected over a L2 dualstack primary UDN",
+						"two pods connected over a L2 primary UDN",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer2",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						*podConfig(
@@ -130,11 +130,11 @@ var _ = Describe("Network Segmentation", func() {
 						),
 					),
 					Entry(
-						"two pods connected over a L3 dualstack primary UDN",
+						"two pods connected over a L3 primary UDN",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer3",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						*podConfig(
@@ -700,7 +700,6 @@ var _ = Describe("Network Segmentation", func() {
 				deleteClusterExternalContainer(externalContainerName)
 			})
 		})
-
 		DescribeTable(
 			"can be accessed to from the pods running in the Kubernetes cluster",
 			func(netConfigParams networkAttachmentConfigParams, clientPodConfig podConfiguration) {
@@ -753,32 +752,25 @@ var _ = Describe("Network Segmentation", func() {
 				podAnno, err := unmarshalPodAnnotation(updatedPod.Annotations, f.Namespace.Name+"/"+userDefinedNetworkName)
 				Expect(err).NotTo(HaveOccurred())
 				framework.Logf("Client pod's annotation for network %s is %v", userDefinedNetworkName, podAnno)
-				Expect(podAnno.Routes).To(HaveLen(expectedNumberOfRoutes(netConfig)))
 
-				By("asserting the *client* pod can contact the server's v4 IP located outside the cluster")
-				Eventually(func() error {
-					return connectToServer(clientPodConfig, externalIpv4, port)
-				}, 2*time.Minute, 6*time.Second).Should(Succeed())
+				Expect(podAnno.Routes).To(HaveLen(expectedNumberOfRoutes(netConfig)))
 
-				By("asserting the *client* pod can contact the server's v6 IP located outside the cluster")
-				Eventually(func() error {
-					return connectToServer(clientPodConfig, externalIpv6, port)
-				}, 2*time.Minute, 6*time.Second).Should(Succeed())
+				assertClientExternalConnectivity(clientPodConfig, externalIpv4, externalIpv6, port)
 			},
 			Entry("by one pod with dualstack addresses over a layer2 network",
 				networkAttachmentConfigParams{
 					name:     userDefinedNetworkName,
 					topology: "layer2",
-					cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+					cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 					role:     "primary",
 				},
 				*podConfig("client-pod"),
 			),
-			Entry("by one pod with dualstack addresses over a layer3 network",
+			Entry("by one pod over a layer3 network",
 				networkAttachmentConfigParams{
 					name:     userDefinedNetworkName,
 					topology: "layer3",
-					cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+					cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 					role:     "primary",
 				},
 				*podConfig("client-pod"),
@@ -935,10 +927,25 @@ spec:
   topology: Layer3
   layer3:
     role: Primary
-    subnets:
+    subnets: ` + generateCIDRforUDN()
+}
+
+func generateCIDRforUDN() string {
+	cidr := `
+    - cidr: 10.20.100.0/16
+`
+	if isIPv6Supported() && isIPv4Supported() {
+		cidr = `
     - cidr: 10.20.100.0/16
     - cidr: 2014:100:200::0/60
 `
+	} else if isIPv6Supported() {
+		cidr = `
+    - cidr: 2014:100:200::0/60
+`
+	}
+	return cidr
+
 }
 
 type podOption func(*podConfiguration)
@@ -1052,13 +1059,37 @@ func connectToServerViaDefaultNetwork(clientPodConfig podConfiguration, serverIP
 	return err
 }
 
+// assertClientExternalConnectivity checks if the client can connect to an externally created IP outside the cluster
+func assertClientExternalConnectivity(clientPodConfig podConfiguration, externalIpv4 string, externalIpv6 string, port int) {
+	if isIPv4Supported() {
+		By("asserting the *client* pod can contact the server's v4 IP located outside the cluster")
+		Eventually(func() error {
+			return connectToServer(clientPodConfig, externalIpv4, port)
+		}, 2*time.Minute, 6*time.Second).Should(Succeed())
+	}
+
+	if isIPv6Supported() {
+		By("asserting the *client* pod can contact the server's v6 IP located outside the cluster")
+		Eventually(func() error {
+			return connectToServer(clientPodConfig, externalIpv6, port)
+		}, 2*time.Minute, 6*time.Second).Should(Succeed())
+	}
+}
+
 func runExternalContainerCmd() []string {
 	return []string{"--network", "kind"}
 }
 
 func expectedNumberOfRoutes(netConfig networkAttachmentConfig) int {
 	if netConfig.topology == "layer2" {
-		return 4 // 2 routes per family
+		if isIPv6Supported() && isIPv4Supported() {
+			return 4 // 2 routes per family
+		} else {
+			return 2 //one family supported
+		}
+	}
+	if isIPv6Supported() && isIPv4Supported() {
+		return 6 // 3 v4 routes + 3 v6 routes for UDN
 	}
-	return 6 // 3 v4 routes + 3 v6 routes for UDN
+	return 3 //only one family, each has 3 routes
 }
diff --git a/test/e2e/network_segmentation_endpointslices_mirror.go b/test/e2e/network_segmentation_endpointslices_mirror.go
index 371e112dfc4..2985d994150 100644
--- a/test/e2e/network_segmentation_endpointslices_mirror.go
+++ b/test/e2e/network_segmentation_endpointslices_mirror.go
@@ -111,41 +111,41 @@ var _ = Describe("Network Segmentation EndpointSlices mirroring", func() {
 
 					},
 					Entry(
-						"L2 dualstack primary UDN, cluster-networked pods",
+						"L2 primary UDN, cluster-networked pods",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer2",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						false,
 					),
 					Entry(
-						"L3 dualstack primary UDN, cluster-networked pods",
+						"L3 primary UDN, cluster-networked pods",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer3",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						false,
 					),
 					Entry(
-						"L2 dualstack primary UDN, host-networked pods",
+						"L2 primary UDN, host-networked pods",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer2",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						true,
 					),
 					Entry(
-						"L3 dualstack primary UDN, host-networked pods",
+						"L3 primary UDN, host-networked pods",
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer3",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "primary",
 						},
 						true,
@@ -214,7 +214,7 @@ var _ = Describe("Network Segmentation EndpointSlices mirroring", func() {
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer2",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "secondary",
 						},
 					),
@@ -223,7 +223,7 @@ var _ = Describe("Network Segmentation EndpointSlices mirroring", func() {
 						networkAttachmentConfigParams{
 							name:     nadName,
 							topology: "layer3",
-							cidr:     fmt.Sprintf("%s,%s", userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
 							role:     "secondary",
 						},
 					),

From c275e72bec40fead2a615c6617f6804b4f596bbe Mon Sep 17 00:00:00 2001
From: Enrique Llorente <ellorent@redhat.com>
Date: Fri, 6 Sep 2024 17:11:39 +0200
Subject: [PATCH 14/48] kubevirt, e2e: Use e2enode to label/unlabel

Signed-off-by: Enrique Llorente <ellorent@redhat.com>
---
 test/e2e/kubevirt.go | 27 ++++++---------------------
 1 file changed, 6 insertions(+), 21 deletions(-)

diff --git a/test/e2e/kubevirt.go b/test/e2e/kubevirt.go
index 0619d65fcbe..470e236b932 100644
--- a/test/e2e/kubevirt.go
+++ b/test/e2e/kubevirt.go
@@ -30,6 +30,7 @@ import (
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/util/retry"
+	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
 	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 	utilnet "k8s.io/utils/net"
 	"k8s.io/utils/pointer"
@@ -86,23 +87,7 @@ var _ = Describe("Kubevirt Virtual Machines", func() {
 		clientSet          kubernetes.Interface
 		// Systemd resolvd prevent resolving kube api service by fqdn, so
 		// we replace it here with NetworkManager
-		labelNode = func(nodeName, label string) error {
-			patch := fmt.Sprintf(`{"metadata": {"labels": {"%s": ""}}}`, label)
-			_, err := fr.ClientSet.CoreV1().Nodes().Patch(context.Background(), nodeName, types.MergePatchType, []byte(patch), metav1.PatchOptions{})
-			if err != nil {
-				return err
-			}
-			return nil
-		}
 
-		unlabelNode = func(nodeName, label string) error {
-			patch := fmt.Sprintf(`[{"op": "remove", "path": "/metadata/labels/%s"}]`, label)
-			_, err := clientSet.CoreV1().Nodes().Patch(context.Background(), nodeName, types.JSONPatchType, []byte(patch), metav1.PatchOptions{})
-			if err != nil {
-				return err
-			}
-			return nil
-		}
 		isDualStack = func() bool {
 			GinkgoHelper()
 			nodeList, err := fr.ClientSet.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
@@ -878,16 +863,16 @@ passwd:
 				by(vm.Name, "Live migrate for the second time to a node not owning the subnet")
 				// Remove the node selector label from original node to force
 				// live migration to a different one.
-				Expect(unlabelNode(originalNode, namespace)).To(Succeed())
+				e2enode.RemoveLabelOffNode(fr.ClientSet, originalNode, namespace)
 				liveMigrateAndCheck(vm.Name, td.mode, endpoints, "after live migration for the second time to node not owning subnet")
 
 				by(vm.Name, "Live migrate for the third time to the node owning the subnet")
 				// Patch back the original node with the label and remove it
 				// from the rest of nodes to force live migration target to it.
-				Expect(labelNode(originalNode, namespace)).To(Succeed())
+				e2enode.AddOrUpdateLabelOnNode(fr.ClientSet, originalNode, namespace, "")
 				for _, selectedNode := range selectedNodes {
 					if selectedNode.Name != originalNode {
-						Expect(unlabelNode(selectedNode.Name, namespace)).To(Succeed())
+						e2enode.RemoveLabelOffNode(fr.ClientSet, selectedNode.Name, namespace)
 					}
 				}
 				liveMigrateAndCheck(vm.Name, td.mode, endpoints, "after live migration to node owning the subnet")
@@ -997,7 +982,7 @@ passwd:
 			// configure VM nodeSelector with it and live migration will take only
 			// them into consideration
 			for _, node := range selectedNodes {
-				Expect(labelNode(node.Name, namespace)).To(Succeed())
+				e2enode.AddOrUpdateLabelOnNode(fr.ClientSet, node.Name, namespace, "")
 			}
 
 			prepareHTTPServerPods(map[string]string{}, checkPodHasIPAtStatus)
@@ -1006,7 +991,7 @@ passwd:
 
 		AfterEach(func() {
 			for _, node := range selectedNodes {
-				unlabelNode(node.Name, namespace)
+				e2enode.RemoveLabelOffNode(fr.ClientSet, node.Name, namespace)
 			}
 		})
 

From d57e8c25f108ce49b2651314d608981c3eb5734c Mon Sep 17 00:00:00 2001
From: Jacob Tanenbaum <jtanenba@redhat.com>
Date: Thu, 22 Aug 2024 11:49:23 -0400
Subject: [PATCH 15/48] add testing of CRDs to the network segmentation tests

adding testing using User Defined Network objects to pod2Egress testing
and "isolates overlapping CIDRs" tests

Signed-off-by: Jacob Tanenbaum <jtanenba@redhat.com>
---
 test/e2e/network_segmentation.go | 482 ++++++++++++++++---------------
 test/e2e/util.go                 |   1 -
 2 files changed, 242 insertions(+), 241 deletions(-)

diff --git a/test/e2e/network_segmentation.go b/test/e2e/network_segmentation.go
index b2aeebfb1ac..8bcfba9d783 100644
--- a/test/e2e/network_segmentation.go
+++ b/test/e2e/network_segmentation.go
@@ -172,6 +172,7 @@ var _ = Describe("Network Segmentation", func() {
 							},
 						}, metav1.CreateOptions{})
 						Expect(err).NotTo(HaveOccurred())
+						// required so the namespaces get cleaned up
 						defer func() {
 							Expect(cs.CoreV1().Namespaces().Delete(context.Background(), defaultNetNamespace, metav1.DeleteOptions{})).To(Succeed())
 						}()
@@ -363,192 +364,182 @@ var _ = Describe("Network Segmentation", func() {
 						),
 					),
 				)
-			},
-			Entry("NetworkAttachmentDefinitions", func(c networkAttachmentConfigParams) error {
-				netConfig := newNetworkAttachmentConfig(c)
-				nad := generateNAD(netConfig)
-				_, err := nadClient.NetworkAttachmentDefinitions(f.Namespace.Name).Create(context.Background(), nad, metav1.CreateOptions{})
-				return err
-			}),
-			Entry("UserDefinedNetwork", func(c networkAttachmentConfigParams) error {
-				udnManifest := generateUserDefinedNetworkManifest(&c)
-				cleanup, err := createManifest(f.Namespace.Name, udnManifest)
-				DeferCleanup(cleanup)
-				Expect(waitForUserDefinedNetworkReady(f.Namespace.Name, c.name, 5*time.Second)).To(Succeed())
-				return err
-			}),
-		)
-		DescribeTable(
-			"isolates overlapping CIDRs",
-			func(
-				topology string,
-				numberOfPods int,
-				userDefinedSubnet string,
+				DescribeTable(
+					"isolates overlapping CIDRs",
+					func(
+						topology string,
+						numberOfPods int,
+						userDefinedv4Subnet string,
+						userDefinedv6Subnet string,
 
-			) {
+					) {
 
-				nadClient, err := nadclient.NewForConfig(f.ClientConfig())
-				Expect(err).NotTo(HaveOccurred())
+						red := "red"
+						blue := "blue"
 
-				red := "red"
-				blue := "blue"
+						namespaceRed := f.Namespace.Name + "-" + red
+						namespaceBlue := f.Namespace.Name + "-" + blue
 
-				namespaceRed := f.Namespace.Name + "-" + red
-				namespaceBlue := f.Namespace.Name + "-" + blue
+						netConfig := networkAttachmentConfigParams{
 
-				nad := networkAttachmentConfigParams{
-					topology: topology,
-					cidr:     fmt.Sprintf("%s,%s", userDefinedSubnet, userDefinedNetworkIPv6Subnet),
-					role:     "primary",
-				}
-				for _, namespace := range []string{namespaceRed, namespaceBlue} {
-					By("Creating namespace " + namespace)
-					_, err = cs.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{
-						ObjectMeta: metav1.ObjectMeta{
-							Name: namespace,
-						},
-					}, metav1.CreateOptions{})
-					Expect(err).NotTo(HaveOccurred())
-					defer func() {
-						Expect(cs.CoreV1().Namespaces().Delete(
-							context.Background(),
-							namespace,
-							metav1.DeleteOptions{},
-						)).To(Succeed())
-					}()
-				}
-				networkNamespaceMap := map[string]string{namespaceRed: red, namespaceBlue: blue}
-				for namespace, network := range networkNamespaceMap {
-					By("creating the attachment configuration for network " + network + " in namespace " + namespace)
-					netConfig := newNetworkAttachmentConfig(nad)
-					netConfig.namespace = namespace
-					netConfig.name = network
-
-					_, err = nadClient.NetworkAttachmentDefinitions(namespace).Create(
-						context.Background(),
-						generateNAD(netConfig),
-						metav1.CreateOptions{},
-					)
-					Expect(err).NotTo(HaveOccurred())
-				}
-				pods := []*v1.Pod{}
-				redIPs := []string{}
-				blueIPs := []string{}
-				for namespace, network := range networkNamespaceMap {
-					for i := range numberOfPods {
-						podConfig := *podConfig(
-							fmt.Sprintf("%s-pod-%d", network, i),
-							withCommand(func() []string {
-								return httpServerContainerCmd(port)
-							}),
-						)
-						podConfig.namespace = namespace
-						//ensure testing accross nodes
-						if i%2 == 0 {
-							podConfig.nodeSelector = map[string]string{nodeHostnameKey: workerOneNodeName}
+							topology: topology,
+							cidr:     correctCIDRFamily(userDefinedv4Subnet, userDefinedv6Subnet),
+							role:     "primary",
+						}
+						for _, namespace := range []string{namespaceRed, namespaceBlue} {
+							By("Creating namespace " + namespace)
+							_, err := cs.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{
+								ObjectMeta: metav1.ObjectMeta{
+									Name: namespace,
+								},
+							}, metav1.CreateOptions{})
+							Expect(err).NotTo(HaveOccurred())
+							defer func() {
+								Expect(cs.CoreV1().Namespaces().Delete(context.Background(), namespace, metav1.DeleteOptions{})).To(Succeed())
+							}()
+						}
+						networkNamespaceMap := map[string]string{namespaceRed: red, namespaceBlue: blue}
+						for namespace, network := range networkNamespaceMap {
+							By("creating the network " + network + " in namespace " + namespace)
+							netConfig.namespace = namespace
+							netConfig.name = network
+							Expect(createNetworkFn(netConfig)).To(Succeed())
+						}
+						pods := []*v1.Pod{}
+						redIPs := []string{}
+						blueIPs := []string{}
+						for namespace, network := range networkNamespaceMap {
+							for i := range numberOfPods {
+								podConfig := *podConfig(
+									fmt.Sprintf("%s-pod-%d", network, i),
+									withCommand(func() []string {
+										return httpServerContainerCmd(port)
+									}),
+								)
+								podConfig.namespace = namespace
+								//ensure testing accross nodes
+								if i%2 == 0 {
+									podConfig.nodeSelector = map[string]string{nodeHostnameKey: workerOneNodeName}
+
+								} else {
+
+									podConfig.nodeSelector = map[string]string{nodeHostnameKey: workerTwoNodeName}
+								}
+								By("creating pod " + podConfig.name + " in " + podConfig.namespace)
+								pod := runUDNPod(cs, podConfig.namespace, podConfig, nil)
+								pods = append(pods, pod)
+								podIP, err := podIPsForUserDefinedPrimaryNetwork(
+									cs,
+									pod.Namespace,
+									pod.Name,
+									namespacedName(namespace, network),
+									0,
+								)
+								Expect(err).NotTo(HaveOccurred())
+								if network == red {
+									redIPs = append(redIPs, podIP)
+								} else {
+									blueIPs = append(blueIPs, podIP)
+								}
+							}
+						}
 
-						} else {
+						By("ensuring pods only communicate with pods in their network")
+						for _, pod := range pods {
+							isRedPod := strings.Contains(pod.Name, red)
+							ips := redIPs
+							if !isRedPod {
+								ips = blueIPs
+							}
+							for _, ip := range ips {
+								result, err := e2ekubectl.RunKubectl(
+									pod.Namespace,
+									"exec",
+									pod.Name,
+									"--",
+									"curl",
+									"--connect-timeout",
+									"2",
+									net.JoinHostPort(ip, fmt.Sprintf("%d", port)+"/hostname"),
+								)
+								Expect(err).NotTo(HaveOccurred())
+								if isRedPod {
+									Expect(strings.Contains(result, red)).To(BeTrue())
+								} else {
+									Expect(strings.Contains(result, blue)).To(BeTrue())
+								}
+							}
+						}
 
-							podConfig.nodeSelector = map[string]string{nodeHostnameKey: workerTwoNodeName}
+						By("Deleting pods in network blue except " + fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1))
+						for i := range numberOfPods - 1 {
+							err := cs.CoreV1().Pods(namespaceBlue).Delete(
+								context.Background(),
+								fmt.Sprintf("%s-pod-%d", blue, i),
+								metav1.DeleteOptions{},
+							)
+							Expect(err).NotTo(HaveOccurred())
 						}
-						By("creating pod " + podConfig.name + " in " + podConfig.namespace)
-						pod := runUDNPod(cs, podConfig.namespace, podConfig, nil)
-						pods = append(pods, pod)
+
 						podIP, err := podIPsForUserDefinedPrimaryNetwork(
 							cs,
-							pod.Namespace,
-							pod.Name,
-							namespacedName(namespace, network),
+							namespaceBlue,
+							fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1),
+							namespacedName(namespaceBlue, blue),
 							0,
 						)
 						Expect(err).NotTo(HaveOccurred())
-						if network == red {
-							redIPs = append(redIPs, podIP)
-						} else {
-							blueIPs = append(blueIPs, podIP)
-						}
-					}
-				}
 
-				By("ensuring pods only communicate with pods in their network")
-				for _, pod := range pods {
-					isRedPod := strings.Contains(pod.Name, red)
-					ips := redIPs
-					if !isRedPod {
-						ips = blueIPs
-					}
-					for _, ip := range ips {
-						result, err := e2ekubectl.RunKubectl(
-							pod.Namespace,
-							"exec",
-							pod.Name,
-							"--",
-							"curl",
-							"--connect-timeout",
-							"2",
-							net.JoinHostPort(ip, fmt.Sprintf("%d", port)+"/hostname"),
-						)
-						Expect(err).NotTo(HaveOccurred())
-						if isRedPod {
-							Expect(strings.Contains(result, red)).To(BeTrue())
-						} else {
-							Expect(strings.Contains(result, blue)).To(BeTrue())
+						By("Remaining blue pod cannot communicate with red networks overlapping CIDR")
+						for _, ip := range redIPs {
+							if podIP == ip {
+								//don't try with your own IP
+								continue
+							}
+							_, err := e2ekubectl.RunKubectl(
+								namespaceBlue,
+								"exec",
+								fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1),
+								"--",
+								"curl",
+								"--connect-timeout",
+								"2",
+								net.JoinHostPort(ip, fmt.Sprintf("%d", port)),
+							)
+							Expect(strings.Contains(err.Error(), "Connection timeout")).To(Equal(true))
 						}
-					}
-				}
-
-				By("Deleting pods in network blue except " + fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1))
-				for i := range numberOfPods - 1 {
-					err := cs.CoreV1().Pods(namespaceBlue).Delete(
-						context.Background(),
-						fmt.Sprintf("%s-pod-%d", blue, i),
-						metav1.DeleteOptions{},
-					)
-					Expect(err).NotTo(HaveOccurred())
-				}
-
-				podIP, err := podIPsForUserDefinedPrimaryNetwork(
-					cs,
-					namespaceBlue,
-					fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1),
-					namespacedName(namespaceBlue, blue),
-					0,
+					},
+					// can completely fill the L2 topology because it does not depend on the size of the clusters hostsubnet
+					Entry(
+						"with L2 primary UDN",
+						"layer2",
+						4,
+						"10.128.0.0/29",
+						"2014:100:200::0/125",
+					),
+					// limit the number of pods to 10
+					Entry(
+						"with L3 primary UDN",
+						"layer3",
+						10,
+						userDefinedNetworkIPv4Subnet,
+						userDefinedNetworkIPv6Subnet,
+					),
 				)
-				Expect(err).NotTo(HaveOccurred())
-
-				By("Remaining blue pod cannot communicate with red networks overlapping CIDR")
-				for _, ip := range redIPs {
-					if podIP == ip {
-						//don't try with your own IP
-						continue
-					}
-					_, err := e2ekubectl.RunKubectl(
-						namespaceBlue,
-						"exec",
-						fmt.Sprintf("%s-pod-%d", blue, numberOfPods-1),
-						"--",
-						"curl",
-						"--connect-timeout",
-						"2",
-						net.JoinHostPort(ip, fmt.Sprintf("%d", port)),
-					)
-					Expect(strings.Contains(err.Error(), "Connection timeout")).To(Equal(true))
-				}
 			},
-			// can completely fill the L2 topology because it does not depend on the size of the clusters hostsubnet
-			Entry(
-				"with L2 primary UDN",
-				"layer2",
-				4,
-				"10.128.0.0/29",
-			),
-			// limit the number of pods to 10
-			Entry(
-				"with L3 primary UDN",
-				"layer3",
-				10,
-				userDefinedNetworkIPv4Subnet,
-			),
+			Entry("NetworkAttachmentDefinitions", func(c networkAttachmentConfigParams) error {
+				netConfig := newNetworkAttachmentConfig(c)
+				nad := generateNAD(netConfig)
+				_, err := nadClient.NetworkAttachmentDefinitions(c.namespace).Create(context.Background(), nad, metav1.CreateOptions{})
+				return err
+			}),
+			Entry("UserDefinedNetwork", func(c networkAttachmentConfigParams) error {
+				udnManifest := generateUserDefinedNetworkManifest(&c)
+				cleanup, err := createManifest(c.namespace, udnManifest)
+				DeferCleanup(cleanup)
+				Expect(waitForUserDefinedNetworkReady(c.namespace, c.name, 5*time.Second)).To(Succeed())
+				return err
+			}),
 		)
 	})
 
@@ -700,81 +691,92 @@ var _ = Describe("Network Segmentation", func() {
 				deleteClusterExternalContainer(externalContainerName)
 			})
 		})
-		DescribeTable(
-			"can be accessed to from the pods running in the Kubernetes cluster",
-			func(netConfigParams networkAttachmentConfigParams, clientPodConfig podConfiguration) {
-				if isLocalGWModeEnabled() {
-					const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/pull/4554"
-					e2eskipper.Skipf(
-						"These tests are known to fail on Local Gateway deployments. Upstream issue: %s", upstreamIssue,
-					)
-				}
-				if netConfigParams.topology == "layer2" && !isInterconnectEnabled() {
-					const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/issues/4642"
-					e2eskipper.Skipf(
-						"Egress e2e tests for layer2 topologies are known to fail on non-IC deployments. Upstream issue: %s", upstreamIssue,
-					)
-				}
-				netConfig := newNetworkAttachmentConfig(netConfigParams)
+		DescribeTableSubtree("created using",
+			func(createNetworkFn func(c networkAttachmentConfigParams) error) {
 
-				netConfig.namespace = f.Namespace.Name
-				clientPodConfig.namespace = f.Namespace.Name
+				DescribeTable(
+					"can be accessed to from the pods running in the Kubernetes cluster",
+					func(netConfigParams networkAttachmentConfigParams, clientPodConfig podConfiguration) {
+						if isLocalGWModeEnabled() {
+							const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/pull/4554"
+							e2eskipper.Skipf(
+								"These tests are known to fail on Local Gateway deployments. Upstream issue: %s", upstreamIssue,
+							)
+						}
+						if netConfigParams.topology == "layer2" && !isInterconnectEnabled() {
+							const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/issues/4642"
+							e2eskipper.Skipf(
+								"Egress e2e tests for layer2 topologies are known to fail on non-IC deployments. Upstream issue: %s", upstreamIssue,
+							)
+						}
+						clientPodConfig.namespace = f.Namespace.Name
 
-				By("creating the attachment configuration")
-				_, err := nadClient.NetworkAttachmentDefinitions(f.Namespace.Name).Create(
-					context.Background(),
-					generateNAD(netConfig),
-					metav1.CreateOptions{},
-				)
-				Expect(err).NotTo(HaveOccurred())
+						By("creating the network")
+						netConfigParams.namespace = f.Namespace.Name
+						Expect(createNetworkFn(netConfigParams)).To(Succeed())
+
+						By("instantiating the client pod")
+						clientPod, err := cs.CoreV1().Pods(clientPodConfig.namespace).Create(
+							context.Background(),
+							generatePodSpec(clientPodConfig),
+							metav1.CreateOptions{},
+						)
+						Expect(err).NotTo(HaveOccurred())
+						Expect(clientPod).NotTo(BeNil())
+
+						By("asserting the client pod reaches the `Ready` state")
+						var updatedPod *v1.Pod
+						Eventually(func() v1.PodPhase {
+							updatedPod, err = cs.CoreV1().Pods(f.Namespace.Name).Get(context.Background(), clientPod.GetName(), metav1.GetOptions{})
+							if err != nil {
+								return v1.PodFailed
+							}
+							return updatedPod.Status.Phase
+						}, 2*time.Minute, 6*time.Second).Should(Equal(v1.PodRunning))
+						framework.Logf("Client pod was created on node %s", updatedPod.Spec.NodeName)
+
+						By("asserting UDN pod is connected to UDN network")
+						podAnno, err := unmarshalPodAnnotation(updatedPod.Annotations, f.Namespace.Name+"/"+userDefinedNetworkName)
+						Expect(err).NotTo(HaveOccurred())
+						framework.Logf("Client pod's annotation for network %s is %v", userDefinedNetworkName, podAnno)
 
-				By("instantiating the client pod")
-				clientPod, err := cs.CoreV1().Pods(clientPodConfig.namespace).Create(
-					context.Background(),
-					generatePodSpec(clientPodConfig),
-					metav1.CreateOptions{},
+						Expect(podAnno.Routes).To(HaveLen(expectedNumberOfRoutes(netConfigParams)))
+
+						assertClientExternalConnectivity(clientPodConfig, externalIpv4, externalIpv6, port)
+					},
+					Entry("by one pod over a layer2 network",
+						networkAttachmentConfigParams{
+							name:     userDefinedNetworkName,
+							topology: "layer2",
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							role:     "primary",
+						},
+						*podConfig("client-pod"),
+					),
+					Entry("by one pod over a layer3 network",
+						networkAttachmentConfigParams{
+							name:     userDefinedNetworkName,
+							topology: "layer3",
+							cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
+							role:     "primary",
+						},
+						*podConfig("client-pod"),
+					),
 				)
-				Expect(err).NotTo(HaveOccurred())
-				Expect(clientPod).NotTo(BeNil())
-
-				By("asserting the client pod reaches the `Ready` state")
-				var updatedPod *v1.Pod
-				Eventually(func() v1.PodPhase {
-					updatedPod, err = cs.CoreV1().Pods(f.Namespace.Name).Get(context.Background(), clientPod.GetName(), metav1.GetOptions{})
-					if err != nil {
-						return v1.PodFailed
-					}
-					return updatedPod.Status.Phase
-				}, 2*time.Minute, 6*time.Second).Should(Equal(v1.PodRunning))
-				framework.Logf("Client pod was created on node %s", updatedPod.Spec.NodeName)
-
-				By("asserting UDN pod is connected to UDN network")
-				podAnno, err := unmarshalPodAnnotation(updatedPod.Annotations, f.Namespace.Name+"/"+userDefinedNetworkName)
-				Expect(err).NotTo(HaveOccurred())
-				framework.Logf("Client pod's annotation for network %s is %v", userDefinedNetworkName, podAnno)
-
-				Expect(podAnno.Routes).To(HaveLen(expectedNumberOfRoutes(netConfig)))
-
-				assertClientExternalConnectivity(clientPodConfig, externalIpv4, externalIpv6, port)
 			},
-			Entry("by one pod with dualstack addresses over a layer2 network",
-				networkAttachmentConfigParams{
-					name:     userDefinedNetworkName,
-					topology: "layer2",
-					cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
-					role:     "primary",
-				},
-				*podConfig("client-pod"),
-			),
-			Entry("by one pod over a layer3 network",
-				networkAttachmentConfigParams{
-					name:     userDefinedNetworkName,
-					topology: "layer3",
-					cidr:     correctCIDRFamily(userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet),
-					role:     "primary",
-				},
-				*podConfig("client-pod"),
-			),
+			Entry("NetworkAttachmentDefinitions", func(c networkAttachmentConfigParams) error {
+				netConfig := newNetworkAttachmentConfig(c)
+				nad := generateNAD(netConfig)
+				_, err := nadClient.NetworkAttachmentDefinitions(f.Namespace.Name).Create(context.Background(), nad, metav1.CreateOptions{})
+				return err
+			}),
+			Entry("UserDefinedNetwork", func(c networkAttachmentConfigParams) error {
+				udnManifest := generateUserDefinedNetworkManifest(&c)
+				cleanup, err := createManifest(f.Namespace.Name, udnManifest)
+				DeferCleanup(cleanup)
+				Expect(waitForUserDefinedNetworkReady(f.Namespace.Name, c.name, 5*time.Second)).To(Succeed())
+				return err
+			}),
 		)
 	})
 })
@@ -1080,7 +1082,7 @@ func runExternalContainerCmd() []string {
 	return []string{"--network", "kind"}
 }
 
-func expectedNumberOfRoutes(netConfig networkAttachmentConfig) int {
+func expectedNumberOfRoutes(netConfig networkAttachmentConfigParams) int {
 	if netConfig.topology == "layer2" {
 		if isIPv6Supported() && isIPv4Supported() {
 			return 4 // 2 routes per family
diff --git a/test/e2e/util.go b/test/e2e/util.go
index 794a22b247f..9ac0c0b109b 100644
--- a/test/e2e/util.go
+++ b/test/e2e/util.go
@@ -185,7 +185,6 @@ func unmarshalPodAnnotation(annotations map[string]string, networkName string) (
 
 	podAnnotation := &PodAnnotation{Primary: a.Primary}
 	var err error
-
 	podAnnotation.MAC, err = net.ParseMAC(a.MAC)
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse pod MAC %q: %v", a.MAC, err)

From 96abe3246082163b1cd8929132c4d6e4b5cb6b67 Mon Sep 17 00:00:00 2001
From: Arnab Ghosh <arnabghosh89@gmail.com>
Date: Mon, 9 Sep 2024 17:48:41 +0530
Subject: [PATCH 16/48] Add unit tests for UDN while DS is true

This commit is to add some unit tests to make sure proper NAT entries
are being created i NBDB while DisableSNATMultipleGWs is set to true.

Signed-off-by: Arnab Ghosh <arnabghosh89@gmail.com>
---
 go-controller/pkg/ovn/multihoming_test.go     |  8 ++++
 ...econdary_layer2_network_controller_test.go | 40 ++++++++++++++++---
 ...econdary_layer3_network_controller_test.go | 40 +++++++++++++++++--
 3 files changed, 78 insertions(+), 10 deletions(-)

diff --git a/go-controller/pkg/ovn/multihoming_test.go b/go-controller/pkg/ovn/multihoming_test.go
index 8a6681347ca..ee67dc1e6ed 100644
--- a/go-controller/pkg/ovn/multihoming_test.go
+++ b/go-controller/pkg/ovn/multihoming_test.go
@@ -396,6 +396,14 @@ func nonICClusterTestConfiguration() testConfiguration {
 	return testConfiguration{}
 }
 
+func icClusterWithDisableSNATTestConfiguration() testConfiguration {
+	return testConfiguration{
+		configToOverride:   enableICFeatureConfig(),
+		expectationOptions: []option{withInterconnectCluster()},
+		gatewayConfig:      &config.GatewayConfig{DisableSNATMultipleGWs: true},
+	}
+}
+
 func newMultiHomedPod(namespace, name, node, podIP string, multiHomingConfigs ...secondaryNetInfo) *v1.Pod {
 	pod := newPod(namespace, name, node, podIP)
 	var secondaryNetworks []nadapi.NetworkSelectionElement
diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
index 4c093da399d..774f9b78031 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
@@ -58,6 +58,9 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			podInfo := dummyL2TestPod(ns, netInfo)
 			if testConfig.configToOverride != nil {
 				config.OVNKubernetesFeature = *testConfig.configToOverride
+				if testConfig.gatewayConfig != nil {
+					config.Gateway.DisableSNATMultipleGWs = testConfig.gatewayConfig.DisableSNATMultipleGWs
+				}
 			}
 			app.Action = func(ctx *cli.Context) error {
 				By(fmt.Sprintf("creating a network attachment definition for network: %s", netInfo.netName))
@@ -171,6 +174,10 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16"),
 			icClusterTestConfiguration(),
 		),
+		table.Entry("pod on a user defined primary network on an IC cluster",
+			dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16"),
+			icClusterWithDisableSNATTestConfiguration(),
+		),
 	)
 
 	table.DescribeTable(
@@ -179,6 +186,9 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			podInfo := dummyTestPod(ns, netInfo)
 			if testConfig.configToOverride != nil {
 				config.OVNKubernetesFeature = *testConfig.configToOverride
+				if testConfig.gatewayConfig != nil {
+					config.Gateway.DisableSNATMultipleGWs = testConfig.gatewayConfig.DisableSNATMultipleGWs
+				}
 			}
 			app.Action = func(ctx *cli.Context) error {
 				netConf := netInfo.netconf()
@@ -277,6 +287,10 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			dummyLayer2PrimaryUserDefinedNetwork("192.168.0.0/16"),
 			icClusterTestConfiguration(),
 		),
+		table.Entry("pod on a user defined primary network on an interconnect cluster",
+			dummyLayer2PrimaryUserDefinedNetwork("192.168.0.0/16"),
+			icClusterWithDisableSNATTestConfiguration(),
+		),
 	)
 
 })
@@ -351,11 +365,17 @@ func dummyL2TestPod(nsName string, info secondaryNetInfo) testPod {
 	return pod
 }
 
+func dummyL2TestPodAdditionalNetworkIP() string {
+	secNetInfo := dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16")
+	return dummyL2TestPod(ns, secNetInfo).getNetworkPortInfo(secNetInfo.netName, secNetInfo.nadName).podIP
+}
+
 func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayConfig, nodeName string) []libovsdbtest.TestData {
 	const (
 		nat1              = "nat1-UUID"
 		nat2              = "nat2-UUID"
 		nat3              = "nat3-UUID"
+		perPodSNAT        = "pod-snat-UUID"
 		sr1               = "sr1-UUID"
 		sr2               = "sr2-UUID"
 		routerPolicyUUID1 = "lrp1-UUID"
@@ -365,11 +385,17 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 	gwRouterToNetworkSwitchPortName := ovntypes.GWRouterToJoinSwitchPrefix + gwRouterName
 	gwRouterToExtSwitchPortName := fmt.Sprintf("%s%s", ovntypes.GWRouterToExtSwitchPrefix, gwRouterName)
 
+	var nat []string
+	if config.Gateway.DisableSNATMultipleGWs {
+		nat = append(nat, perPodSNAT)
+	} else {
+		nat = append(nat, nat1, nat2, nat3)
+	}
 	expectedEntities := []libovsdbtest.TestData{
 		&nbdb.LogicalRouter{
 			Name:         gwRouterName,
 			UUID:         gwRouterName + "-UUID",
-			Nat:          []string{nat1, nat2, nat3},
+			Nat:          nat,
 			Ports:        []string{gwRouterToNetworkSwitchPortName + "-UUID", gwRouterToExtSwitchPortName + "-UUID"},
 			StaticRoutes: []string{sr1, sr2},
 			ExternalIDs:  gwRouterExternalIDs(netInfo, gwConfig),
@@ -379,11 +405,6 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		expectedGWToNetworkSwitchRouterPort(gwRouterToNetworkSwitchPortName, netInfo, gwRouterIPAddress(), layer2SubnetGWAddr()),
 		expectedGRStaticRoute(sr1, dummyMasqueradeSubnet().String(), nextHopMasqueradeIP().String(), nil, &staticRouteOutputPort, netInfo),
 		expectedGRStaticRoute(sr2, ipv4DefaultRoute().String(), nodeGateway().IP.String(), nil, &staticRouteOutputPort, netInfo),
-
-		newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)),
-		newNATEntry(nat2, dummyJoinIP().IP.String(), layer2Subnet().String(), standardNonDefaultNetworkExtIDs(netInfo)),
-		newNATEntry(nat3, dummyJoinIP().IP.String(), layer2SubnetGWAddr().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)),
-
 		expectedGRToExternalSwitchLRP(gwRouterName, netInfo, nodePhysicalIPAddress(), udnGWSNATAddress()),
 		expectedStaticMACBinding(gwRouterName, nextHopMasqueradeIP()),
 
@@ -393,6 +414,13 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 	for _, entity := range expectedExternalSwitchAndLSPs(netInfo, gwConfig, nodeName) {
 		expectedEntities = append(expectedEntities, entity)
 	}
+	if config.Gateway.DisableSNATMultipleGWs {
+		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyL2TestPodAdditionalNetworkIP(), nil))
+	} else {
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), layer2Subnet().String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat3, dummyJoinIP().IP.String(), layer2SubnetGWAddr().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+	}
 	return expectedEntities
 }
 
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index 386064b73eb..38c24b0ff3c 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -46,6 +46,7 @@ const (
 
 type testConfiguration struct {
 	configToOverride   *config.OVNKubernetesFeatureConfig
+	gatewayConfig      *config.GatewayConfig
 	expectationOptions []option
 }
 
@@ -86,6 +87,9 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			podInfo := dummyTestPod(ns, netInfo)
 			if testConfig.configToOverride != nil {
 				config.OVNKubernetesFeature = *testConfig.configToOverride
+				if testConfig.gatewayConfig != nil {
+					config.Gateway.DisableSNATMultipleGWs = testConfig.gatewayConfig.DisableSNATMultipleGWs
+				}
 			}
 			app.Action = func(ctx *cli.Context) error {
 				nad, err := newNetworkAttachmentDefinition(
@@ -201,6 +205,10 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
 			icClusterTestConfiguration(),
 		),
+		table.Entry("pod on a user defined primary network on an interconnect cluster",
+			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			icClusterWithDisableSNATTestConfiguration(),
+		),
 	)
 
 	table.DescribeTable(
@@ -209,6 +217,9 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			podInfo := dummyTestPod(ns, netInfo)
 			if testConfig.configToOverride != nil {
 				config.OVNKubernetesFeature = *testConfig.configToOverride
+				if testConfig.gatewayConfig != nil {
+					config.Gateway.DisableSNATMultipleGWs = testConfig.gatewayConfig.DisableSNATMultipleGWs
+				}
 			}
 			app.Action = func(ctx *cli.Context) error {
 				netConf := netInfo.netconf()
@@ -309,6 +320,10 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
 			icClusterTestConfiguration(),
 		),
+		table.Entry("pod on a user defined primary network on an interconnect cluster",
+			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			icClusterWithDisableSNATTestConfiguration(),
+		),
 	)
 })
 
@@ -459,6 +474,11 @@ func dummyTestPod(nsName string, info secondaryNetInfo) testPod {
 	return pod
 }
 
+func dummyTestPodAdditionalNetworkIP() string {
+	secNetInfo := dummyPrimaryLayer2UserDefinedNetwork("192.168.0.0/16")
+	return dummyTestPod(ns, secNetInfo).getNetworkPortInfo(secNetInfo.netName, secNetInfo.nadName).podIP
+}
+
 func dummySecondaryUserDefinedNetwork(subnets string) secondaryNetInfo {
 	return secondaryNetInfo{
 		netName:  secondaryNetworkName,
@@ -568,6 +588,7 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 	const (
 		nat1             = "abc-UUID"
 		nat2             = "cba-UUID"
+		perPodSNAT       = "pod-snat-UUID"
 		staticRoute1     = "srA-UUID"
 		staticRoute2     = "srB-UUID"
 		staticRoute3     = "srC-UUID"
@@ -579,22 +600,33 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 	ipv4Subnet := networkSubnet(netInfo)
 	nextHopMasqIP := nextHopMasqueradeIP().String()
 	masqSubnet := config.Gateway.V4MasqueradeSubnet
-	return []libovsdbtest.TestData{
+	var nat []string
+	if config.Gateway.DisableSNATMultipleGWs {
+		nat = append(nat, perPodSNAT)
+	} else {
+		nat = append(nat, nat1, nat2)
+	}
+	expectedEntities := []libovsdbtest.TestData{
 		&nbdb.LogicalRouter{
 			Name:         gwRouterName,
 			UUID:         gwRouterName + "-UUID",
 			ExternalIDs:  gwRouterExternalIDs(netInfo, gwConfig),
 			Options:      gwRouterOptions(gwConfig),
 			Ports:        []string{gwRouterToJoinLRPUUID, gwRouterToExtLRPUUID},
-			Nat:          []string{nat1, nat2},
+			Nat:          nat,
 			StaticRoutes: []string{staticRoute1, staticRoute2, staticRoute3},
 		},
-		newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)),
-		newNATEntry(nat2, dummyJoinIP().IP.String(), networkSubnet(netInfo), standardNonDefaultNetworkExtIDs(netInfo)),
 		expectedGRStaticRoute(staticRoute1, ipv4Subnet, dummyJoinIP().IP.String(), nil, nil, netInfo),
 		expectedGRStaticRoute(staticRoute2, ipv4DefaultRoute, nextHopIP, nil, &staticRouteOutputPort, netInfo),
 		expectedGRStaticRoute(staticRoute3, masqSubnet, nextHopMasqIP, nil, &staticRouteOutputPort, netInfo),
 	}
+	if config.Gateway.DisableSNATMultipleGWs {
+		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyTestPodAdditionalNetworkIP(), nil))
+	} else {
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), networkSubnet(netInfo), standardNonDefaultNetworkExtIDs(netInfo)))
+	}
+	return expectedEntities
 }
 
 func expectedStaticMACBinding(gwRouterName string, ip net.IP) *nbdb.StaticMACBinding {

From 6cdf7ecdff5ea9017c09ca8b35acc3de944a6eff Mon Sep 17 00:00:00 2001
From: Peng Liu <pliu@redhat.com>
Date: Thu, 29 Aug 2024 13:23:56 +0000
Subject: [PATCH 17/48] Add static route to the hairpin masquerade IPs to pod

When users attach pod to a secondary network and override the default
route pod. It will cause the assymetric routing for service haripin
traffic.

We add static routes to ensure the traffic to the hairpin masquerade
IP always goes to OVN.

Signed-off-by: Peng Liu <pliu@redhat.com>
---
 .../pkg/allocator/pod/pod_annotation_test.go  | 35 +++++++++++++++++++
 go-controller/pkg/ovn/pods_test.go            |  7 ++++
 go-controller/pkg/util/pod_annotation.go      | 17 ++++++++-
 3 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/go-controller/pkg/allocator/pod/pod_annotation_test.go b/go-controller/pkg/allocator/pod/pod_annotation_test.go
index 58fdc76873c..334ece75733 100644
--- a/go-controller/pkg/allocator/pod/pod_annotation_test.go
+++ b/go-controller/pkg/allocator/pod/pod_annotation_test.go
@@ -218,6 +218,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) {
 				MAC:      util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.3/24")[0].IP),
 				Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()},
 				Routes: []util.PodRoute{
+					{
+						Dest: &net.IPNet{
+							IP:   ovntest.MustParseIP("169.254.169.5"),
+							Mask: net.CIDRMask(32, 32),
+						},
+						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
+					},
 					{
 						Dest:    ovntest.MustParseIPNet("100.64.0.0/16"),
 						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
@@ -301,6 +308,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) {
 				MAC:      util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.4/24")[0].IP),
 				Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()},
 				Routes: []util.PodRoute{
+					{
+						Dest: &net.IPNet{
+							IP:   ovntest.MustParseIP("169.254.169.5"),
+							Mask: net.CIDRMask(32, 32),
+						},
+						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
+					},
 					{
 						Dest:    ovntest.MustParseIPNet("100.64.0.0/16"),
 						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
@@ -332,6 +346,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) {
 				MAC:      util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.4/24")[0].IP),
 				Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()},
 				Routes: []util.PodRoute{
+					{
+						Dest: &net.IPNet{
+							IP:   ovntest.MustParseIP("169.254.169.5"),
+							Mask: net.CIDRMask(32, 32),
+						},
+						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
+					},
 					{
 						Dest:    ovntest.MustParseIPNet("100.64.0.0/16"),
 						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
@@ -362,6 +383,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) {
 				MAC:      util.IPAddrToHWAddr(ovntest.MustParseIPNets("192.168.0.3/24")[0].IP),
 				Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()},
 				Routes: []util.PodRoute{
+					{
+						Dest: &net.IPNet{
+							IP:   ovntest.MustParseIP("169.254.169.5"),
+							Mask: net.CIDRMask(32, 32),
+						},
+						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
+					},
 					{
 						Dest:    ovntest.MustParseIPNet("100.64.0.0/16"),
 						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
@@ -420,6 +448,13 @@ func Test_allocatePodAnnotationWithRollback(t *testing.T) {
 				MAC:      requestedMACParsed,
 				Gateways: []net.IP{ovntest.MustParseIP("192.168.0.1").To4()},
 				Routes: []util.PodRoute{
+					{
+						Dest: &net.IPNet{
+							IP:   ovntest.MustParseIP("169.254.169.5"),
+							Mask: net.CIDRMask(32, 32),
+						},
+						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
+					},
 					{
 						Dest:    ovntest.MustParseIPNet("100.64.0.0/16"),
 						NextHop: ovntest.MustParseIP("192.168.0.1").To4(),
diff --git a/go-controller/pkg/ovn/pods_test.go b/go-controller/pkg/ovn/pods_test.go
index 860da8ffebd..2540ef194ff 100644
--- a/go-controller/pkg/ovn/pods_test.go
+++ b/go-controller/pkg/ovn/pods_test.go
@@ -250,6 +250,13 @@ func newTPod(nodeName, nodeSubnet, nodeMgtIP, nodeGWIP, podName, podIPs, podMAC,
 				routeSources = append(routeSources, sc)
 			}
 		}
+		hairpinMasqueradeIP := config.Gateway.MasqueradeIPs.V4OVNServiceHairpinMasqueradeIP.String()
+		mask := 32
+		if isIPv6 {
+			hairpinMasqueradeIP = config.Gateway.MasqueradeIPs.V6OVNServiceHairpinMasqueradeIP.String()
+			mask = 128
+		}
+		routeSources = append(routeSources, ovntest.MustParseIPNet(fmt.Sprintf("%s/%d", hairpinMasqueradeIP, mask)))
 		joinNet := config.Gateway.V4JoinSubnet
 		if isIPv6 {
 			joinNet = config.Gateway.V6JoinSubnet
diff --git a/go-controller/pkg/util/pod_annotation.go b/go-controller/pkg/util/pod_annotation.go
index 6d44a58fa33..12583871b96 100644
--- a/go-controller/pkg/util/pod_annotation.go
+++ b/go-controller/pkg/util/pod_annotation.go
@@ -467,6 +467,20 @@ func serviceCIDRToRoute(isIPv6 bool, gatewayIP net.IP) []PodRoute {
 	return podRoutes
 }
 
+func hairpinMasqueradeIPToRoute(isIPv6 bool, gatewayIP net.IP) PodRoute {
+	ip := config.Gateway.MasqueradeIPs.V4OVNServiceHairpinMasqueradeIP
+	if isIPv6 {
+		ip = config.Gateway.MasqueradeIPs.V6OVNServiceHairpinMasqueradeIP
+	}
+	return PodRoute{
+		Dest: &net.IPNet{
+			IP:   ip,
+			Mask: GetIPFullMask(ip),
+		},
+		NextHop: gatewayIP,
+	}
+}
+
 // addRoutesGatewayIP updates the provided pod annotation for the provided pod
 // with the gateways derived from the allocated IPs
 func AddRoutesGatewayIP(
@@ -581,7 +595,8 @@ func AddRoutesGatewayIP(
 		if podAnnotation.Role == types.NetworkRolePrimary {
 			// Ensure default service network traffic always goes to OVN
 			podAnnotation.Routes = append(podAnnotation.Routes, serviceCIDRToRoute(isIPv6, gatewayIPnet.IP)...)
-
+			// Ensure service hairpin masquerade traffic always goes to OVN
+			podAnnotation.Routes = append(podAnnotation.Routes, hairpinMasqueradeIPToRoute(isIPv6, gatewayIPnet.IP))
 			otherDefaultRoute := otherDefaultRouteV4
 			if isIPv6 {
 				otherDefaultRoute = otherDefaultRouteV6

From b3ba566d043011be863a5cffce47918002fb3d72 Mon Sep 17 00:00:00 2001
From: Enrique Llorente <ellorent@redhat.com>
Date: Tue, 10 Sep 2024 13:04:11 +0200
Subject: [PATCH 18/48] kind: Pin metallb to v0.14.8

There are some expectation at the dev-env interface at metallb that can
change and break ovn-k CI, let's pin it so we can propertly consume
those changes at a PR later on.

Signed-off-by: Enrique Llorente <ellorent@redhat.com>
---
 contrib/kind-common | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/contrib/kind-common b/contrib/kind-common
index 0d49fdbe5df..fe7dc3e6d7e 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -122,12 +122,13 @@ install_ingress() {
 
 METALLB_DIR="/tmp/metallb"
 install_metallb() {
+  local metallb_version=v0.14.8
   mkdir -p /tmp/metallb
   local builddir
   builddir=$(mktemp -d "${METALLB_DIR}/XXXXXX")
 
   pushd "${builddir}"
-  git clone https://github.com/metallb/metallb.git
+  git clone https://github.com/metallb/metallb.git -b $metallb_version
   cd metallb
   # Use global IP next hops in IPv6
   if  [ "$KIND_IPV6_SUPPORT" == true ]; then

From 45abadba6b42558d699e1d97604c6124430344b0 Mon Sep 17 00:00:00 2001
From: Tim Rozet <trozet@redhat.com>
Date: Tue, 10 Sep 2024 15:49:37 -0400
Subject: [PATCH 19/48] Adds e2e test: conntrack flush after ovnkube delete

Test opens a TCP connection that simulates a GCP LB environment where
the packet is redirected via iptables to a local server on a node. Note,
in GCP the LB does not DNAT the VIP, so the packet arrives to the node
with the GCP VIP on it. In OCP, we then redirect that packet to the
local kapi server running on the node.

Once the test opens the TCP connection, it leaves it open for 2 minutes
while ovnkube-node is then deleted. Post ovn-controller starting it
should not flush the conntrack in zone 0, and the test ensures that the
conntrack entry still exists.

Recent OVN regression that prompted this E2E: https://issues.redhat.com/browse/FDP-773

Signed-off-by: Tim Rozet <trozet@redhat.com>
---
 test/e2e/e2e.go  | 94 ++++++++++++++++++++++++++++++++++++++++++++++++
 test/e2e/util.go | 51 ++++++++++++++------------
 2 files changed, 123 insertions(+), 22 deletions(-)

diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go
index 790a0798902..f1d9308e6fc 100644
--- a/test/e2e/e2e.go
+++ b/test/e2e/e2e.go
@@ -46,10 +46,70 @@ const (
 	agnhostImage         = "registry.k8s.io/e2e-test-images/agnhost:2.26"
 	iperf3Image          = "quay.io/sronanrh/iperf"
 	ovnNs                = "ovn-kubernetes" //OVN kubernetes namespace
+	redirectIP           = "123.123.123.123"
+	redirectPort         = "13337"
+	exContainerName      = "tcp-continuous-client"
 )
 
 type podCondition = func(pod *v1.Pod) (bool, error)
 
+// setupHostRedirectPod
+func setupHostRedirectPod(f *framework.Framework, node *v1.Node, exContainerName string, isIPv6 bool) error {
+	_, _ = createClusterExternalContainer(exContainerName, externalContainerImage, []string{"-itd", "--privileged", "--network", externalContainerNetwork}, []string{})
+	nodeV4, nodeV6 := getContainerAddressesForNetwork(node.Name, externalContainerNetwork)
+	mask := 32
+	ipCmd := []string{"ip"}
+	nodeIP := nodeV4
+	if isIPv6 {
+		mask = 128
+		ipCmd = []string{"ip", "-6"}
+		nodeIP = nodeV6
+	}
+	cmd := []string{"docker", "exec", exContainerName}
+	cmd = append(cmd, ipCmd...)
+	cmd = append(cmd, "route", "add", fmt.Sprintf("%s/%d", redirectIP, mask), "via", nodeIP)
+	_, err := runCommand(cmd...)
+	if err != nil {
+		return err
+	}
+
+	// setup redirect iptables rule in node
+	ipTablesArgs := []string{"PREROUTING", "-t", "nat", "--dst", redirectIP, "-j", "REDIRECT"}
+	updateIPTablesRulesForNode("insert", node.Name, ipTablesArgs, isIPv6)
+
+	command := []string{
+		"bash", "-c",
+		fmt.Sprintf("set -xe; while true; do nc -l -p %s; done",
+			redirectPort),
+	}
+	tcpServer := "tcp-continuous-server"
+	// setup host networked pod to act as server
+	pod := &v1.Pod{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: tcpServer,
+		},
+		Spec: v1.PodSpec{
+			Containers: []v1.Container{
+				{
+					Name:    tcpServer,
+					Image:   agnhostImage,
+					Command: command,
+				},
+			},
+			NodeName:      node.Name,
+			RestartPolicy: v1.RestartPolicyNever,
+			HostNetwork:   true,
+		},
+	}
+	podClient := f.ClientSet.CoreV1().Pods(f.Namespace.Name)
+	_, err = podClient.Create(context.Background(), pod, metav1.CreateOptions{})
+	if err != nil {
+		return err
+	}
+	err = e2epod.WaitForPodNotPending(context.TODO(), f.ClientSet, f.Namespace.Name, tcpServer)
+	return err
+}
+
 // checkContinuousConnectivity creates a pod and checks that it can connect to the given host over tries*2 seconds.
 // The created pod object is sent to the podChan while any errors along the way are sent to the errChan.
 // Callers are expected to read the errChan and verify that they received a nil before fetching
@@ -649,6 +709,7 @@ var _ = ginkgo.Describe("e2e control plane", func() {
 		numControlPlanePods   int
 		controlPlanePodName   string
 		controlPlaneLeaseName string
+		nodes                 []v1.Node
 	)
 
 	ginkgo.BeforeEach(func() {
@@ -683,6 +744,14 @@ var _ = ginkgo.Describe("e2e control plane", func() {
 		if IsIPv6Cluster(f.ClientSet) {
 			extDNSIP = "2001:4860:4860::8888"
 		}
+		n, err := e2enode.GetBoundedReadySchedulableNodes(context.TODO(), f.ClientSet, 3)
+		framework.ExpectNoError(err)
+		nodes = n.Items
+
+	})
+
+	ginkgo.AfterEach(func() {
+		deleteClusterExternalContainer(exContainerName)
 	})
 
 	ginkgo.It("should provide Internet connection continuously when ovnkube-node pod is killed", func() {
@@ -700,6 +769,26 @@ var _ = ginkgo.Describe("e2e control plane", func() {
 		testPod := <-podChan
 		nodeName := testPod.Spec.NodeName
 		framework.Logf("Test pod running on %q", nodeName)
+		var targetNode *v1.Node
+		for _, node := range nodes {
+			if node.Name == nodeName {
+				targetNode = &node
+			}
+		}
+		gomega.Expect(targetNode).ToNot(gomega.BeNil())
+		err = setupHostRedirectPod(f, targetNode, exContainerName, IsIPv6Cluster(f.ClientSet))
+		framework.ExpectNoError(err)
+
+		// start TCP client
+		go func() {
+			defer ginkgo.GinkgoRecover()
+			_, _ = runCommand(containerRuntime, "exec", exContainerName, "nc", "--idle-timeout", "120s", redirectIP, redirectPort)
+		}()
+
+		ginkgo.By("Checking that TCP redirect connection entry in conntrack before ovnkube-node restart")
+		gomega.Eventually(func() int {
+			return pokeConntrackEntries(nodeName, redirectIP, "tcp", nil)
+		}, "10s", "1s").ShouldNot(gomega.Equal(0))
 
 		ginkgo.By("Deleting ovn-kube pod on node " + nodeName)
 		err = restartOVNKubeNodePod(f.ClientSet, "ovn-kubernetes", nodeName)
@@ -710,6 +799,11 @@ var _ = ginkgo.Describe("e2e control plane", func() {
 
 		err = waitClusterHealthy(f, numControlPlanePods, controlPlanePodName)
 		framework.ExpectNoError(err, "one or more nodes failed to go back ready, schedulable, and untainted")
+
+		ginkgo.By("Checking that TCP redirect connection entry in conntrack remained after ovnkube-node restart")
+		gomega.Consistently(func() int {
+			return pokeConntrackEntries(nodeName, redirectIP, "tcp", nil)
+		}, "5s", "500ms").ShouldNot(gomega.Equal(0))
 	})
 
 	ginkgo.It("should provide Internet connection continuously when pod running master instance of ovnkube-control-plane is killed", func() {
diff --git a/test/e2e/util.go b/test/e2e/util.go
index 9ac0c0b109b..27f645c6278 100644
--- a/test/e2e/util.go
+++ b/test/e2e/util.go
@@ -1037,6 +1037,7 @@ func setUnsetTemplateContainerEnv(c kubernetes.Interface, namespace, resource, c
 // allowOrDropNodeInputTrafficOnPort ensures or deletes a drop iptables
 // input rule for the specified node, protocol and port
 func allowOrDropNodeInputTrafficOnPort(op, nodeName, protocol, port string) {
+	ipTablesArgs := []string{"INPUT", "-p", protocol, "--dport", port, "-j", "DROP"}
 	switch op {
 	case "Allow":
 		op = "delete"
@@ -1045,31 +1046,37 @@ func allowOrDropNodeInputTrafficOnPort(op, nodeName, protocol, port string) {
 	default:
 		framework.Failf("unsupported op %s", op)
 	}
+	updateIPTablesRulesForNode(op, nodeName, ipTablesArgs, false)
+	updateIPTablesRulesForNode(op, nodeName, ipTablesArgs, true)
+}
 
+func updateIPTablesRulesForNode(op, nodeName string, ipTablesArgs []string, ipv6 bool) {
 	args := []string{"get", "pods", "--selector=app=ovnkube-node", "--field-selector", fmt.Sprintf("spec.nodeName=%s", nodeName), "-o", "jsonpath={.items..metadata.name}"}
 	ovnKubePodName := e2ekubectl.RunKubectlOrDie(ovnNamespace, args...)
-	ipTablesArgs := []string{"INPUT", "-p", protocol, "--dport", port, "-j", "DROP"}
-	for _, iptables := range []string{"iptables", "ip6tables"} {
-		args = []string{"exec", ovnKubePodName, "-c", getNodeContainerName(), "--", iptables, "--check"}
-		_, err := e2ekubectl.RunKubectl(ovnNamespace, append(args, ipTablesArgs...)...)
-		// errors known to be equivalent to not found
-		notFound1 := "No chain/target/match by that name"
-		notFound2 := "does a matching rule exist in that chain?"
-		notFound := err != nil && (strings.Contains(err.Error(), notFound1) || strings.Contains(err.Error(), notFound2))
-		if err != nil && !notFound {
-			framework.Failf("failed to check existance of %s rule on node %s: %v", iptables, nodeName, err)
-		}
-		if op == "delete" && notFound {
-			// rule is not there
-			return
-		} else if op == "insert" && err == nil {
-			// rule is already there
-			return
-		}
-		args = []string{"exec", ovnKubePodName, "-c", getNodeContainerName(), "--", iptables, "--" + op}
-		framework.Logf("%s %s input rule for protocol %s port %s action DROP on node %s", op, iptables, protocol, port, nodeName)
-		e2ekubectl.RunKubectlOrDie(ovnNamespace, append(args, ipTablesArgs...)...)
-	}
+	iptables := "iptables"
+	if ipv6 {
+		iptables = "ip6tables"
+	}
+
+	args = []string{"exec", ovnKubePodName, "-c", getNodeContainerName(), "--", iptables, "--check"}
+	_, err := e2ekubectl.RunKubectl(ovnNamespace, append(args, ipTablesArgs...)...)
+	// errors known to be equivalent to not found
+	notFound1 := "No chain/target/match by that name"
+	notFound2 := "does a matching rule exist in that chain?"
+	notFound := err != nil && (strings.Contains(err.Error(), notFound1) || strings.Contains(err.Error(), notFound2))
+	if err != nil && !notFound {
+		framework.Failf("failed to check existance of %s rule on node %s: %v", iptables, nodeName, err)
+	}
+	if op == "delete" && notFound {
+		// rule is not there
+		return
+	} else if op == "insert" && err == nil {
+		// rule is already there
+		return
+	}
+	args = []string{"exec", ovnKubePodName, "-c", getNodeContainerName(), "--", iptables, "--" + op}
+	framework.Logf("%s %s rule: %q on node %s", op, iptables, strings.Join(ipTablesArgs, ","), nodeName)
+	e2ekubectl.RunKubectlOrDie(ovnNamespace, append(args, ipTablesArgs...)...)
 }
 
 func randStr(n int) string {

From 537a64f189b15b759d9fd8f2fd054784ca062ff5 Mon Sep 17 00:00:00 2001
From: Tim Rozet <trozet@redhat.com>
Date: Wed, 11 Sep 2024 11:57:21 -0400
Subject: [PATCH 20/48] Bump OVN for https://issues.redhat.com/browse/FDP-773

Signed-off-by: Tim Rozet <trozet@redhat.com>
---
 dist/images/Dockerfile.fedora | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dist/images/Dockerfile.fedora b/dist/images/Dockerfile.fedora
index efdfb4d57d7..5e891a6c189 100644
--- a/dist/images/Dockerfile.fedora
+++ b/dist/images/Dockerfile.fedora
@@ -15,7 +15,7 @@ USER root
 
 ENV PYTHONDONTWRITEBYTECODE yes
 
-ARG ovnver=ovn-24.03.90-6.fc42
+ARG ovnver=ovn-24.03.90-7.fc42
 # Automatically populated when using docker buildx
 ARG TARGETPLATFORM
 ARG BUILDPLATFORM

From faa0f5f564ce441631bf963c1c752e0bb42c697b Mon Sep 17 00:00:00 2001
From: Ram Lavi <ralavi@redhat.com>
Date: Tue, 27 Aug 2024 13:43:14 +0300
Subject: [PATCH 21/48] contrib/kind-common: Patch passt binding to kubevirt CR

When deploying the kind cluster, in order to allow running VMs with
primary-UDN, the kubevirt CR is patched with:
- NetworkBindingPlugins feature gate.
- the passt network binding

Signed-off-by: Ram Lavi <ralavi@redhat.com>
---
 contrib/kind-common | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/contrib/kind-common b/contrib/kind-common
index fe7dc3e6d7e..8934ec475c6 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -349,6 +349,11 @@ install_kubevirt() {
             kubectl logs --all-containers=true -n kubevirt $p || true
         done
     fi
+
+    kubectl -n kubevirt patch kubevirt kubevirt --type=json --patch '[{"op":"add","path":"/spec/configuration/developerConfiguration","value":{"featureGates":[]}},{"op":"add","path":"/spec/configuration/developerConfiguration/featureGates/-","value":"NetworkBindingPlugins"}]'
+    
+    local passt_binding_image="quay.io/kubevirt/network-passt-binding:${kubevirt_version}"
+    kubectl -n kubevirt patch kubevirt kubevirt --type=json --patch '[{"op":"add","path":"/spec/configuration/network","value":{}},{"op":"add","path":"/spec/configuration/network/binding","value":{"passt":{"computeResourceOverhead":{"requests":{"memory":"500Mi"}},"migration":{"method":"link-refresh"},"networkAttachmentDefinition":"default/primary-udn-kubevirt-binding","sidecarImage":"'"${passt_binding_image}"'"}}}]'
     
     if [ ! -d "./bin" ]
     then

From 7cf7c4e420129f495b9956f853c651de798642a6 Mon Sep 17 00:00:00 2001
From: Ram Lavi <ralavi@redhat.com>
Date: Tue, 27 Aug 2024 15:26:06 +0300
Subject: [PATCH 22/48] contrib/kind-common: Add primary-udn NAD

Co-authored-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
Signed-off-by: Ram Lavi <ralavi@redhat.com>
---
 contrib/kind-common | 21 +++++++++++++++++++++
 contrib/kind.sh     |  1 +
 2 files changed, 22 insertions(+)

diff --git a/contrib/kind-common b/contrib/kind-common
index 8934ec475c6..670193255f5 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -582,3 +582,24 @@ kubectl_wait_dnsnameresolver_pods() {
   echo "Waiting for pods in dnsnameresolver-operator namespace to become ready (timeout ${timeout})..."
   kubectl wait -n dnsnameresolver-operator --for=condition=ready pods --all --timeout=${timeout}s
 }
+
+deploy_kubevirt_binding() {
+  cat <<EOF | run_kubectl apply -f -
+---
+apiVersion: "k8s.cni.cncf.io/v1"
+kind: NetworkAttachmentDefinition
+metadata:
+  name: primary-udn-kubevirt-binding
+  namespace: default
+spec:
+  config: '{
+  "cniVersion": "1.0.0",
+  "name": "primary-udn-kubevirt-binding",
+  "plugins": [
+    {
+      "type": "network-passt-binding"
+    }
+  ]
+}'
+EOF
+}
diff --git a/contrib/kind.sh b/contrib/kind.sh
index 469e1191c53..4215037d708 100755
--- a/contrib/kind.sh
+++ b/contrib/kind.sh
@@ -1163,5 +1163,6 @@ if [ "$KIND_INSTALL_PLUGINS" == true ]; then
 fi
 if [ "$KIND_INSTALL_KUBEVIRT" == true ]; then
   install_kubevirt
+  deploy_kubevirt_binding
   install_kubevirt_ipam_controller
 fi

From 4a028e9a8556c35fa6d8432259d9a55aecaaacc2 Mon Sep 17 00:00:00 2001
From: Ram Lavi <ralavi@redhat.com>
Date: Tue, 27 Aug 2024 16:12:35 +0300
Subject: [PATCH 23/48] contrib/kind-common: Add primary-udn passt binary

Signed-off-by: Ram Lavi <ralavi@redhat.com>
---
 contrib/kind-common | 8 ++++++++
 contrib/kind.sh     | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/contrib/kind-common b/contrib/kind-common
index 670193255f5..9a627322a12 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -603,3 +603,11 @@ spec:
 }'
 EOF
 }
+
+deploy_passt_binary() {
+  echo "Installing passt-binding-cni-ds ..."
+  local manifest="https://raw.githubusercontent.com/kubevirt/ipam-extensions/main/passt/passt-binding-cni-ds.yaml"
+  run_kubectl apply -f "$manifest"
+
+  run_kubectl rollout status -n kube-system daemonset/passt-binding-cni --timeout 2m
+}
diff --git a/contrib/kind.sh b/contrib/kind.sh
index 4215037d708..471471c3cd1 100755
--- a/contrib/kind.sh
+++ b/contrib/kind.sh
@@ -1164,5 +1164,7 @@ fi
 if [ "$KIND_INSTALL_KUBEVIRT" == true ]; then
   install_kubevirt
   deploy_kubevirt_binding
+  deploy_passt_binary
+
   install_kubevirt_ipam_controller
 fi

From 02ce6f2809ad27e2584a9a5090b5debfe02e1348 Mon Sep 17 00:00:00 2001
From: Ram Lavi <ralavi@redhat.com>
Date: Wed, 28 Aug 2024 20:37:52 +0300
Subject: [PATCH 24/48] contrib/kind: separate cert-manager and kubevirt-ipam
 installations

Separating two different installations into different functions.
In future commit this will allow deploying kubevirt-ipam separately when
needed.

Signed-off-by: Ram Lavi <ralavi@redhat.com>
---
 contrib/kind-common | 4 +++-
 contrib/kind.sh     | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/contrib/kind-common b/contrib/kind-common
index 9a627322a12..55741a9833a 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -379,12 +379,14 @@ install_kubevirt() {
     chmod +x ./bin/virtctl
 }
 
-install_kubevirt_ipam_controller() {
+install_cert_manager() {
   local cert_manager_version="v1.14.4"
   echo "Installing cert-manager ..."
   manifest="https://github.com/cert-manager/cert-manager/releases/download/${cert_manager_version}/cert-manager.yaml"
   run_kubectl apply -f "$manifest"
+}
 
+install_kubevirt_ipam_controller() {
   echo "Installing KubeVirt IPAM controller manager ..."
   manifest="https://raw.githubusercontent.com/kubevirt/ipam-extensions/main/dist/install.yaml"
   run_kubectl apply -f "$manifest"
diff --git a/contrib/kind.sh b/contrib/kind.sh
index 471471c3cd1..e1554e80a8b 100755
--- a/contrib/kind.sh
+++ b/contrib/kind.sh
@@ -1166,5 +1166,6 @@ if [ "$KIND_INSTALL_KUBEVIRT" == true ]; then
   deploy_kubevirt_binding
   deploy_passt_binary
 
+  install_cert_manager
   install_kubevirt_ipam_controller
 fi

From 5b3eacd56062e5275dfc10c4c2dee1a2e80d7254 Mon Sep 17 00:00:00 2001
From: Ram Lavi <ralavi@redhat.com>
Date: Thu, 29 Aug 2024 14:53:40 +0300
Subject: [PATCH 25/48] contrib/kind: Add kubevirt-ipam opt out flag

Although they usually deployed together, ipam may sometimes need to be
deployed out of band for dev purposes.
For this purpose, introducing an opt-out flag that will prevent
installing the latest ipam-controller while still installing
cert-manager.

Signed-off-by: Ram Lavi <ralavi@redhat.com>
---
 contrib/kind.sh | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/contrib/kind.sh b/contrib/kind.sh
index e1554e80a8b..db7355b2784 100755
--- a/contrib/kind.sh
+++ b/contrib/kind.sh
@@ -163,6 +163,8 @@ parse_args() {
                                                 ;;
             -ikv | --install-kubevirt)          KIND_INSTALL_KUBEVIRT=true
                                                 ;;
+            -nokvipam | --opt-out-kv-ipam)      KIND_OPT_OUT_KUBEVIRT_IPAM=true
+                                                ;;
             -ha | --ha-enabled )                OVN_HA=true
                                                 ;;
             -me | --multicast-enabled)          OVN_MULTICAST_ENABLE=true
@@ -350,6 +352,7 @@ print_params() {
      echo "KIND_INSTALL_METALLB = $KIND_INSTALL_METALLB"
      echo "KIND_INSTALL_PLUGINS = $KIND_INSTALL_PLUGINS"
      echo "KIND_INSTALL_KUBEVIRT = $KIND_INSTALL_KUBEVIRT"
+     echo "KIND_OPT_OUT_KUBEVIRT_IPAM = $KIND_OPT_OUT_KUBEVIRT_IPAM"
      echo "OVN_HA = $OVN_HA"
      echo "RUN_IN_CONTAINER = $RUN_IN_CONTAINER"
      echo "KIND_CLUSTER_NAME = $KIND_CLUSTER_NAME"
@@ -503,6 +506,7 @@ set_default_params() {
   KIND_INSTALL_METALLB=${KIND_INSTALL_METALLB:-false}
   KIND_INSTALL_PLUGINS=${KIND_INSTALL_PLUGINS:-false}
   KIND_INSTALL_KUBEVIRT=${KIND_INSTALL_KUBEVIRT:-false}
+  KIND_OPT_OUT_KUBEVIRT_IPAM=${KIND_OPT_OUT_KUBEVIRT_IPAM:-false}
   OVN_HA=${OVN_HA:-false}
   KIND_LOCAL_REGISTRY=${KIND_LOCAL_REGISTRY:-false}
   KIND_LOCAL_REGISTRY_NAME=${KIND_LOCAL_REGISTRY_NAME:-kind-registry}
@@ -1167,5 +1171,7 @@ if [ "$KIND_INSTALL_KUBEVIRT" == true ]; then
   deploy_passt_binary
 
   install_cert_manager
-  install_kubevirt_ipam_controller
+  if [ "$KIND_OPT_OUT_KUBEVIRT_IPAM" != true ]; then
+    install_kubevirt_ipam_controller
+  fi
 fi

From 4508f150ad8601b3aa3b6d12c962462397eae7d8 Mon Sep 17 00:00:00 2001
From: Miguel Duarte Barroso <mdbarroso@redhat.com>
Date: Thu, 22 Aug 2024 18:05:20 +0200
Subject: [PATCH 26/48] udn: set persistentIPs for UDN ifaces

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
---
 go-controller/pkg/util/multi_network.go  | 10 ++++++++++
 go-controller/pkg/util/pod_annotation.go |  3 +++
 2 files changed, 13 insertions(+)

diff --git a/go-controller/pkg/util/multi_network.go b/go-controller/pkg/util/multi_network.go
index e4710fbb26d..624ce5ee75d 100644
--- a/go-controller/pkg/util/multi_network.go
+++ b/go-controller/pkg/util/multi_network.go
@@ -954,8 +954,18 @@ func GetPodNADToNetworkMappingWithActiveNetwork(pod *kapi.Pod, nInfo NetInfo, ac
 		Name:      activeNetworkNADKey[1],
 	}
 
+	if nInfo.IsPrimaryNetwork() &&
+		nInfo.TopologyType() == types.Layer2Topology &&
+		nInfo.AllowsPersistentIPs() {
+		ipamClaimName, wasPersistentIPRequested := pod.Annotations[OvnUDNIPAMClaimName]
+		if wasPersistentIPRequested {
+			networkSelections[activeNetworkNADs[0]].IPAMClaimReference = ipamClaimName
+		}
+	}
+
 	return true, networkSelections, nil
 }
+
 func IsMultiNetworkPoliciesSupportEnabled() bool {
 	return config.OVNKubernetesFeature.EnableMultiNetwork && config.OVNKubernetesFeature.EnableMultiNetworkPolicy
 }
diff --git a/go-controller/pkg/util/pod_annotation.go b/go-controller/pkg/util/pod_annotation.go
index 12583871b96..7fc9e5dec93 100644
--- a/go-controller/pkg/util/pod_annotation.go
+++ b/go-controller/pkg/util/pod_annotation.go
@@ -51,6 +51,9 @@ const (
 	OvnPodAnnotationName = "k8s.ovn.org/pod-networks"
 	// DefNetworkAnnotation is the pod annotation for the cluster-wide default network
 	DefNetworkAnnotation = "v1.multus-cni.io/default-network"
+	// OvnUDNIPAMClaimName is used for workload owners to instruct OVN-K which
+	// IPAMClaim will hold the allocation for the workload
+	OvnUDNIPAMClaimName = "k8s.ovn.org/primary-udn-ipamclaim"
 )
 
 var ErrNoPodIPFound = errors.New("no pod IPs found")

From 813847c4c22e403decaf1ad9f406057a18409b49 Mon Sep 17 00:00:00 2001
From: Miguel Duarte Barroso <mdbarroso@redhat.com>
Date: Fri, 6 Sep 2024 12:34:26 +0200
Subject: [PATCH 27/48] udn: unit test persistent IPs integration

As a bonus add some coverage to the function that generates the syntetic
network selection element we use to request the primary UDN attachment.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
---
 go-controller/pkg/util/multi_network_test.go | 207 +++++++++++++++++++
 1 file changed, 207 insertions(+)

diff --git a/go-controller/pkg/util/multi_network_test.go b/go-controller/pkg/util/multi_network_test.go
index c5d7dca2fdf..30112e277d5 100644
--- a/go-controller/pkg/util/multi_network_test.go
+++ b/go-controller/pkg/util/multi_network_test.go
@@ -845,6 +845,213 @@ func TestGetPodNADToNetworkMapping(t *testing.T) {
 	}
 }
 
+func TestGetPodNADToNetworkMappingWithActiveNetwork(t *testing.T) {
+	const (
+		attachmentName = "attachment1"
+		namespaceName  = "ns1"
+		networkName    = "l3-network"
+	)
+
+	type testConfig struct {
+		desc                             string
+		inputNamespace                   string
+		inputNetConf                     *ovncnitypes.NetConf
+		inputPrimaryUDNConfig            *ovncnitypes.NetConf
+		inputPodAnnotations              map[string]string
+		expectedError                    error
+		expectedIsAttachmentRequested    bool
+		expectedNetworkSelectionElements map[string]*nadv1.NetworkSelectionElement
+	}
+
+	tests := []testConfig{
+		{
+			desc: "there isn't a primary UDN",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:  cnitypes.NetConf{Name: networkName},
+				Topology: ovntypes.Layer3Topology,
+				NADName:  GetNADName(namespaceName, attachmentName),
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, attachmentName),
+			},
+			expectedIsAttachmentRequested: true,
+			expectedNetworkSelectionElements: map[string]*nadv1.NetworkSelectionElement{
+				"ns1/attachment1": {
+					Name:      "attachment1",
+					Namespace: "ns1",
+				},
+			},
+		},
+		{
+			desc: "the netinfo is different from the active network",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:  cnitypes.NetConf{Name: networkName},
+				Topology: ovntypes.Layer3Topology,
+				NADName:  GetNADName(namespaceName, attachmentName),
+			},
+			inputPrimaryUDNConfig: &ovncnitypes.NetConf{
+				NetConf:  cnitypes.NetConf{Name: "another-network"},
+				Topology: ovntypes.Layer3Topology,
+				NADName:  GetNADName(namespaceName, "another-network"),
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, "another-network"),
+			},
+			expectedIsAttachmentRequested: false,
+		},
+		{
+			desc: "the network configuration for a primary layer2 UDN features allow persistent IPs but the pod does not request it",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPrimaryUDNConfig: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, "another-network"),
+			},
+			expectedIsAttachmentRequested: true,
+			expectedNetworkSelectionElements: map[string]*nadv1.NetworkSelectionElement{
+				"ns1/attachment1": {
+					Name:      "attachment1",
+					Namespace: "ns1",
+				},
+			},
+		},
+		{
+			desc: "the network configuration for a primary layer2 UDN features allow persistent IPs, and the pod requests it",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPrimaryUDNConfig: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, "another-network"),
+				OvnUDNIPAMClaimName:          "the-one-to-the-left-of-the-pony",
+			},
+			expectedIsAttachmentRequested: true,
+			expectedNetworkSelectionElements: map[string]*nadv1.NetworkSelectionElement{
+				"ns1/attachment1": {
+					Name:               "attachment1",
+					Namespace:          "ns1",
+					IPAMClaimReference: "the-one-to-the-left-of-the-pony",
+				},
+			},
+		},
+		{
+			desc: "the network configuration for a secondary layer2 UDN features allow persistent IPs and the pod requests it",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRoleSecondary,
+				AllowPersistentIPs: true,
+			},
+			inputPrimaryUDNConfig: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer2Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRoleSecondary,
+				AllowPersistentIPs: true,
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, "another-network"),
+			},
+			expectedIsAttachmentRequested: true,
+			expectedNetworkSelectionElements: map[string]*nadv1.NetworkSelectionElement{
+				"ns1/attachment1": {
+					Name:      "attachment1",
+					Namespace: "ns1",
+				},
+			},
+		},
+		{
+			desc: "the network configuration for a primary layer3 UDN features allow persistent IPs and the pod requests it",
+			inputNetConf: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer3Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPrimaryUDNConfig: &ovncnitypes.NetConf{
+				NetConf:            cnitypes.NetConf{Name: networkName},
+				Topology:           ovntypes.Layer3Topology,
+				NADName:            GetNADName(namespaceName, attachmentName),
+				Role:               ovntypes.NetworkRolePrimary,
+				AllowPersistentIPs: true,
+			},
+			inputPodAnnotations: map[string]string{
+				nadv1.NetworkAttachmentAnnot: GetNADName(namespaceName, "another-network"),
+				OvnUDNIPAMClaimName:          "the-one-to-the-left-of-the-pony",
+			},
+			expectedIsAttachmentRequested: true,
+			expectedNetworkSelectionElements: map[string]*nadv1.NetworkSelectionElement{
+				"ns1/attachment1": {
+					Name:      "attachment1",
+					Namespace: "ns1",
+				},
+			},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			g := gomega.NewWithT(t)
+			netInfo, err := NewNetInfo(test.inputNetConf)
+			g.Expect(err).To(gomega.BeNil())
+			if test.inputNetConf.NADName != "" {
+				netInfo.AddNADs(test.inputNetConf.NADName)
+			}
+
+			var primaryUDNNetInfo NetInfo
+			if test.inputPrimaryUDNConfig != nil {
+				primaryUDNNetInfo, err = NewNetInfo(test.inputPrimaryUDNConfig)
+				g.Expect(err).To(gomega.BeNil())
+				if test.inputPrimaryUDNConfig.NADName != "" {
+					primaryUDNNetInfo.AddNADs(test.inputPrimaryUDNConfig.NADName)
+				}
+			}
+
+			pod := &corev1.Pod{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:        "test-pod",
+					Namespace:   test.inputNamespace,
+					Annotations: test.inputPodAnnotations,
+				},
+			}
+
+			isAttachmentRequested, networkSelectionElements, err := GetPodNADToNetworkMappingWithActiveNetwork(
+				pod,
+				netInfo,
+				primaryUDNNetInfo,
+			)
+
+			if err != nil {
+				g.Expect(err).To(gomega.MatchError(test.expectedError))
+			}
+			g.Expect(isAttachmentRequested).To(gomega.Equal(test.expectedIsAttachmentRequested))
+			g.Expect(networkSelectionElements).To(gomega.Equal(test.expectedNetworkSelectionElements))
+		})
+	}
+}
+
 func TestSubnetOverlapCheck(t *testing.T) {
 	_, cidr4, _ := net.ParseCIDR("10.128.0.0/14")
 	_, cidr6, _ := net.ParseCIDR("fe00::/16")

From e712f386cb50eb2cb5f2e34bb9ee12eca8fa2a4f Mon Sep 17 00:00:00 2001
From: Enrique Llorente <ellorent@redhat.com>
Date: Mon, 2 Sep 2024 15:13:22 +0200
Subject: [PATCH 28/48] kubevirt, e2e: Pin kubevirt to 1.1.0

Signed-off-by: Enrique Llorente <ellorent@redhat.com>
---
 test/e2e/go.mod | 2 +-
 test/e2e/go.sum | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/test/e2e/go.mod b/test/e2e/go.mod
index 5f1ff01d8f3..8616bfb6ea3 100644
--- a/test/e2e/go.mod
+++ b/test/e2e/go.mod
@@ -175,7 +175,7 @@ require (
 	go.universe.tf/metallb v0.0.0-00010101000000-000000000000
 	google.golang.org/grpc v1.64.0
 	k8s.io/kubectl v0.30.2
-	kubevirt.io/api v1.0.0
+	kubevirt.io/api v1.1.0
 	sigs.k8s.io/controller-runtime v0.18.4
 )
 
diff --git a/test/e2e/go.sum b/test/e2e/go.sum
index 516edc9c91d..1e59df92126 100644
--- a/test/e2e/go.sum
+++ b/test/e2e/go.sum
@@ -1001,8 +1001,8 @@ k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
 k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
 k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-kubevirt.io/api v1.0.0 h1:RBdXP5CDhE0v5qL2OUQdrYyRrHe/F68Z91GWqBDF6nw=
-kubevirt.io/api v1.0.0/go.mod h1:CJ4vZsaWhVN3jNbyc9y3lIZhw8nUHbWjap0xHABQiqc=
+kubevirt.io/api v1.1.0 h1:G6/32Emr8cout4MUc2Gi3tfp92rq/rMh5LyhKhMxntw=
+kubevirt.io/api v1.1.0/go.mod h1:CJ4vZsaWhVN3jNbyc9y3lIZhw8nUHbWjap0xHABQiqc=
 kubevirt.io/containerized-data-importer-api v1.57.0-alpha1 h1:IWo12+ei3jltSN5jQN1xjgakfvRSF3G3Rr4GXVOOy2I=
 kubevirt.io/containerized-data-importer-api v1.57.0-alpha1/go.mod h1:Y/8ETgHS1GjO89bl682DPtQOYEU/1ctPFBz6Sjxm4DM=
 kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 h1:QMrd0nKP0BGbnxTqakhDZAUhGKxPiPiN5gSDqKUmGGc=

From 6e1f2d7562693ad487339781f6aff012f0a69897 Mon Sep 17 00:00:00 2001
From: Enrique Llorente <ellorent@redhat.com>
Date: Mon, 2 Sep 2024 15:40:02 +0200
Subject: [PATCH 29/48] kubevirt, e2e: UDN

Co-authored-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
Signed-off-by: Enrique Llorente <ellorent@redhat.com>
---
 test/e2e/kubevirt.go         | 159 +++++++++++++++++++++++++++++++----
 test/e2e/kubevirt/console.go |  10 ++-
 2 files changed, 149 insertions(+), 20 deletions(-)

diff --git a/test/e2e/kubevirt.go b/test/e2e/kubevirt.go
index 470e236b932..44fc9c6e100 100644
--- a/test/e2e/kubevirt.go
+++ b/test/e2e/kubevirt.go
@@ -14,6 +14,7 @@ import (
 	. "github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
 
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
 	"github.com/ovn-org/ovn-kubernetes/test/e2e/diagnostics"
 	"github.com/ovn-org/ovn-kubernetes/test/e2e/kubevirt"
 
@@ -32,6 +33,7 @@ import (
 	"k8s.io/client-go/util/retry"
 	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
 	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
+	testutils "k8s.io/kubernetes/test/utils"
 	utilnet "k8s.io/utils/net"
 	"k8s.io/utils/pointer"
 	crclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -298,16 +300,30 @@ var _ = Describe("Kubevirt Virtual Machines", func() {
 			}
 		}
 
-		httpServerTestPodsMultusNetworkIPs = func(netName string) map[string][]string {
+		checkPodRunningReady = func() func(Gomega, *corev1.Pod) {
+			return func(g Gomega, pod *corev1.Pod) {
+				ok, err := testutils.PodRunningReady(pod)
+				Expect(err).ToNot(HaveOccurred())
+				Expect(ok).To(BeTrue())
+			}
+		}
+
+		httpServerTestPodsMultusNetworkIPs = func(nadName string) map[string][]string {
 			GinkgoHelper()
 			ips := map[string][]string{}
 			for _, pod := range httpServerTestPods {
-				netStatus, err := podNetworkStatus(pod, func(status nadapi.NetworkStatus) bool {
-					return status.Name == netName
-				})
-				Expect(err).NotTo(HaveOccurred())
-				Expect(netStatus).To(HaveLen(1))
-				ips[pod.Name] = append(ips[pod.Name], netStatus[0].IPs...)
+				var ovnPodAnnotation *util.PodAnnotation
+				Eventually(func() (*util.PodAnnotation, error) {
+					var err error
+					ovnPodAnnotation, err = util.UnmarshalPodAnnotation(pod.Annotations, nadName)
+					return ovnPodAnnotation, err
+				}).
+					WithTimeout(5 * time.Second).
+					WithPolling(200 * time.Millisecond).
+					ShouldNot(BeNil())
+				for _, ipnet := range ovnPodAnnotation.IPs {
+					ips[pod.Name] = append(ips[pod.Name], ipnet.IP.String())
+				}
 			}
 			return ips
 		}
@@ -635,6 +651,19 @@ var _ = Describe("Kubevirt Virtual Machines", func() {
 			return addresses
 		}
 
+		virtualMachineAddressesFromGuest = func(vmi *kubevirtv1.VirtualMachineInstance) []string {
+			GinkgoHelper()
+			addresses := waitVirtualMachineAddresses(vmi)
+			ips := []string{}
+			for _, address := range addresses {
+				if net.ParseIP(address.Ip).IsLinkLocalUnicast() {
+					continue
+				}
+				ips = append(ips, address.Ip)
+			}
+			return ips
+		}
+
 		fcosVMI = func(idx int, labels map[string]string, annotations map[string]string, nodeSelector map[string]string, networkSource kubevirtv1.NetworkSource, butane string) (*kubevirtv1.VirtualMachineInstance, error) {
 			workingDirectory, err := os.Getwd()
 			if err != nil {
@@ -1097,7 +1126,7 @@ passwd:
 			}),
 		)
 	})
-	Context("with secondary network and persistent ips configured", func() {
+	Context("with user defined networks and persistent ips configured", func() {
 		type testCommand struct {
 			description string
 			cmd         func()
@@ -1156,6 +1185,22 @@ passwd:
 				},
 			}
 
+			virtualMachineWithUDN = resourceCommand{
+				description: "VirtualMachine with interface binding for UDN",
+				cmd: func() string {
+					var err error
+					vm, err = fcosVM(1, nil /*labels*/, nil /*annotations*/, nil, /*nodeSelector*/
+						kubevirtv1.NetworkSource{
+							Pod: &kubevirtv1.PodNetwork{},
+						}, butane)
+					Expect(err).ToNot(HaveOccurred())
+					vm.Spec.Template.Spec.Domain.Devices.Interfaces[0].Bridge = nil
+					vm.Spec.Template.Spec.Domain.Devices.Interfaces[0].Binding = &kubevirtv1.PluginBinding{Name: "passt"}
+					createVirtualMachine(vm)
+					return vm.Name
+				},
+			}
+
 			virtualMachineInstance = resourceCommand{
 				description: "VirtualMachineInstance",
 				cmd: func() string {
@@ -1170,6 +1215,22 @@ passwd:
 					return vmi.Name
 				},
 			}
+
+			virtualMachineInstanceWithUDN = resourceCommand{
+				description: "VirtualMachineInstance with interface binding for UDN",
+				cmd: func() string {
+					var err error
+					vmi, err = fcosVMI(1, nil /*labels*/, nil /*annotations*/, nil, /*nodeSelector*/
+						kubevirtv1.NetworkSource{
+							Pod: &kubevirtv1.PodNetwork{},
+						}, butane)
+					Expect(err).ToNot(HaveOccurred())
+					vmi.Spec.Domain.Devices.Interfaces[0].Bridge = nil
+					vmi.Spec.Domain.Devices.Interfaces[0].Binding = &kubevirtv1.PluginBinding{Name: "passt"}
+					createVirtualMachineInstance(vmi)
+					return vmi.Name
+				},
+			}
 			filterOutIPv6 = func(ips map[string][]string) map[string][]string {
 				filteredOutIPs := map[string][]string{}
 				for podName, podIPs := range ips {
@@ -1191,6 +1252,7 @@ passwd:
 			resource    resourceCommand
 			test        testCommand
 			topology    string
+			role        string
 		}
 		DescribeTable("should keep ip", func(td testData) {
 			netConfig := newNetworkAttachmentConfig(
@@ -1198,8 +1260,9 @@ passwd:
 					namespace:          namespace,
 					name:               "net1",
 					topology:           td.topology,
-					cidr:               strings.Join([]string{cidrIPv4, cidrIPv6}, ","),
+					cidr:               correctCIDRFamily(cidrIPv4, cidrIPv6),
 					allowPersistentIPs: true,
+					role:               td.role,
 				})
 
 			if td.topology == "localnet" {
@@ -1222,9 +1285,19 @@ passwd:
 			Expect(err).ToNot(HaveOccurred())
 			selectedNodes = workerNodeList.Items
 			networkName := fmt.Sprintf("%s/%s", nad.Namespace, nad.Name)
-			prepareHTTPServerPods(map[string]string{
-				"k8s.v1.cni.cncf.io/networks": fmt.Sprintf(`[{"name": %q}]`, nad.Name),
-			}, checkPodHasIPsAtNetwork(networkName, 2 /*expectedNumberOfAddresses*/))
+			httpServerPodsAnnotations := map[string]string{}
+			if td.role != "primary" {
+				httpServerPodsAnnotations["k8s.v1.cni.cncf.io/networks"] = fmt.Sprintf(`[{"name": %q}]`, nad.Name)
+			}
+			var httpServerPodCondition func(Gomega, *corev1.Pod)
+			if td.role != "primary" {
+				expectedNumberOfAddresses := len(strings.Split(netConfig.cidr, ","))
+				httpServerPodCondition = checkPodHasIPsAtNetwork(networkName, expectedNumberOfAddresses)
+			} else {
+				httpServerPodCondition = checkPodRunningReady()
+			}
+
+			prepareHTTPServerPods(httpServerPodsAnnotations, httpServerPodCondition)
 
 			vmiName := td.resource.cmd()
 			vmi = &kubevirtv1.VirtualMachineInstance{
@@ -1237,12 +1310,30 @@ passwd:
 			Expect(crClient.Get(context.TODO(), crclient.ObjectKeyFromObject(vmi), vmi)).To(Succeed())
 
 			step := by(vmi.Name, "Login to virtual machine for the first time")
-			Expect(kubevirt.LoginToFedora(vmi, "core", "fedora")).To(Succeed(), step)
-			expectedAddreses = virtualMachineAddressesFromStatus(vmi, 2 /*two addresses, dual stack*/)
+			Eventually(func() error {
+				if td.role != "primary" {
+					return kubevirt.LoginToFedora(vmi, "core", "fedora")
+				} else {
+					return kubevirt.LoginToFedoraWithHostname(vmi, "core", "fedora", "localhost")
+				}
+			}).
+				WithTimeout(5*time.Second).
+				WithPolling(time.Second).
+				Should(Succeed(), step)
+
+			step = by(vmi.Name, "Wait for addresses at the virtual machine")
+			if td.role != "primary" {
+				// expect 2 addresses on dual-stack deployments; 1 on single-stack
+				expectedNumberOfAddresses := len(strings.Split(netConfig.cidr, ","))
+				expectedAddreses = virtualMachineAddressesFromStatus(vmi, expectedNumberOfAddresses)
+			} else {
+				expectedAddreses = virtualMachineAddressesFromGuest(vmi)
+			}
 
 			step = by(vmi.Name, fmt.Sprintf("Check east/west traffic before %s %s", td.resource.description, td.test.description))
 			testPodsIPs := httpServerTestPodsMultusNetworkIPs(networkName)
 
+			//TODO: We do support it with primary, since passt do support it
 			// kubevirt secondary IPAM do not support IPv6, so guest is not
 			// going to have an ipv6 address at the interface
 			testPodsIPs = filterOutIPv6(testPodsIPs)
@@ -1253,15 +1344,31 @@ passwd:
 			td.test.cmd()
 
 			step = by(vm.Name, fmt.Sprintf("Login to virtual machine after %s %s", td.resource.description, td.test.description))
-			Expect(kubevirt.LoginToFedora(vmi, "core", "fedora")).To(Succeed(), step)
-			obtainedAddresses := virtualMachineAddressesFromStatus(vmi, 2 /*two addresses, dual stack*/)
+			if td.role != "primary" {
+				Expect(kubevirt.LoginToFedora(vmi, "core", "fedora")).To(Succeed(), step)
+			} else {
+				Expect(kubevirt.LoginToFedoraWithHostname(vmi, "core", "fedora", "localhost")).To(Succeed(), step)
+			}
+			var obtainedAddresses []string
+
+			if td.role != "primary" { // expect 2 addresses on dual-stack deployments; 1 on single-stack
+				expectedNumberOfAddresses := len(strings.Split(netConfig.cidr, ","))
+				obtainedAddresses = virtualMachineAddressesFromStatus(vmi, expectedNumberOfAddresses)
+			} else {
+				obtainedAddresses = virtualMachineAddressesFromGuest(vmi)
+			}
+
 			Expect(obtainedAddresses).To(Equal(expectedAddreses))
 
 			step = by(vmi.Name, fmt.Sprintf("Check east/west traffic after %s %s", td.resource.description, td.test.description))
 			checkEastWestTraffic(vmi, testPodsIPs, step)
 		},
 			func(td testData) string {
-				return fmt.Sprintf("after %s of %s with %s", td.test.description, td.resource.description, td.topology)
+				role := "secondary"
+				if td.role != "" {
+					role = td.role
+				}
+				return fmt.Sprintf("after %s of %s with %s/%s", td.test.description, td.resource.description, role, td.topology)
 			},
 			Entry(nil, testData{
 				resource: virtualMachine,
@@ -1273,6 +1380,12 @@ passwd:
 				test:     restart,
 				topology: "layer2",
 			}),
+			Entry(nil, testData{
+				resource: virtualMachineWithUDN,
+				test:     restart,
+				topology: "layer2",
+				role:     "primary",
+			}),
 			Entry(nil, testData{
 				resource: virtualMachine,
 				test:     liveMigrate,
@@ -1283,6 +1396,12 @@ passwd:
 				test:     liveMigrate,
 				topology: "layer2",
 			}),
+			Entry(nil, testData{
+				resource: virtualMachineWithUDN,
+				test:     liveMigrate,
+				topology: "layer2",
+				role:     "primary",
+			}),
 			Entry(nil, testData{
 				resource: virtualMachineInstance,
 				test:     liveMigrate,
@@ -1293,6 +1412,12 @@ passwd:
 				test:     liveMigrate,
 				topology: "layer2",
 			}),
+			Entry(nil, testData{
+				resource: virtualMachineInstanceWithUDN,
+				test:     liveMigrate,
+				topology: "layer2",
+				role:     "primary",
+			}),
 		)
 	})
 })
diff --git a/test/e2e/kubevirt/console.go b/test/e2e/kubevirt/console.go
index a8ee71c4421..822bd041620 100644
--- a/test/e2e/kubevirt/console.go
+++ b/test/e2e/kubevirt/console.go
@@ -182,8 +182,12 @@ func expectBatchWithValidatedSend(expecter expect.Expecter, batch []expect.Batch
 	return res, err
 }
 
-// LoginToFedora performs a console login to a Fedora base VM
 func LoginToFedora(vmi *kubevirtv1.VirtualMachineInstance, user, password string) error {
+	return LoginToFedoraWithHostname(vmi, user, password, vmi.Name)
+}
+
+// LoginToFedora performs a console login to a Fedora base VM
+func LoginToFedoraWithHostname(vmi *kubevirtv1.VirtualMachineInstance, user, password, hostname string) error {
 	expecter, _, err := newExpecter(vmi, consoleConnectionTimeout, expect.Verbose(true), expect.VerboseWriter(GinkgoWriter))
 	if err != nil {
 		return err
@@ -197,7 +201,7 @@ func LoginToFedora(vmi *kubevirtv1.VirtualMachineInstance, user, password string
 
 	// Do not login, if we already logged in
 	loggedInPromptRegex := fmt.Sprintf(
-		`(\[%s@%s ~\]\$ |\[root@%s %s\]\# )`, user, vmi.Name, vmi.Name, user,
+		`(\[%s@%s ~\]\$ |\[root@%s %s\]\# )`, user, hostname, hostname, user,
 	)
 	b := []expect.Batcher{
 		&expect.BSnd{S: "\n"},
@@ -215,7 +219,7 @@ func LoginToFedora(vmi *kubevirtv1.VirtualMachineInstance, user, password string
 			&expect.Case{
 				// Using only "login: " would match things like "Last failed login: Tue Jun  9 22:25:30 UTC 2020 on ttyS0"
 				// and in case the VM's did not get hostname form DHCP server try the default hostname
-				R:  regexp.MustCompile(fmt.Sprintf(`%s login: `, vmi.Name)),
+				R:  regexp.MustCompile(fmt.Sprintf(`%s login: `, hostname)),
 				S:  user + "\n",
 				T:  expect.Next(),
 				Rt: 10,

From 725eb873588a59b8e3a3d0d0f9b93e9e983fc490 Mon Sep 17 00:00:00 2001
From: Enrique Llorente <ellorent@redhat.com>
Date: Fri, 6 Sep 2024 14:42:43 +0200
Subject: [PATCH 30/48] gh, actions: activate net-seg for kv-live-migration

Signed-off-by: Enrique Llorente <ellorent@redhat.com>
---
 .github/workflows/test.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 950fdd84f87..38b180183bd 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -451,7 +451,7 @@ jobs:
       KIND_IPV4_SUPPORT: "${{ matrix.ipfamily == 'IPv4' || matrix.ipfamily == 'dualstack' }}"
       KIND_IPV6_SUPPORT: "${{ matrix.ipfamily == 'IPv6' || matrix.ipfamily == 'dualstack' }}"
       ENABLE_MULTI_NET: "${{ matrix.target == 'multi-homing' || matrix.target == 'kv-live-migration' || matrix.target == 'network-segmentation' || matrix.target == 'tools' || matrix.target == 'multi-homing-helm' }}"
-      ENABLE_NETWORK_SEGMENTATION: "${{ matrix.target == 'network-segmentation' || matrix.target == 'tools'}}"
+      ENABLE_NETWORK_SEGMENTATION: "${{ matrix.target == 'network-segmentation' || matrix.target == 'tools' || matrix.target == 'kv-live-migration'}}"
       KIND_INSTALL_KUBEVIRT: "${{ matrix.target == 'kv-live-migration' }}"
       OVN_COMPACT_MODE: "${{ matrix.target == 'compact-mode' }}"
       OVN_DUMMY_GATEWAY_BRIDGE: "${{ matrix.target == 'compact-mode' }}"

From 8fae3ea62eb885b23e8f46fd5c727bb9f229f4b8 Mon Sep 17 00:00:00 2001
From: Miguel Duarte Barroso <mdbarroso@redhat.com>
Date: Mon, 9 Sep 2024 16:27:53 +0200
Subject: [PATCH 31/48] persistentips, util: add AllowsPersistentIPs helper

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
---
 .../network_cluster_controller.go              |  3 +--
 .../ovn/base_network_controller_secondary.go   |  3 +--
 go-controller/pkg/util/multi_network.go        | 18 +++++++++++++++---
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/go-controller/pkg/clustermanager/network_cluster_controller.go b/go-controller/pkg/clustermanager/network_cluster_controller.go
index 0d244a8b721..67023e3678c 100644
--- a/go-controller/pkg/clustermanager/network_cluster_controller.go
+++ b/go-controller/pkg/clustermanager/network_cluster_controller.go
@@ -140,9 +140,8 @@ func (ncc *networkClusterController) hasNodeAllocation() bool {
 
 func (ncc *networkClusterController) allowPersistentIPs() bool {
 	return config.OVNKubernetesFeature.EnablePersistentIPs &&
-		ncc.NetInfo.AllowsPersistentIPs() &&
 		util.DoesNetworkRequireIPAM(ncc.NetInfo) &&
-		(ncc.NetInfo.TopologyType() == types.Layer2Topology || ncc.NetInfo.TopologyType() == types.LocalnetTopology)
+		util.AllowsPersistentIPs(ncc.NetInfo)
 }
 
 func (ncc *networkClusterController) init() error {
diff --git a/go-controller/pkg/ovn/base_network_controller_secondary.go b/go-controller/pkg/ovn/base_network_controller_secondary.go
index 19df223ecd9..7a4520cc47b 100644
--- a/go-controller/pkg/ovn/base_network_controller_secondary.go
+++ b/go-controller/pkg/ovn/base_network_controller_secondary.go
@@ -773,9 +773,8 @@ func (bsnc *BaseSecondaryNetworkController) WatchIPAMClaims() error {
 
 func (oc *BaseSecondaryNetworkController) allowPersistentIPs() bool {
 	return config.OVNKubernetesFeature.EnablePersistentIPs &&
-		oc.NetInfo.AllowsPersistentIPs() &&
 		util.DoesNetworkRequireIPAM(oc.NetInfo) &&
-		(oc.NetInfo.TopologyType() == types.Layer2Topology || oc.NetInfo.TopologyType() == types.LocalnetTopology)
+		util.AllowsPersistentIPs(oc.NetInfo)
 }
 
 func (oc *BaseSecondaryNetworkController) getNetworkID() (int, error) {
diff --git a/go-controller/pkg/util/multi_network.go b/go-controller/pkg/util/multi_network.go
index 624ce5ee75d..a33097bda68 100644
--- a/go-controller/pkg/util/multi_network.go
+++ b/go-controller/pkg/util/multi_network.go
@@ -954,9 +954,7 @@ func GetPodNADToNetworkMappingWithActiveNetwork(pod *kapi.Pod, nInfo NetInfo, ac
 		Name:      activeNetworkNADKey[1],
 	}
 
-	if nInfo.IsPrimaryNetwork() &&
-		nInfo.TopologyType() == types.Layer2Topology &&
-		nInfo.AllowsPersistentIPs() {
+	if nInfo.IsPrimaryNetwork() && AllowsPersistentIPs(nInfo) {
 		ipamClaimName, wasPersistentIPRequested := pod.Annotations[OvnUDNIPAMClaimName]
 		if wasPersistentIPRequested {
 			networkSelections[activeNetworkNADs[0]].IPAMClaimReference = ipamClaimName
@@ -982,3 +980,17 @@ func DoesNetworkRequireTunnelIDs(netInfo NetInfo) bool {
 	// Layer2Topology with IC require that we allocate tunnel IDs for each pod
 	return netInfo.TopologyType() == types.Layer2Topology && config.OVNKubernetesFeature.EnableInterconnect
 }
+
+func AllowsPersistentIPs(netInfo NetInfo) bool {
+	switch {
+	case netInfo.IsPrimaryNetwork():
+		return netInfo.TopologyType() == types.Layer2Topology && netInfo.AllowsPersistentIPs()
+
+	case netInfo.IsSecondary():
+		return (netInfo.TopologyType() == types.Layer2Topology || netInfo.TopologyType() == types.LocalnetTopology) &&
+			netInfo.AllowsPersistentIPs()
+
+	default:
+		return false
+	}
+}

From 309c32f7142e9bfc8e782ab224182c9cd0cfa7d8 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Wed, 11 Sep 2024 12:10:31 +0200
Subject: [PATCH 32/48] UDN: L3: Use nodesubnet annotations for L3

We were setting the hostSubnet as the clusterSubnet
for UDN L3 which was creating wrong routes in ovn
cluster router for UDN

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../pkg/ovn/secondary_layer3_network_controller.go        | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller.go b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
index 71b3f476c3f..878e18e8215 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
@@ -898,10 +898,10 @@ func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) (
 		hostAddrs = append(hostAddrs, externalIP.String())
 	}
 
-	// Use the host subnets present in the network attachment definition.
-	hostSubnets := make([]*net.IPNet, 0, len(oc.Subnets()))
-	for _, subnet := range oc.Subnets() {
-		hostSubnets = append(hostSubnets, subnet.CIDR)
+	// Fetch the host subnets present in the node annotation for this network
+	hostSubnets, err := util.ParseNodeHostSubnetAnnotation(node, oc.GetNetworkName())
+	if err != nil {
+		return nil, fmt.Errorf("failed to get node %q subnet annotation for network %q: %v", node.Name, oc.GetNetworkName(), err)
 	}
 
 	gwLRPIPs, err := util.ParseNodeGatewayRouterJoinAddrs(node, oc.GetNetworkName())

From 97b4808f294941208ed055846c11de83f7bad267 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Thu, 12 Sep 2024 12:41:12 +0200
Subject: [PATCH 33/48] UDN: L3: Use clustersubnets for GR routes

Since hostSubnets was getting feeded as clusterSubnet
when I fixed the hostSubnet in the previous commit
we started to break GR routes Let's also fix that back up.

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../secondary_layer3_network_controller.go    | 30 ++++++++++++-------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller.go b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
index 878e18e8215..a27428dd7b1 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
@@ -682,7 +682,7 @@ func (oc *SecondaryLayer3NetworkController) addUpdateLocalNodeEvent(node *kapi.N
 					gwConfig.config,
 					gwConfig.hostSubnets,
 					gwConfig.hostAddrs,
-					gwConfig.hostSubnets,
+					gwConfig.clusterSubnets,
 					gwConfig.gwLRPIPs,
 					oc.SCTPSupport,
 					oc.ovnClusterLRPToJoinIfAddrs,
@@ -858,11 +858,12 @@ func (oc *SecondaryLayer3NetworkController) gatherJoinSwitchIPs() error {
 }
 
 type SecondaryL3GatewayConfig struct {
-	config      *util.L3GatewayConfig
-	hostSubnets []*net.IPNet
-	gwLRPIPs    []*net.IPNet
-	hostAddrs   []string
-	externalIPs []net.IP
+	config         *util.L3GatewayConfig
+	hostSubnets    []*net.IPNet
+	clusterSubnets []*net.IPNet
+	gwLRPIPs       []*net.IPNet
+	hostAddrs      []string
+	externalIPs    []net.IP
 }
 
 func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) (*SecondaryL3GatewayConfig, error) {
@@ -898,6 +899,12 @@ func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) (
 		hostAddrs = append(hostAddrs, externalIP.String())
 	}
 
+	// Use the cluster subnets present in the network attachment definition.
+	clusterSubnets := make([]*net.IPNet, 0, len(oc.Subnets()))
+	for _, subnet := range oc.Subnets() {
+		clusterSubnets = append(clusterSubnets, subnet.CIDR)
+	}
+
 	// Fetch the host subnets present in the node annotation for this network
 	hostSubnets, err := util.ParseNodeHostSubnetAnnotation(node, oc.GetNetworkName())
 	if err != nil {
@@ -913,11 +920,12 @@ func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) (
 	l3GatewayConfig.InterfaceID = oc.GetNetworkScopedExtPortName(l3GatewayConfig.BridgeID, node.Name)
 
 	return &SecondaryL3GatewayConfig{
-		config:      l3GatewayConfig,
-		hostSubnets: hostSubnets,
-		gwLRPIPs:    gwLRPIPs,
-		hostAddrs:   hostAddrs,
-		externalIPs: externalIPs,
+		config:         l3GatewayConfig,
+		hostSubnets:    hostSubnets,
+		clusterSubnets: clusterSubnets,
+		gwLRPIPs:       gwLRPIPs,
+		hostAddrs:      hostAddrs,
+		externalIPs:    externalIPs,
 	}, nil
 }
 

From 961b5df39c5109ba5c423a4b1e6d8c51672ac9a7 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Fri, 13 Sep 2024 12:29:35 +0200
Subject: [PATCH 34/48] Fix unit tests to be more explicit

UTs were silently translating /16 to /24
which was not correct. Let's make the
L3 tests pass the nodesubnet in as well
to atleast make it more transparent
which is what.

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 go-controller/pkg/ovn/multihoming_test.go     | 22 ++---
 ...econdary_layer2_network_controller_test.go | 20 ++--
 ...econdary_layer3_network_controller_test.go | 99 +++++++++----------
 3 files changed, 69 insertions(+), 72 deletions(-)

diff --git a/go-controller/pkg/ovn/multihoming_test.go b/go-controller/pkg/ovn/multihoming_test.go
index ee67dc1e6ed..a0d31b3ebe9 100644
--- a/go-controller/pkg/ovn/multihoming_test.go
+++ b/go-controller/pkg/ovn/multihoming_test.go
@@ -19,6 +19,7 @@ import (
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/kube"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
+	ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
 	libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb"
 	ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
@@ -122,14 +123,13 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is
 			if !ok {
 				continue
 			}
-
-			subnets := ocInfo.bnc.Subnets()
+			subnets := podInfo.nodeSubnet
 			var (
-				subnet     config.CIDRNetworkEntry
+				subnet     *net.IPNet
 				hasSubnets bool
 			)
 			if len(subnets) > 0 {
-				subnet = subnets[0]
+				subnet = ovntest.MustParseIPNet(subnets)
 				hasSubnets = true
 			}
 
@@ -154,7 +154,7 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is
 				switch ocInfo.bnc.TopologyType() {
 				case ovntypes.Layer3Topology:
 					switchName = ocInfo.bnc.GetNetworkScopedName(pod.nodeName)
-					managementIP := managementPortIP(subnet.CIDR)
+					managementIP := managementPortIP(subnet)
 
 					switchToRouterPortName := "stor-" + switchName
 					switchToRouterPortUUID := switchToRouterPortName + "-UUID"
@@ -176,7 +176,7 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is
 					}
 				case ovntypes.Layer2Topology:
 					switchName = ocInfo.bnc.GetNetworkScopedName(ovntypes.OVNLayer2Switch)
-					managementIP := managementPortIP(subnet.CIDR)
+					managementIP := managementPortIP(subnet)
 
 					if em.gatewayConfig != nil {
 						// there are multiple mgmt ports in the cluster, thus the ports must be scoped with the node name
@@ -222,8 +222,8 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is
 			var otherConfig map[string]string
 			if hasSubnets {
 				otherConfig = map[string]string{
-					"exclude_ips": managementPortIP(subnet.CIDR).String(),
-					"subnet":      subnet.CIDR.String(),
+					"exclude_ips": managementPortIP(subnet).String(),
+					"subnet":      subnet.String(),
 				}
 			}
 
@@ -247,8 +247,8 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is
 			})
 			if em.gatewayConfig != nil {
 				if ocInfo.bnc.TopologyType() == ovntypes.Layer3Topology {
-					data = append(data, expectedGWEntities(pod.nodeName, ocInfo.bnc, *em.gatewayConfig)...)
-					data = append(data, expectedLayer3EgressEntities(ocInfo.bnc, *em.gatewayConfig)...)
+					data = append(data, expectedGWEntities(pod.nodeName, subnets, ocInfo.bnc, *em.gatewayConfig)...)
+					data = append(data, expectedLayer3EgressEntities(ocInfo.bnc, *em.gatewayConfig, subnet)...)
 				} else {
 					data = append(data, expectedLayer2EgressEntities(ocInfo.bnc, *em.gatewayConfig, pod.nodeName)...)
 				}
@@ -464,7 +464,7 @@ func dummyOVNPodNetworkAnnotationForNetwork(netConfig secondaryNetInfo, tunnelID
 		gateways []string
 		ips      []string
 	)
-	for _, subnetStr := range strings.Split(netConfig.subnets, ",") {
+	for _, subnetStr := range strings.Split(netConfig.clustersubnets, ",") {
 		subnet := testing.MustParseIPNet(subnetStr)
 		ips = append(ips, GetWorkloadSecondaryNetworkDummyIP(subnet).String())
 		gateways = append(gateways, util.GetNodeGatewayIfAddr(subnet).IP.String())
diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
index 774f9b78031..69396c7d7d7 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
@@ -297,10 +297,10 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 
 func dummySecondaryLayer2UserDefinedNetwork(subnets string) secondaryNetInfo {
 	return secondaryNetInfo{
-		netName:  secondaryNetworkName,
-		nadName:  namespacedName(ns, nadName),
-		topology: ovntypes.Layer2Topology,
-		subnets:  subnets,
+		netName:        secondaryNetworkName,
+		nadName:        namespacedName(ns, nadName),
+		topology:       ovntypes.Layer2Topology,
+		clustersubnets: subnets,
 	}
 }
 
@@ -329,7 +329,7 @@ func dummyL2TestPod(nsName string, info secondaryNetInfo) testPod {
 		pod.addNetwork(
 			info.netName,
 			info.nadName,
-			info.subnets,
+			info.clustersubnets,
 			"",
 			"100.200.0.1",
 			"100.200.0.3/16",
@@ -353,7 +353,7 @@ func dummyL2TestPod(nsName string, info secondaryNetInfo) testPod {
 	pod.addNetwork(
 		info.netName,
 		info.nadName,
-		info.subnets,
+		info.clustersubnets,
 		"",
 		"",
 		"100.200.0.1/16",
@@ -459,10 +459,10 @@ func ipv4DefaultRoute() *net.IPNet {
 
 func dummyLayer2SecondaryUserDefinedNetwork(subnets string) secondaryNetInfo {
 	return secondaryNetInfo{
-		netName:  secondaryNetworkName,
-		nadName:  namespacedName(ns, nadName),
-		topology: ovntypes.Layer2Topology,
-		subnets:  subnets,
+		netName:        secondaryNetworkName,
+		nadName:        namespacedName(ns, nadName),
+		topology:       ovntypes.Layer2Topology,
+		clustersubnets: subnets,
 	}
 }
 
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index 38c24b0ff3c..27e1fc660c1 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
+	ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
 	libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
 	ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
@@ -30,11 +31,12 @@ import (
 )
 
 type secondaryNetInfo struct {
-	netName   string
-	nadName   string
-	subnets   string
-	topology  string
-	isPrimary bool
+	netName        string
+	nadName        string
+	clustersubnets string
+	hostsubnets    string // not used in layer2 tests
+	topology       string
+	isPrimary      bool
 }
 
 const (
@@ -190,23 +192,23 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			Expect(app.Run([]string{app.Name})).To(Succeed())
 		},
 		table.Entry("pod on a user defined secondary network",
-			dummySecondaryUserDefinedNetwork("192.168.0.0/16"),
+			dummySecondaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			nonICClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined primary network",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			nonICClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined secondary network on an interconnect cluster",
-			dummySecondaryUserDefinedNetwork("192.168.0.0/16"),
+			dummySecondaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined primary network on an interconnect cluster",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined primary network on an interconnect cluster",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),
 	)
@@ -249,10 +251,10 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 					Expect(err).NotTo(HaveOccurred())
 					initialDB.NBData = append(
 						initialDB.NBData,
-						expectedGWEntities(podInfo.nodeName, networkConfig, *gwConfig)...)
+						expectedGWEntities(podInfo.nodeName, netInfo.hostsubnets, networkConfig, *gwConfig)...)
 					initialDB.NBData = append(
 						initialDB.NBData,
-						expectedLayer3EgressEntities(networkConfig, *gwConfig)...)
+						expectedLayer3EgressEntities(networkConfig, *gwConfig, ovntest.MustParseIPNet(netInfo.hostsubnets))...)
 				}
 				initialDB.NBData = append(initialDB.NBData, nbZone)
 
@@ -313,15 +315,15 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			Expect(app.Run([]string{app.Name})).To(Succeed())
 		},
 		table.Entry("pod on a user defined primary network",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			nonICClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined primary network on an interconnect cluster",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
 		table.Entry("pod on a user defined primary network on an interconnect cluster",
-			dummyPrimaryUserDefinedNetwork("192.168.0.0/16"),
+			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),
 	)
@@ -349,25 +351,25 @@ func newPodWithPrimaryUDN(
 	pod.addNetwork(
 		primaryUDNConfig.netName,
 		primaryUDNConfig.nadName,
-		primaryUDNConfig.subnets,
+		primaryUDNConfig.hostsubnets,
 		"",
 		nodeGWIP,
-		"192.168.0.3/16",
-		"0a:58:c0:a8:00:03",
+		"192.168.1.3/24",
+		"0a:58:c0:a8:01:03",
 		"primary",
 		0,
 		[]util.PodRoute{
 			{
 				Dest:    testing.MustParseIPNet("192.168.0.0/16"),
-				NextHop: testing.MustParseIP("192.168.0.1"),
+				NextHop: testing.MustParseIP("192.168.1.1"),
 			},
 			{
 				Dest:    testing.MustParseIPNet("172.16.1.0/24"),
-				NextHop: testing.MustParseIP("192.168.0.1"),
+				NextHop: testing.MustParseIP("192.168.1.1"),
 			},
 			{
 				Dest:    testing.MustParseIPNet("100.65.0.0/16"),
-				NextHop: testing.MustParseIP("192.168.0.1"),
+				NextHop: testing.MustParseIP("192.168.1.1"),
 			},
 		},
 	)
@@ -433,7 +435,7 @@ func (sni *secondaryNetInfo) netconf() *ovncnitypes.NetConf {
 		},
 		Topology: sni.topology,
 		NADName:  sni.nadName,
-		Subnets:  sni.subnets,
+		Subnets:  sni.clustersubnets,
 		Role:     role,
 	}
 }
@@ -445,7 +447,7 @@ func dummyTestPod(nsName string, info secondaryNetInfo) testPod {
 			nodeName,
 			nodeSubnet,
 			"10.128.1.2",
-			"192.168.0.1",
+			"192.168.1.1",
 			"myPod",
 			"10.128.1.3",
 			"0a:58:0a:80:01:03",
@@ -457,17 +459,17 @@ func dummyTestPod(nsName string, info secondaryNetInfo) testPod {
 	pod.addNetwork(
 		info.netName,
 		info.nadName,
-		info.subnets,
+		info.hostsubnets,
 		"",
 		"",
-		"192.168.0.3/16",
-		"0a:58:c0:a8:00:03",
+		"192.168.1.3/24",
+		"0a:58:c0:a8:01:03",
 		"secondary",
 		0,
 		[]util.PodRoute{
 			{
-				Dest:    testing.MustParseIPNet("192.168.0.0/16"),
-				NextHop: testing.MustParseIP("192.168.0.1"),
+				Dest:    testing.MustParseIPNet(info.clustersubnets),
+				NextHop: testing.MustParseIP("192.168.1.1"),
 			},
 		},
 	)
@@ -479,23 +481,25 @@ func dummyTestPodAdditionalNetworkIP() string {
 	return dummyTestPod(ns, secNetInfo).getNetworkPortInfo(secNetInfo.netName, secNetInfo.nadName).podIP
 }
 
-func dummySecondaryUserDefinedNetwork(subnets string) secondaryNetInfo {
+func dummySecondaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets string) secondaryNetInfo {
 	return secondaryNetInfo{
-		netName:  secondaryNetworkName,
-		nadName:  namespacedName(ns, nadName),
-		topology: ovntypes.Layer3Topology,
-		subnets:  subnets,
+		netName:        secondaryNetworkName,
+		nadName:        namespacedName(ns, nadName),
+		topology:       ovntypes.Layer3Topology,
+		clustersubnets: clustersubnets,
+		hostsubnets:    hostsubnets,
 	}
 }
 
-func dummyPrimaryUserDefinedNetwork(subnets string) secondaryNetInfo {
-	secondaryNet := dummySecondaryUserDefinedNetwork(subnets)
+func dummyPrimaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets string) secondaryNetInfo {
+	secondaryNet := dummySecondaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets)
 	secondaryNet.isPrimary = true
 	return secondaryNet
 }
 
+// This util is returning a network-name/hostSubnet for the node's node-subnets annotation
 func (sni *secondaryNetInfo) String() string {
-	return fmt.Sprintf("%q: %q", sni.netName, sni.subnets)
+	return fmt.Sprintf("%q: %q", sni.netName, sni.hostsubnets)
 }
 
 func newNodeWithSecondaryNets(nodeName string, nodeIPv4CIDR string, netInfos ...secondaryNetInfo) (*v1.Node, error) {
@@ -562,7 +566,7 @@ func emptyDefaultClusterNetworkNodeSwitch(nodeName string) []libovsdbtest.TestDa
 	return []libovsdbtest.TestData{&nbdb.LogicalSwitch{UUID: switchUUID, Name: nodeName}}
 }
 
-func expectedGWEntities(nodeName string, netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData {
+func expectedGWEntities(nodeName, nodeSubnet string, netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData {
 	gwRouterName := fmt.Sprintf("GR_%s_%s", netInfo.GetNetworkName(), nodeName)
 
 	expectedEntities := append(
@@ -597,7 +601,6 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 
 	staticRouteOutputPort := ovntypes.GWRouterToExtSwitchPrefix + netInfo.GetNetworkScopedGWRouterName(nodeName)
 	nextHopIP := gwConfig.NextHops[0].String()
-	ipv4Subnet := networkSubnet(netInfo)
 	nextHopMasqIP := nextHopMasqueradeIP().String()
 	masqSubnet := config.Gateway.V4MasqueradeSubnet
 	var nat []string
@@ -616,7 +619,7 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 			Nat:          nat,
 			StaticRoutes: []string{staticRoute1, staticRoute2, staticRoute3},
 		},
-		expectedGRStaticRoute(staticRoute1, ipv4Subnet, dummyJoinIP().IP.String(), nil, nil, netInfo),
+		expectedGRStaticRoute(staticRoute1, netInfo.Subnets()[0].CIDR.String(), dummyJoinIP().IP.String(), nil, nil, netInfo),
 		expectedGRStaticRoute(staticRoute2, ipv4DefaultRoute, nextHopIP, nil, &staticRouteOutputPort, netInfo),
 		expectedGRStaticRoute(staticRoute3, masqSubnet, nextHopMasqIP, nil, &staticRouteOutputPort, netInfo),
 	}
@@ -624,7 +627,7 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyTestPodAdditionalNetworkIP(), nil))
 	} else {
 		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
-		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), networkSubnet(netInfo), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), netInfo.Subnets()[0].CIDR.String(), standardNonDefaultNetworkExtIDs(netInfo)))
 	}
 	return expectedEntities
 }
@@ -679,7 +682,7 @@ func expectedLogicalRouterPort(lrpName string, netInfo util.NetInfo, options map
 	}
 }
 
-func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData {
+func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayConfig, nodeSubnet *net.IPNet) []libovsdbtest.TestData {
 	const (
 		routerPolicyUUID1 = "lrpol1-UUID"
 		routerPolicyUUID2 = "lrpol2-UUID"
@@ -691,8 +694,6 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 	rtosLRPName := fmt.Sprintf("%s%s", ovntypes.RouterToSwitchPrefix, netInfo.GetNetworkScopedName(nodeName))
 	rtosLRPUUID := rtosLRPName + "-UUID"
 	nodeIP := gwConfig.IPAddresses[0].IP.String()
-	networkIPv4Subnet := networkSubnet(netInfo)
-	subnet := netInfo.Subnets()[0] // egress requires subnets. So far, these helpers do not work for dual-stack
 
 	gatewayChassisUUID := fmt.Sprintf("%s-%s-UUID", rtosLRPName, gwConfig.ChassisID)
 	expectedEntities := []libovsdbtest.TestData{
@@ -704,11 +705,11 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 			Policies:     []string{routerPolicyUUID1, routerPolicyUUID2},
 			ExternalIDs:  standardNonDefaultNetworkExtIDs(netInfo),
 		},
-		&nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.0.1/16"}, MAC: "0a:58:c0:a8:00:01", GatewayChassis: []string{gatewayChassisUUID}},
-		expectedGRStaticRoute(staticRouteUUID1, networkIPv4Subnet, gwRouterIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo),
+		&nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.1.1/24"}, MAC: "0a:58:c0:a8:01:01", GatewayChassis: []string{gatewayChassisUUID}},
+		expectedGRStaticRoute(staticRouteUUID1, nodeSubnet.String(), gwRouterIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo),
 		expectedGRStaticRoute(staticRouteUUID2, gwRouterIPAddress().IP.String(), gwRouterIPAddress().IP.String(), nil, nil, netInfo),
-		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(subnet.CIDR).String()),
-		expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, joinIPAddr, managementPortIP(subnet.CIDR).String()),
+		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(nodeSubnet).String()),
+		expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, joinIPAddr, managementPortIP(nodeSubnet).String()),
 	}
 	return expectedEntities
 }
@@ -862,10 +863,6 @@ func gwRouterIPAddress() *net.IPNet {
 	}
 }
 
-func networkSubnet(netInfo util.NetInfo) string {
-	return strings.TrimSuffix(subnetsAsString(netInfo.Subnets())[0], "/24")
-}
-
 func gwRouterOptions(gwConfig util.L3GatewayConfig) map[string]string {
 	return map[string]string{
 		"lb_force_snat_ip":              "router_ip",

From 369ac907f9123cfaff68868cad3389d20201508c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Caama=C3=B1o=20Ruiz?= <jcaamano@redhat.com>
Date: Wed, 11 Sep 2024 18:11:29 +0000
Subject: [PATCH 35/48] Fix gateway manager not accounting for UDN join subnets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* gateway manager not accounting for different join subnets of UDNs
* tests not expecting the join subnet SNAT when pods snats were disabled
* tests misleadingly naming join ip to the ovn masquerade ip

Signed-off-by: Jaime Caamaño Ruiz <jcaamano@redhat.com>
---
 go-controller/pkg/config/utils.go             | 18 -------
 go-controller/pkg/ovn/gateway.go              | 50 +++++++++++++------
 ...econdary_layer2_network_controller_test.go | 17 +++----
 ...econdary_layer3_network_controller_test.go | 29 +++++------
 4 files changed, 59 insertions(+), 55 deletions(-)

diff --git a/go-controller/pkg/config/utils.go b/go-controller/pkg/config/utils.go
index 92bafdec970..b27be4d7cb6 100644
--- a/go-controller/pkg/config/utils.go
+++ b/go-controller/pkg/config/utils.go
@@ -263,24 +263,6 @@ func (cs *ConfigSubnets) checkIPFamilies() (usingIPv4, usingIPv6 bool, err error
 	return false, false, fmt.Errorf("illegal network configuration: %s", netConfig)
 }
 
-func ContainsJoinIP(ip net.IP) bool {
-	var joinSubnetsConfig []string
-	if IPv4Mode {
-		joinSubnetsConfig = append(joinSubnetsConfig, Gateway.V4JoinSubnet)
-	}
-	if IPv6Mode {
-		joinSubnetsConfig = append(joinSubnetsConfig, Gateway.V6JoinSubnet)
-	}
-
-	for _, subnet := range joinSubnetsConfig {
-		_, joinSubnet, _ := net.ParseCIDR(subnet)
-		if joinSubnet.Contains(ip) {
-			return true
-		}
-	}
-	return false
-}
-
 // masqueradeIP represents the masqueradeIPs used by the masquerade subnets for host to service traffic
 type MasqueradeIPsConfig struct {
 	V4OVNMasqueradeIP               net.IP
diff --git a/go-controller/pkg/ovn/gateway.go b/go-controller/pkg/ovn/gateway.go
index 9013f0447ed..f5b42121e11 100644
--- a/go-controller/pkg/ovn/gateway.go
+++ b/go-controller/pkg/ovn/gateway.go
@@ -159,6 +159,7 @@ func (gw *GatewayManager) cleanupStalePodSNATs(nodeName string, nodeIPs []*net.I
 	if !config.Gateway.DisableSNATMultipleGWs {
 		return nil
 	}
+
 	pods, err := gw.kube.GetPods(metav1.NamespaceAll, metav1.ListOptions{
 		FieldSelector: fields.OneTermEqualSelector("spec.nodeName", nodeName).String(),
 	})
@@ -166,13 +167,7 @@ func (gw *GatewayManager) cleanupStalePodSNATs(nodeName string, nodeIPs []*net.I
 		return fmt.Errorf("unable to list existing pods on node: %s, %w",
 			nodeName, err)
 	}
-	gatewayRouter := nbdb.LogicalRouter{
-		Name: gw.gwRouterName,
-	}
-	routerNats, err := libovsdbops.GetRouterNATs(gw.nbClient, &gatewayRouter)
-	if err != nil && errors.Is(err, libovsdbclient.ErrNotFound) {
-		return fmt.Errorf("unable to get NAT entries for router %s on node %s: %w", gatewayRouter.Name, nodeName, err)
-	}
+
 	podIPsOnNode := sets.NewString() // collects all podIPs on node
 	for _, pod := range pods {
 		pod := *pod
@@ -204,18 +199,37 @@ func (gw *GatewayManager) cleanupStalePodSNATs(nodeName string, nodeIPs []*net.I
 			podIPsOnNode.Insert(podIP.String())
 		}
 	}
+
+	gatewayRouter := nbdb.LogicalRouter{
+		Name: gw.gwRouterName,
+	}
+	routerNats, err := libovsdbops.GetRouterNATs(gw.nbClient, &gatewayRouter)
+	if err != nil && errors.Is(err, libovsdbclient.ErrNotFound) {
+		return fmt.Errorf("unable to get NAT entries for router %s on node %s: %w", gatewayRouter.Name, nodeName, err)
+	}
+
+	nodeIPset := sets.New(util.IPNetsIPToStringSlice(nodeIPs)...)
 	natsToDelete := []*nbdb.NAT{}
 	for _, routerNat := range routerNats {
 		routerNat := routerNat
 		if routerNat.Type != nbdb.NATTypeSNAT {
 			continue
 		}
-		for _, nodeIP := range nodeIPs {
-			logicalIP := net.ParseIP(routerNat.LogicalIP)
-			if routerNat.ExternalIP == nodeIP.IP.String() && !config.ContainsJoinIP(logicalIP) && !podIPsOnNode.Has(routerNat.LogicalIP) {
-				natsToDelete = append(natsToDelete, routerNat)
-			}
+		if !nodeIPset.Has(routerNat.ExternalIP) {
+			continue
+		}
+		if podIPsOnNode.Has(routerNat.LogicalIP) {
+			continue
 		}
+		logicalIP := net.ParseIP(routerNat.LogicalIP)
+		if logicalIP == nil {
+			// this is probably a CIDR so not a pod IP
+			continue
+		}
+		if gw.containsJoinIP(logicalIP) {
+			continue
+		}
+		natsToDelete = append(natsToDelete, routerNat)
 	}
 	if len(natsToDelete) > 0 {
 		err := libovsdbops.DeleteNATs(gw.nbClient, &gatewayRouter, natsToDelete...)
@@ -592,7 +606,7 @@ func (gw *GatewayManager) GatewayInit(
 			if gw.clusterRouterName != "" {
 				p := func(item *nbdb.LogicalRouterStaticRoute) bool {
 					return item.IPPrefix == lrsr.IPPrefix && item.Policy != nil && *item.Policy == *lrsr.Policy &&
-						config.ContainsJoinIP(net.ParseIP(item.Nexthop))
+						gw.containsJoinIP(net.ParseIP(item.Nexthop))
 				}
 				err := libovsdbops.DeleteLogicalRouterStaticRoutesWithPredicate(gw.nbClient, gw.clusterRouterName, p)
 				if err != nil {
@@ -643,7 +657,7 @@ func (gw *GatewayManager) GatewayInit(
 		// note, nat.LogicalIP may be a CIDR or IP, we don't care unless it's an IP
 		parsedLogicalIP := net.ParseIP(nat.LogicalIP)
 		// check if join ip changed
-		if config.ContainsJoinIP(parsedLogicalIP) {
+		if gw.containsJoinIP(parsedLogicalIP) {
 			// is a join SNAT, check if IP needs updating
 			joinIP, err := util.MatchFirstIPFamily(utilnet.IsIPv6(parsedLogicalIP), gwLRPIPs)
 			if err != nil {
@@ -1152,6 +1166,14 @@ func (gw *GatewayManager) removeLRPolicies(nodeName string) {
 	}
 }
 
+func (gw *GatewayManager) containsJoinIP(ip net.IP) bool {
+	ipNet := &net.IPNet{
+		IP:   ip,
+		Mask: util.GetIPFullMask(ip),
+	}
+	return util.IsContainedInAnyCIDR(ipNet, gw.netInfo.JoinSubnets()...)
+}
+
 func (gw *GatewayManager) syncGatewayLogicalNetwork(
 	node *kapi.Node,
 	l3GatewayConfig *util.L3GatewayConfig,
diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
index 69396c7d7d7..08ee1d415da 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
@@ -387,7 +387,7 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 
 	var nat []string
 	if config.Gateway.DisableSNATMultipleGWs {
-		nat = append(nat, perPodSNAT)
+		nat = append(nat, nat1, perPodSNAT)
 	} else {
 		nat = append(nat, nat1, nat2, nat3)
 	}
@@ -402,7 +402,7 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 			Options:      gwRouterOptions(gwConfig),
 			Policies:     []string{routerPolicyUUID1},
 		},
-		expectedGWToNetworkSwitchRouterPort(gwRouterToNetworkSwitchPortName, netInfo, gwRouterIPAddress(), layer2SubnetGWAddr()),
+		expectedGWToNetworkSwitchRouterPort(gwRouterToNetworkSwitchPortName, netInfo, gwRouterJoinIPAddress(), layer2SubnetGWAddr()),
 		expectedGRStaticRoute(sr1, dummyMasqueradeSubnet().String(), nextHopMasqueradeIP().String(), nil, &staticRouteOutputPort, netInfo),
 		expectedGRStaticRoute(sr2, ipv4DefaultRoute().String(), nodeGateway().IP.String(), nil, &staticRouteOutputPort, netInfo),
 		expectedGRToExternalSwitchLRP(gwRouterName, netInfo, nodePhysicalIPAddress(), udnGWSNATAddress()),
@@ -411,15 +411,14 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP().IP.String(), managementPortIP(layer2Subnet()).String()),
 	}
 
-	for _, entity := range expectedExternalSwitchAndLSPs(netInfo, gwConfig, nodeName) {
-		expectedEntities = append(expectedEntities, entity)
-	}
+	expectedEntities = append(expectedEntities, expectedExternalSwitchAndLSPs(netInfo, gwConfig, nodeName)...)
 	if config.Gateway.DisableSNATMultipleGWs {
-		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyL2TestPodAdditionalNetworkIP(), nil))
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyMasqueradeIP().IP.String(), gwRouterJoinIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyMasqueradeIP().IP.String(), dummyL2TestPodAdditionalNetworkIP(), nil))
 	} else {
-		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
-		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), layer2Subnet().String(), standardNonDefaultNetworkExtIDs(netInfo)))
-		expectedEntities = append(expectedEntities, newNATEntry(nat3, dummyJoinIP().IP.String(), layer2SubnetGWAddr().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyMasqueradeIP().IP.String(), gwRouterJoinIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyMasqueradeIP().IP.String(), layer2Subnet().String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat3, dummyMasqueradeIP().IP.String(), layer2SubnetGWAddr().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
 	}
 	return expectedEntities
 }
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index 27e1fc660c1..1fbcbf73bc2 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -527,7 +527,7 @@ func newNodeWithSecondaryNets(nodeName string, nodeIPv4CIDR string, netInfos ...
 				util.OvnNodeChassisID:                  "abdcef",
 				"k8s.ovn.org/network-ids":              "{\"default\":\"0\",\"isolatednet\":\"2\"}",
 				util.OvnNodeManagementPortMacAddresses: fmt.Sprintf("{\"isolatednet\":%q}", dummyMACAddr),
-				util.OVNNodeGRLRPAddrs:                 fmt.Sprintf("{\"isolatednet\":{\"ipv4\":%q}}", gwRouterIPAddress()),
+				util.OVNNodeGRLRPAddrs:                 fmt.Sprintf("{\"isolatednet\":{\"ipv4\":%q}}", gwRouterJoinIPAddress()),
 			},
 			Labels: map[string]string{
 				"k8s.ovn.org/egress-assignable": "",
@@ -545,10 +545,10 @@ func newNodeWithSecondaryNets(nodeName string, nodeIPv4CIDR string, netInfos ...
 }
 
 func dummyJoinIPs() []*net.IPNet {
-	return []*net.IPNet{dummyJoinIP()}
+	return []*net.IPNet{dummyMasqueradeIP()}
 }
 
-func dummyJoinIP() *net.IPNet {
+func dummyMasqueradeIP() *net.IPNet {
 	return &net.IPNet{
 		IP:   net.ParseIP("169.254.169.13"),
 		Mask: net.CIDRMask(24, 32),
@@ -571,7 +571,7 @@ func expectedGWEntities(nodeName, nodeSubnet string, netInfo util.NetInfo, gwCon
 
 	expectedEntities := append(
 		expectedGWRouterPlusNATAndStaticRoutes(nodeName, gwRouterName, netInfo, gwConfig),
-		expectedGRToJoinSwitchLRP(gwRouterName, gwRouterIPAddress(), netInfo),
+		expectedGRToJoinSwitchLRP(gwRouterName, gwRouterJoinIPAddress(), netInfo),
 		expectedGRToExternalSwitchLRP(gwRouterName, netInfo, nodePhysicalIPAddress(), udnGWSNATAddress()),
 		expectedGatewayChassis(nodeName, netInfo, gwConfig),
 		expectedStaticMACBinding(gwRouterName, nextHopMasqueradeIP()),
@@ -605,7 +605,7 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 	masqSubnet := config.Gateway.V4MasqueradeSubnet
 	var nat []string
 	if config.Gateway.DisableSNATMultipleGWs {
-		nat = append(nat, perPodSNAT)
+		nat = append(nat, nat1, perPodSNAT)
 	} else {
 		nat = append(nat, nat1, nat2)
 	}
@@ -619,15 +619,16 @@ func expectedGWRouterPlusNATAndStaticRoutes(
 			Nat:          nat,
 			StaticRoutes: []string{staticRoute1, staticRoute2, staticRoute3},
 		},
-		expectedGRStaticRoute(staticRoute1, netInfo.Subnets()[0].CIDR.String(), dummyJoinIP().IP.String(), nil, nil, netInfo),
+		expectedGRStaticRoute(staticRoute1, netInfo.Subnets()[0].CIDR.String(), dummyMasqueradeIP().IP.String(), nil, nil, netInfo),
 		expectedGRStaticRoute(staticRoute2, ipv4DefaultRoute, nextHopIP, nil, &staticRouteOutputPort, netInfo),
 		expectedGRStaticRoute(staticRoute3, masqSubnet, nextHopMasqIP, nil, &staticRouteOutputPort, netInfo),
 	}
 	if config.Gateway.DisableSNATMultipleGWs {
-		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyTestPodAdditionalNetworkIP(), nil))
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyMasqueradeIP().IP.String(), gwRouterJoinIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyMasqueradeIP().IP.String(), dummyTestPodAdditionalNetworkIP(), nil))
 	} else {
-		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
-		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), netInfo.Subnets()[0].CIDR.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyMasqueradeIP().IP.String(), gwRouterJoinIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo)))
+		expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyMasqueradeIP().IP.String(), netInfo.Subnets()[0].CIDR.String(), standardNonDefaultNetworkExtIDs(netInfo)))
 	}
 	return expectedEntities
 }
@@ -689,7 +690,7 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		staticRouteUUID1  = "sr1-UUID"
 		staticRouteUUID2  = "sr2-UUID"
 	)
-	joinIPAddr := dummyJoinIP().IP.String()
+	masqIPAddr := dummyMasqueradeIP().IP.String()
 	clusterRouterName := fmt.Sprintf("%s_ovn_cluster_router", netInfo.GetNetworkName())
 	rtosLRPName := fmt.Sprintf("%s%s", ovntypes.RouterToSwitchPrefix, netInfo.GetNetworkScopedName(nodeName))
 	rtosLRPUUID := rtosLRPName + "-UUID"
@@ -706,10 +707,10 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 			ExternalIDs:  standardNonDefaultNetworkExtIDs(netInfo),
 		},
 		&nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.1.1/24"}, MAC: "0a:58:c0:a8:01:01", GatewayChassis: []string{gatewayChassisUUID}},
-		expectedGRStaticRoute(staticRouteUUID1, nodeSubnet.String(), gwRouterIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo),
-		expectedGRStaticRoute(staticRouteUUID2, gwRouterIPAddress().IP.String(), gwRouterIPAddress().IP.String(), nil, nil, netInfo),
+		expectedGRStaticRoute(staticRouteUUID1, nodeSubnet.String(), gwRouterJoinIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo),
+		expectedGRStaticRoute(staticRouteUUID2, gwRouterJoinIPAddress().IP.String(), gwRouterJoinIPAddress().IP.String(), nil, nil, netInfo),
 		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(nodeSubnet).String()),
-		expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, joinIPAddr, managementPortIP(nodeSubnet).String()),
+		expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, masqIPAddr, managementPortIP(nodeSubnet).String()),
 	}
 	return expectedEntities
 }
@@ -856,7 +857,7 @@ func nextHopMasqueradeIP() net.IP {
 	return net.ParseIP("169.254.169.4")
 }
 
-func gwRouterIPAddress() *net.IPNet {
+func gwRouterJoinIPAddress() *net.IPNet {
 	return &net.IPNet{
 		IP:   net.ParseIP("100.65.0.4"),
 		Mask: net.CIDRMask(16, 32),

From a4e2a0029301659c094d346dbdca685d32ce88ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Caama=C3=B1o=20Ruiz?= <jcaamano@redhat.com>
Date: Wed, 11 Sep 2024 18:11:55 +0000
Subject: [PATCH 36/48] Fix duplicated UDN tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* L3 tests with duplicated names
* L2 duplicated tests of which some were intended to test something else
* L2 secondary tests annotating the wrong IP under the assumption that mgmt and gw IPs would be allocated

Signed-off-by: Jaime Caamaño Ruiz <jcaamano@redhat.com>
---
 go-controller/pkg/ovn/multihoming_test.go     | 43 ++++++++-----------
 ...econdary_layer2_network_controller_test.go | 18 ++++----
 ...econdary_layer3_network_controller_test.go | 14 +++---
 3 files changed, 33 insertions(+), 42 deletions(-)

diff --git a/go-controller/pkg/ovn/multihoming_test.go b/go-controller/pkg/ovn/multihoming_test.go
index a0d31b3ebe9..641472cdb3c 100644
--- a/go-controller/pkg/ovn/multihoming_test.go
+++ b/go-controller/pkg/ovn/multihoming_test.go
@@ -8,8 +8,6 @@ import (
 
 	v1 "k8s.io/api/core/v1"
 
-	iputils "github.com/containernetworking/plugins/pkg/ip"
-
 	nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
 
 	libovsdbclient "github.com/ovn-org/libovsdb/client"
@@ -345,7 +343,7 @@ func hostPhysicalIP(gwConfig util.L3GatewayConfig) string {
 
 func hostIPsFromGWConfig(gwConfig util.L3GatewayConfig) []string {
 	var hostIPs []string
-	for _, ip := range append(gwConfig.IPAddresses, dummyJoinIP()) {
+	for _, ip := range append(gwConfig.IPAddresses, dummyMasqueradeIP()) {
 		hostIPs = append(hostIPs, ip.IP.String())
 	}
 	return hostIPs
@@ -404,8 +402,8 @@ func icClusterWithDisableSNATTestConfiguration() testConfiguration {
 	}
 }
 
-func newMultiHomedPod(namespace, name, node, podIP string, multiHomingConfigs ...secondaryNetInfo) *v1.Pod {
-	pod := newPod(namespace, name, node, podIP)
+func newMultiHomedPod(testPod testPod, multiHomingConfigs ...secondaryNetInfo) *v1.Pod {
+	pod := newPod(testPod.namespace, testPod.podName, testPod.nodeName, testPod.podIP)
 	var secondaryNetworks []nadapi.NetworkSelectionElement
 	for _, multiHomingConf := range multiHomingConfigs {
 		if multiHomingConf.isPrimary {
@@ -427,7 +425,7 @@ func newMultiHomedPod(namespace, name, node, podIP string, multiHomingConfigs ..
 	serializedNetworkSelectionElements, _ := json.Marshal(secondaryNetworks)
 	pod.Annotations = map[string]string{nadapi.NetworkAttachmentAnnot: string(serializedNetworkSelectionElements)}
 	if config.OVNKubernetesFeature.EnableInterconnect {
-		dummyOVNNetAnnotations := dummyOVNPodNetworkAnnotations(multiHomingConfigs)
+		dummyOVNNetAnnotations := dummyOVNPodNetworkAnnotations(testPod.secondaryPodInfos, multiHomingConfigs)
 		if dummyOVNNetAnnotations != "{}" {
 			pod.Annotations["k8s.ovn.org/pod-networks"] = dummyOVNNetAnnotations
 		}
@@ -435,7 +433,7 @@ func newMultiHomedPod(namespace, name, node, podIP string, multiHomingConfigs ..
 	return pod
 }
 
-func dummyOVNPodNetworkAnnotations(multiHomingConfigs []secondaryNetInfo) string {
+func dummyOVNPodNetworkAnnotations(secondaryPodInfos map[string]*secondaryPodInfo, multiHomingConfigs []secondaryNetInfo) string {
 	var ovnPodNetworksAnnotations []byte
 	podAnnotations := map[string]podAnnotation{}
 	for i, netConfig := range multiHomingConfigs {
@@ -443,7 +441,8 @@ func dummyOVNPodNetworkAnnotations(multiHomingConfigs []secondaryNetInfo) string
 		// for layer2 topology since allocating the annotation for this cluster configuration
 		// is performed by cluster manager - which doesn't exist in the unit tests.
 		if netConfig.topology == ovntypes.Layer2Topology {
-			podAnnotations[netConfig.nadName] = dummyOVNPodNetworkAnnotationForNetwork(netConfig, i+1)
+			portInfo := secondaryPodInfos[netConfig.netName].allportInfo[netConfig.nadName]
+			podAnnotations[netConfig.nadName] = dummyOVNPodNetworkAnnotationForNetwork(portInfo, netConfig, i+1)
 		}
 	}
 
@@ -455,23 +454,25 @@ func dummyOVNPodNetworkAnnotations(multiHomingConfigs []secondaryNetInfo) string
 	return string(ovnPodNetworksAnnotations)
 }
 
-func dummyOVNPodNetworkAnnotationForNetwork(netConfig secondaryNetInfo, tunnelID int) podAnnotation {
+func dummyOVNPodNetworkAnnotationForNetwork(portInfo portInfo, netConfig secondaryNetInfo, tunnelID int) podAnnotation {
 	role := ovntypes.NetworkRoleSecondary
 	if netConfig.isPrimary {
 		role = ovntypes.NetworkRolePrimary
 	}
-	var (
-		gateways []string
-		ips      []string
-	)
+	var gateways []string
 	for _, subnetStr := range strings.Split(netConfig.clustersubnets, ",") {
 		subnet := testing.MustParseIPNet(subnetStr)
-		ips = append(ips, GetWorkloadSecondaryNetworkDummyIP(subnet).String())
 		gateways = append(gateways, util.GetNodeGatewayIfAddr(subnet).IP.String())
 	}
+	ip := testing.MustParseIP(portInfo.podIP)
+	_, maskSize := util.GetIPFullMask(ip).Size()
+	ipNet := net.IPNet{
+		IP:   ip,
+		Mask: net.CIDRMask(portInfo.prefixLen, maskSize),
+	}
 	return podAnnotation{
-		IPs:      ips,
-		MAC:      util.IPAddrToHWAddr(testing.MustParseIPNet(ips[0]).IP).String(),
+		IPs:      []string{ipNet.String()},
+		MAC:      util.IPAddrToHWAddr(ip).String(),
 		Gateways: gateways,
 		Routes:   nil, // TODO: must add here the expected routes.
 		TunnelID: tunnelID,
@@ -479,16 +480,6 @@ func dummyOVNPodNetworkAnnotationForNetwork(netConfig secondaryNetInfo, tunnelID
 	}
 }
 
-// GetWorkloadSecondaryNetworkDummyIP returns the workload logical switch port
-// address (the ".3" address), return nil if the subnet is invalid
-func GetWorkloadSecondaryNetworkDummyIP(subnet *net.IPNet) *net.IPNet {
-	mgmtIfAddr := util.GetNodeManagementIfAddr(subnet)
-	if mgmtIfAddr == nil {
-		return nil
-	}
-	return &net.IPNet{IP: iputils.NextIP(mgmtIfAddr.IP), Mask: subnet.Mask}
-}
-
 // Internal struct used to marshal PodAnnotation to the pod annotationç
 // Copied from pkg/util/pod_annotation.go
 type podAnnotation struct {
diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
index 08ee1d415da..99918270287 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
@@ -87,7 +87,7 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 					&v1.NodeList{Items: []v1.Node{*testNode}},
 					&v1.PodList{
 						Items: []v1.Pod{
-							*newMultiHomedPod(podInfo.namespace, podInfo.podName, podInfo.nodeName, podInfo.podIP, netInfo),
+							*newMultiHomedPod(podInfo, netInfo),
 						},
 					},
 					&nadapi.NetworkAttachmentDefinitionList{
@@ -160,21 +160,21 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			nonICClusterTestConfiguration(),
 		),
 
-		table.Entry("pod on a user defined primary network on an IC cluster",
+		table.Entry("pod on a user defined primary network",
 			dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16"),
-			icClusterTestConfiguration(),
+			nonICClusterTestConfiguration(),
 		),
 
-		table.Entry("pod on a user defined secondary network",
+		table.Entry("pod on a user defined secondary network on an IC cluster",
 			dummySecondaryLayer2UserDefinedNetwork("100.200.0.0/16"),
-			nonICClusterTestConfiguration(),
+			icClusterTestConfiguration(),
 		),
 
 		table.Entry("pod on a user defined primary network on an IC cluster",
 			dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16"),
 			icClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an IC cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster with per-pod SNATs enabled",
 			dummyPrimaryLayer2UserDefinedNetwork("100.200.0.0/16"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),
@@ -230,7 +230,7 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 					},
 					&v1.PodList{
 						Items: []v1.Pod{
-							*newMultiHomedPod(podInfo.namespace, podInfo.podName, podInfo.nodeName, podInfo.podIP, netInfo),
+							*newMultiHomedPod(podInfo, netInfo),
 						},
 					},
 					&nadapi.NetworkAttachmentDefinitionList{
@@ -283,11 +283,11 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() {
 			dummyLayer2PrimaryUserDefinedNetwork("192.168.0.0/16"),
 			nonICClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster",
 			dummyLayer2PrimaryUserDefinedNetwork("192.168.0.0/16"),
 			icClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster with per-pod SNATs enabled",
 			dummyLayer2PrimaryUserDefinedNetwork("192.168.0.0/16"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index 1fbcbf73bc2..fe490bf912e 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -137,7 +137,7 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 					},
 					&v1.PodList{
 						Items: []v1.Pod{
-							*newMultiHomedPod(podInfo.namespace, podInfo.podName, podInfo.nodeName, podInfo.podIP, netInfo),
+							*newMultiHomedPod(podInfo, netInfo),
 						},
 					},
 					&nadapi.NetworkAttachmentDefinitionList{
@@ -199,15 +199,15 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			nonICClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined secondary network on an interconnect cluster",
+		table.Entry("pod on a user defined secondary network on an IC cluster",
 			dummySecondaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster",
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster with per-pod SNATs enabled",
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),
@@ -270,7 +270,7 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 					},
 					&v1.PodList{
 						Items: []v1.Pod{
-							*newMultiHomedPod(podInfo.namespace, podInfo.podName, podInfo.nodeName, podInfo.podIP, netInfo),
+							*newMultiHomedPod(podInfo, netInfo),
 						},
 					},
 					&nadapi.NetworkAttachmentDefinitionList{
@@ -318,11 +318,11 @@ var _ = Describe("OVN Multi-Homed pod operations", func() {
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			nonICClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster",
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterTestConfiguration(),
 		),
-		table.Entry("pod on a user defined primary network on an interconnect cluster",
+		table.Entry("pod on a user defined primary network on an IC cluster with per-pod SNATs enabled",
 			dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"),
 			icClusterWithDisableSNATTestConfiguration(),
 		),

From 14afcbf9621264f6d1de32107bfdcf77f655bbbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaime=20Caama=C3=B1o=20Ruiz?= <jcaamano@redhat.com>
Date: Fri, 13 Sep 2024 12:27:00 +0000
Subject: [PATCH 37/48] Pin multus to v4.1.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

To avoid being affected by potential dev breakage.

Signed-off-by: Jaime Caamaño Ruiz <jcaamano@redhat.com>
---
 contrib/kind-common | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/contrib/kind-common b/contrib/kind-common
index 55741a9833a..56d450d24e1 100644
--- a/contrib/kind-common
+++ b/contrib/kind-common
@@ -394,9 +394,11 @@ install_kubevirt_ipam_controller() {
 }
 
 install_multus() {
-  echo "Installing multus-cni daemonset ..."
-  multus_manifest="https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml"
-  run_kubectl apply -f "$multus_manifest"
+  local version="v4.1.0"
+  echo "Installing multus-cni $version daemonset ..."
+  wget -qO- "https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/${version}/deployments/multus-daemonset.yml" |\
+    sed -e "s|multus-cni:snapshot|multus-cni:${version}|g" |\
+    run_kubectl apply -f -
 }
 
 install_mpolicy_crd() {

From 1d3c6a32acb7707169bcfc2356fb8d7b153940c1 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 31 Aug 2024 12:04:54 +0200
Subject: [PATCH 38/48] Update OVN DB Schema; generate libovsdb bindings

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 go-controller/Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/go-controller/Makefile b/go-controller/Makefile
index 465c1dd488d..7b02f6b6a64 100644
--- a/go-controller/Makefile
+++ b/go-controller/Makefile
@@ -22,7 +22,8 @@ else
 CONTAINER_RUNTIME=docker
 endif
 CONTAINER_RUNNABLE ?= $(shell $(CONTAINER_RUNTIME) -v > /dev/null 2>&1; echo $$?)
-OVN_SCHEMA_VERSION ?= v24.03.1
+# FIXME(tssurya): In one week when OVN 24.09 is released change the schema version
+OVN_SCHEMA_VERSION ?= 8efac26f6637fc
 ifeq ($(NOROOT),TRUE)
 C_ARGS = -e NOROOT=TRUE
 else

From 294c7887628942db2af8185e510d4dcee1b300b4 Mon Sep 17 00:00:00 2001
From: Periyasamy Palanisamy <pepalani@redhat.com>
Date: Wed, 3 Jul 2024 16:20:45 +0200
Subject: [PATCH 39/48] Update NAT functions to take match expression as
 argument

This is done to get conditional SNATs feature.

Signed-off-by: Periyasamy Palanisamy <pepalani@redhat.com>
---
 go-controller/pkg/libovsdb/ops/router.go | 28 ++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/go-controller/pkg/libovsdb/ops/router.go b/go-controller/pkg/libovsdb/ops/router.go
index 0eb8499abe4..c0436914eb8 100644
--- a/go-controller/pkg/libovsdb/ops/router.go
+++ b/go-controller/pkg/libovsdb/ops/router.go
@@ -886,6 +886,7 @@ func buildNAT(
 	logicalPort string,
 	externalMac string,
 	externalIDs map[string]string,
+	match string,
 ) *nbdb.NAT {
 	nat := &nbdb.NAT{
 		Type:        natType,
@@ -893,6 +894,7 @@ func buildNAT(
 		LogicalIP:   logicalIP,
 		Options:     map[string]string{"stateless": "false"},
 		ExternalIDs: externalIDs,
+		Match:       match,
 	}
 
 	if logicalPort != "" {
@@ -912,6 +914,16 @@ func BuildSNAT(
 	logicalIP *net.IPNet,
 	logicalPort string,
 	externalIDs map[string]string,
+) *nbdb.NAT {
+	return BuildSNATWithMatch(externalIP, logicalIP, logicalPort, externalIDs, "")
+}
+
+func BuildSNATWithMatch(
+	externalIP *net.IP,
+	logicalIP *net.IPNet,
+	logicalPort string,
+	externalIDs map[string]string,
+	match string,
 ) *nbdb.NAT {
 	externalIPStr := ""
 	if externalIP != nil {
@@ -923,7 +935,7 @@ func BuildSNAT(
 	if logicalIPMask != 32 && logicalIPMask != 128 {
 		logicalIPStr = logicalIP.String()
 	}
-	return buildNAT(nbdb.NATTypeSNAT, externalIPStr, logicalIPStr, logicalPort, "", externalIDs)
+	return buildNAT(nbdb.NATTypeSNAT, externalIPStr, logicalIPStr, logicalPort, "", externalIDs, match)
 }
 
 // BuildDNATAndSNAT builds a logical router DNAT/SNAT
@@ -933,6 +945,17 @@ func BuildDNATAndSNAT(
 	logicalPort string,
 	externalMac string,
 	externalIDs map[string]string,
+) *nbdb.NAT {
+	return BuildDNATAndSNATWithMatch(externalIP, logicalIP, logicalPort, externalMac, externalIDs, "")
+}
+
+func BuildDNATAndSNATWithMatch(
+	externalIP *net.IP,
+	logicalIP *net.IPNet,
+	logicalPort string,
+	externalMac string,
+	externalIDs map[string]string,
+	match string,
 ) *nbdb.NAT {
 	externalIPStr := ""
 	if externalIP != nil {
@@ -948,7 +971,8 @@ func BuildDNATAndSNAT(
 		logicalIPStr,
 		logicalPort,
 		externalMac,
-		externalIDs)
+		externalIDs,
+		match)
 }
 
 // isEquivalentNAT if it has same uuid. Otherwise, check if types match.

From 8a47ba5d9f9bfbbb936ed1cda6ef22a92418df97 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 31 Aug 2024 12:50:17 +0200
Subject: [PATCH 40/48] remove deadcode

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 go-controller/pkg/ovn/secondary_layer2_network_controller.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller.go b/go-controller/pkg/ovn/secondary_layer2_network_controller.go
index bb28525e9aa..6d0253f6051 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller.go
@@ -319,10 +319,10 @@ func (oc *SecondaryLayer2NetworkController) Start(ctx context.Context) error {
 		return err
 	}
 
-	return oc.run(ctx)
+	return oc.run()
 }
 
-func (oc *SecondaryLayer2NetworkController) run(ctx context.Context) error {
+func (oc *SecondaryLayer2NetworkController) run() error {
 	return oc.BaseSecondaryLayer2NetworkController.run()
 }
 

From 308415464660627ae568011eaf63c7f6230fd2fe Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 31 Aug 2024 21:13:55 +0200
Subject: [PATCH 41/48] Add buildUDNEgressSNAT util to BSNC controller

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../ovn/base_network_controller_secondary.go  | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/go-controller/pkg/ovn/base_network_controller_secondary.go b/go-controller/pkg/ovn/base_network_controller_secondary.go
index 7a4520cc47b..748fb3adbd6 100644
--- a/go-controller/pkg/ovn/base_network_controller_secondary.go
+++ b/go-controller/pkg/ovn/base_network_controller_secondary.go
@@ -27,6 +27,7 @@ import (
 	kapi "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/klog/v2"
+	utilnet "k8s.io/utils/net"
 	"k8s.io/utils/ptr"
 )
 
@@ -791,3 +792,48 @@ func (oc *BaseSecondaryNetworkController) getNetworkID() (int, error) {
 	}
 	return *oc.networkID, nil
 }
+
+// buildUDNEgressSNAT is used to build the conditional SNAT required on L3 and L2 UDNs to
+// steer traffic correctly via mp0 when leaving OVN to the host
+func (bsnc *BaseSecondaryNetworkController) buildUDNEgressSNAT(localPodSubnets []*net.IPNet, outputPort string,
+	node *kapi.Node) ([]*nbdb.NAT, error) {
+	if len(localPodSubnets) == 0 {
+		return nil, nil // nothing to do
+	}
+	var snats []*nbdb.NAT
+	var masqIP *udn.MasqueradeIPs
+	var err error
+	networkID, err := bsnc.getNetworkID()
+	if err != nil {
+		return nil, fmt.Errorf("failed to get networkID for network %q: %v", bsnc.GetNetworkName(), err)
+	}
+	dstMac, err := util.ParseNodeManagementPortMACAddresses(node, bsnc.GetNetworkName())
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse mac address annotation for network %q on node %q, err: %w",
+			bsnc.GetNetworkName(), node.Name, err)
+	}
+	extIDs := map[string]string{
+		types.NetworkExternalID:  bsnc.GetNetworkName(),
+		types.TopologyExternalID: bsnc.TopologyType(),
+	}
+	for _, localPodSubnet := range localPodSubnets {
+		if utilnet.IsIPv6CIDR(localPodSubnet) {
+			masqIP, err = udn.AllocateV6MasqueradeIPs(networkID)
+		} else {
+			masqIP, err = udn.AllocateV4MasqueradeIPs(networkID)
+		}
+		if err != nil {
+			return nil, err
+		}
+		if masqIP == nil {
+			return nil, fmt.Errorf("masquerade IP cannot be empty network %s (%d): %v", bsnc.GetNetworkName(), networkID, err)
+		}
+		snats = append(snats, libovsdbops.BuildSNATWithMatch(&masqIP.ManagementPort.IP, localPodSubnet, outputPort,
+			extIDs, getMasqueradeManagementIPSNATMatch(dstMac.String())))
+	}
+	return snats, nil
+}
+
+func getMasqueradeManagementIPSNATMatch(dstMac string) string {
+	return fmt.Sprintf("eth.dst == %s", dstMac)
+}

From 5f7ac17a9c5e5663837b79a38a9fd976900abb05 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 31 Aug 2024 21:15:18 +0200
Subject: [PATCH 42/48] L3: Add PodSubnet SNATs on ovn-cluster-router

snat                                   eth.dst == 76:23:f    169.254.0.12                        10.128.0.0/24
snat                                   eth.dst == 76:23:f    169.254.0.12                        2010:100:200::/64

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../secondary_layer3_network_controller.go    | 42 ++++++++++++++++++-
 ...econdary_layer3_network_controller_test.go |  7 ++++
 2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller.go b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
index a27428dd7b1..d90521fad8a 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller.go
@@ -420,8 +420,11 @@ func (oc *SecondaryLayer3NetworkController) newRetryFramework(
 // Start starts the secondary layer3 controller, handles all events and creates all needed logical entities
 func (oc *SecondaryLayer3NetworkController) Start(ctx context.Context) error {
 	klog.Infof("Start secondary %s network controller of network %s", oc.TopologyType(), oc.GetNetworkName())
-
-	if err := oc.Init(ctx); err != nil {
+	_, err := oc.getNetworkID()
+	if err != nil {
+		return fmt.Errorf("unable to set networkID on secondary L3 controller for network %s, err: %w", oc.GetNetworkName(), err)
+	}
+	if err = oc.Init(ctx); err != nil {
 		return err
 	}
 
@@ -747,6 +750,36 @@ func (oc *SecondaryLayer3NetworkController) addUpdateRemoteNodeEvent(node *kapi.
 	return err
 }
 
+// addNodeSubnetEgressSNAT adds the SNAT on each node's ovn-cluster-router in L3 networks
+// snat eth.dst == d6:cf:fd:2c:a6:44 169.254.0.12 10.128.0.0/24
+// snat eth.dst == d6:cf:fd:2c:a6:44 169.254.0.12 2010:100:200::/64
+// these SNATs are required for pod2Egress traffic in LGW mode and pod2SameNode traffic in SGW mode to function properly on UDNs
+// SNAT Breakdown:
+// match = "eth.dst == d6:cf:fd:2c:a6:44"; the MAC here is the mpX interface MAC address for this UDN
+// logicalIP = "10.128.0.0/24"; which is the podsubnet for this node in L3 UDN
+// externalIP = "169.254.0.12"; which is the masqueradeIP for this L3 UDN
+// so all in all we want to condionally SNAT all packets that are coming from pods hosted on this node,
+// which are leaving via UDN's mpX interface to the UDN's masqueradeIP.
+func (oc *SecondaryLayer3NetworkController) addUDNNodeSubnetEgressSNAT(localPodSubnets []*net.IPNet, node *kapi.Node) error {
+	outputPort := types.RouterToSwitchPrefix + oc.GetNetworkScopedName(node.Name)
+	nats, err := oc.buildUDNEgressSNAT(localPodSubnets, outputPort, node)
+	if err != nil {
+		return fmt.Errorf("failed to build UDN masquerade SNATs for network %q on node %q, err: %w",
+			oc.GetNetworkName(), node.Name, err)
+	}
+	if len(nats) == 0 {
+		return nil // nothing to do
+	}
+	router := &nbdb.LogicalRouter{
+		Name: oc.GetNetworkScopedClusterRouterName(),
+	}
+	if err := libovsdbops.CreateOrUpdateNATs(oc.nbClient, router, nats...); err != nil {
+		return fmt.Errorf("failed to update SNAT for node subnet on router: %q for network %q, error: %w",
+			oc.GetNetworkScopedClusterRouterName(), oc.GetNetworkName(), err)
+	}
+	return nil
+}
+
 func (oc *SecondaryLayer3NetworkController) addNode(node *kapi.Node) ([]*net.IPNet, error) {
 	// Node subnet for the secondary layer3 network is allocated by cluster manager.
 	// Make sure that the node is allocated with the subnet before proceeding
@@ -760,6 +793,11 @@ func (oc *SecondaryLayer3NetworkController) addNode(node *kapi.Node) ([]*net.IPN
 	if err != nil {
 		return nil, err
 	}
+	if util.IsNetworkSegmentationSupportEnabled() && oc.IsPrimaryNetwork() {
+		if err := oc.addUDNNodeSubnetEgressSNAT(hostSubnets, node); err != nil {
+			return nil, err
+		}
+	}
 	return hostSubnets, nil
 }
 
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index fe490bf912e..9b62fc0b3e1 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -16,6 +16,7 @@ import (
 
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/utils/ptr"
 
 	nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
 
@@ -689,12 +690,16 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		routerPolicyUUID2 = "lrpol2-UUID"
 		staticRouteUUID1  = "sr1-UUID"
 		staticRouteUUID2  = "sr2-UUID"
+		masqSNATUUID1     = "masq-snat1-UUID"
 	)
 	masqIPAddr := dummyMasqueradeIP().IP.String()
 	clusterRouterName := fmt.Sprintf("%s_ovn_cluster_router", netInfo.GetNetworkName())
 	rtosLRPName := fmt.Sprintf("%s%s", ovntypes.RouterToSwitchPrefix, netInfo.GetNetworkScopedName(nodeName))
 	rtosLRPUUID := rtosLRPName + "-UUID"
 	nodeIP := gwConfig.IPAddresses[0].IP.String()
+	masqSNAT := newNATEntry(masqSNATUUID1, "169.254.169.14", nodeSubnet.String(), standardNonDefaultNetworkExtIDs(netInfo))
+	masqSNAT.Match = getMasqueradeManagementIPSNATMatch(dummyMACAddr)
+	masqSNAT.LogicalPort = ptr.To(fmt.Sprintf("rtos-%s_%s", netInfo.GetNetworkName(), nodeName))
 
 	gatewayChassisUUID := fmt.Sprintf("%s-%s-UUID", rtosLRPName, gwConfig.ChassisID)
 	expectedEntities := []libovsdbtest.TestData{
@@ -705,12 +710,14 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 			StaticRoutes: []string{staticRouteUUID1, staticRouteUUID2},
 			Policies:     []string{routerPolicyUUID1, routerPolicyUUID2},
 			ExternalIDs:  standardNonDefaultNetworkExtIDs(netInfo),
+			Nat:          []string{masqSNATUUID1},
 		},
 		&nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.1.1/24"}, MAC: "0a:58:c0:a8:01:01", GatewayChassis: []string{gatewayChassisUUID}},
 		expectedGRStaticRoute(staticRouteUUID1, nodeSubnet.String(), gwRouterJoinIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo),
 		expectedGRStaticRoute(staticRouteUUID2, gwRouterJoinIPAddress().IP.String(), gwRouterJoinIPAddress().IP.String(), nil, nil, netInfo),
 		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(nodeSubnet).String()),
 		expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, masqIPAddr, managementPortIP(nodeSubnet).String()),
+		masqSNAT,
 	}
 	return expectedEntities
 }

From c4969d9fb99901a3447da18ca9b7bd5ef04ad7c1 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 31 Aug 2024 13:07:02 +0200
Subject: [PATCH 43/48] L2: Add ClusterSubnet SNATs on GR

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../secondary_layer2_network_controller.go    | 40 ++++++++++++++++++-
 ...econdary_layer2_network_controller_test.go |  8 ++--
 ...econdary_layer3_network_controller_test.go |  7 ++++
 3 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller.go b/go-controller/pkg/ovn/secondary_layer2_network_controller.go
index 6d0253f6051..455b934c69a 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller.go
@@ -13,7 +13,9 @@ import (
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/generator/udn"
+	libovsdbops "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/libovsdb/ops"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/metrics"
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb"
 	addressset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/address_set"
 	lsm "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/logical_switch_manager"
 	zoneinterconnect "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/ovn/zone_interconnect"
@@ -456,7 +458,14 @@ func (oc *SecondaryLayer2NetworkController) addUpdateLocalNodeEvent(node *corev1
 					errs = append(errs, err)
 					oc.gatewaysFailed.Store(node.Name, true)
 				} else {
-					oc.gatewaysFailed.Delete(node.Name)
+					if util.IsNetworkSegmentationSupportEnabled() && oc.IsPrimaryNetwork() {
+						if err := oc.addUDNClusterSubnetEgressSNAT(gwConfig.hostSubnets, gwManager.gwRouterName, node); err != nil {
+							errs = append(errs, err)
+							oc.gatewaysFailed.Store(node.Name, true)
+						} else {
+							oc.gatewaysFailed.Delete(node.Name)
+						}
+					}
 				}
 			}
 		}
@@ -481,6 +490,35 @@ func (oc *SecondaryLayer2NetworkController) deleteNodeEvent(node *corev1.Node) e
 	return nil
 }
 
+// addUDNClusterSubnetEgressSNAT adds the SNAT on each node's GR in L2 networks
+// snat eth.dst == d6:cf:fd:2c:a6:44 169.254.0.12 10.128.0.0/14
+// snat eth.dst == d6:cf:fd:2c:a6:44 169.254.0.12 2010:100:200::/64
+// these SNATs are required for pod2Egress traffic in LGW mode and pod2SameNode traffic in SGW mode to function properly on UDNs
+// SNAT Breakdown:
+// match = "eth.dst == d6:cf:fd:2c:a6:44"; the MAC here is the mpX interface MAC address for this UDN
+// logicalIP = "10.128.0.0/14"; which is the clustersubnet for this L2 UDN
+// externalIP = "169.254.0.12"; which is the masqueradeIP for this L2 UDN
+// so all in all we want to condionally SNAT all packets that are coming from pods hosted on this node,
+// which are leaving via UDN's mpX interface to the UDN's masqueradeIP.
+func (oc *SecondaryLayer2NetworkController) addUDNClusterSubnetEgressSNAT(localPodSubnets []*net.IPNet, routerName string, node *kapi.Node) error {
+	outputPort := types.GWRouterToJoinSwitchPrefix + routerName
+	nats, err := oc.buildUDNEgressSNAT(localPodSubnets, outputPort, node)
+	if err != nil {
+		return err
+	}
+	if len(nats) == 0 {
+		return nil // nothing to do
+	}
+	router := &nbdb.LogicalRouter{
+		Name: routerName,
+	}
+	if err := libovsdbops.CreateOrUpdateNATs(oc.nbClient, router, nats...); err != nil {
+		return fmt.Errorf("failed to update SNAT for cluster on router: %q for network %q, error: %w",
+			routerName, oc.GetNetworkName(), err)
+	}
+	return nil
+}
+
 type SecondaryL2GatewayConfig struct {
 	config      *util.L3GatewayConfig
 	hostSubnets []*net.IPNet
diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
index 99918270287..84e95981a1b 100644
--- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go
@@ -379,17 +379,19 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		sr1               = "sr1-UUID"
 		sr2               = "sr2-UUID"
 		routerPolicyUUID1 = "lrp1-UUID"
+		masqSNATUUID1     = "masq-snat1-UUID"
 	)
 	gwRouterName := fmt.Sprintf("GR_%s_test-node", netInfo.GetNetworkName())
 	staticRouteOutputPort := ovntypes.GWRouterToExtSwitchPrefix + gwRouterName
 	gwRouterToNetworkSwitchPortName := ovntypes.GWRouterToJoinSwitchPrefix + gwRouterName
 	gwRouterToExtSwitchPortName := fmt.Sprintf("%s%s", ovntypes.GWRouterToExtSwitchPrefix, gwRouterName)
+	masqSNAT := newMasqueradeManagementNATEntry(masqSNATUUID1, "169.254.169.14", layer2Subnet().String(), netInfo)
 
 	var nat []string
 	if config.Gateway.DisableSNATMultipleGWs {
-		nat = append(nat, nat1, perPodSNAT)
+		nat = append(nat, nat1, perPodSNAT, masqSNATUUID1)
 	} else {
-		nat = append(nat, nat1, nat2, nat3)
+		nat = append(nat, nat1, nat2, nat3, masqSNATUUID1)
 	}
 	expectedEntities := []libovsdbtest.TestData{
 		&nbdb.LogicalRouter{
@@ -407,7 +409,7 @@ func expectedLayer2EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 		expectedGRStaticRoute(sr2, ipv4DefaultRoute().String(), nodeGateway().IP.String(), nil, &staticRouteOutputPort, netInfo),
 		expectedGRToExternalSwitchLRP(gwRouterName, netInfo, nodePhysicalIPAddress(), udnGWSNATAddress()),
 		expectedStaticMACBinding(gwRouterName, nextHopMasqueradeIP()),
-
+		masqSNAT,
 		expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP().IP.String(), managementPortIP(layer2Subnet()).String()),
 	}
 
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index 9b62fc0b3e1..f76c07f2fcf 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -787,6 +787,13 @@ func udnGWSNATAddress() *net.IPNet {
 	}
 }
 
+func newMasqueradeManagementNATEntry(uuid string, externalIP string, logicalIP string, netInfo util.NetInfo) *nbdb.NAT {
+	masqSNAT := newNATEntry(uuid, "169.254.169.14", layer2Subnet().String(), standardNonDefaultNetworkExtIDs(netInfo))
+	masqSNAT.Match = fmt.Sprintf("eth.dst == %s", dummyMACAddr)
+	masqSNAT.LogicalPort = ptr.To(fmt.Sprintf("rtoj-GR_%s_%s", netInfo.GetNetworkName(), nodeName))
+	return masqSNAT
+}
+
 func newNATEntry(uuid string, externalIP string, logicalIP string, extIDs map[string]string) *nbdb.NAT {
 	return &nbdb.NAT{
 		UUID:        uuid,

From eef8c9d5c0cb41881f8f2b6f2f35404d1d473dce Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 14 Sep 2024 13:44:37 +0200
Subject: [PATCH 44/48] Add gatewayport to nonIC conditionalSNAT

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../pkg/ovn/base_network_controller.go        | 32 +++++++++++++++++++
 ...econdary_layer3_network_controller_test.go |  3 ++
 2 files changed, 35 insertions(+)

diff --git a/go-controller/pkg/ovn/base_network_controller.go b/go-controller/pkg/ovn/base_network_controller.go
index f0a9a2651f4..dd0107ee888 100644
--- a/go-controller/pkg/ovn/base_network_controller.go
+++ b/go-controller/pkg/ovn/base_network_controller.go
@@ -307,6 +307,38 @@ func (bnc *BaseNetworkController) syncNodeClusterRouterPort(node *kapi.Node, hos
 		return err
 	}
 
+	if util.IsNetworkSegmentationSupportEnabled() &&
+		bnc.IsPrimaryNetwork() && !config.OVNKubernetesFeature.EnableInterconnect &&
+		bnc.TopologyType() == types.Layer3Topology {
+		// since in nonIC the ovn_cluster_router is distributed, we must specify the gatewayPort for the
+		// conditional SNATs to signal OVN which gatewayport should be chosen if there are mutiple distributed
+		// gateway ports. Now that the LRP is created, let's update the NATs to reflect that.
+		lrp := nbdb.LogicalRouterPort{
+			Name: lrpName,
+		}
+		logicalRouterPort, err := libovsdbops.GetLogicalRouterPort(bnc.nbClient, &lrp)
+		if err != nil {
+			return fmt.Errorf("failed to fetch gatewayport %s for network %q on node %q, err: %w",
+				lrpName, bnc.GetNetworkName(), node.Name, err)
+		}
+		gatewayPort := logicalRouterPort.UUID
+		p := func(item *nbdb.NAT) bool {
+			return item.ExternalIDs[types.NetworkExternalID] == bnc.GetNetworkName() &&
+				item.LogicalPort != nil && *item.LogicalPort == lrpName && item.Match != ""
+		}
+		nonICConditonalSNATs, err := libovsdbops.FindNATsWithPredicate(bnc.nbClient, p)
+		if err != nil {
+			return fmt.Errorf("failed to fetch conditional NATs %s for network %q on node %q, err: %w",
+				lrpName, bnc.GetNetworkName(), node.Name, err)
+		}
+		for _, nat := range nonICConditonalSNATs {
+			nat.GatewayPort = &gatewayPort
+		}
+		if err := libovsdbops.CreateOrUpdateNATs(bnc.nbClient, &logicalRouter, nonICConditonalSNATs...); err != nil {
+			return fmt.Errorf("failed to fetch conditional NATs %s for network %q on node %q, err: %w",
+				lrpName, bnc.GetNetworkName(), node.Name, err)
+		}
+	}
 	return nil
 }
 
diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
index f76c07f2fcf..4595d0fd14e 100644
--- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
+++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go
@@ -700,6 +700,9 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC
 	masqSNAT := newNATEntry(masqSNATUUID1, "169.254.169.14", nodeSubnet.String(), standardNonDefaultNetworkExtIDs(netInfo))
 	masqSNAT.Match = getMasqueradeManagementIPSNATMatch(dummyMACAddr)
 	masqSNAT.LogicalPort = ptr.To(fmt.Sprintf("rtos-%s_%s", netInfo.GetNetworkName(), nodeName))
+	if !config.OVNKubernetesFeature.EnableInterconnect {
+		masqSNAT.GatewayPort = ptr.To(fmt.Sprintf("rtos-%s_%s", netInfo.GetNetworkName(), nodeName) + "-UUID")
+	}
 
 	gatewayChassisUUID := fmt.Sprintf("%s-%s-UUID", rtosLRPName, gwConfig.ChassisID)
 	expectedEntities := []libovsdbtest.TestData{

From 65b12e1ac4690106f3e47e2793c2b18fde20c8c7 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sun, 1 Sep 2024 00:58:44 +0200
Subject: [PATCH 45/48] E2E: Enable L3 pod2Egress test on LGW

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 test/e2e/network_segmentation.go          |  4 ++--
 test/e2e/network_segmentation_services.go | 20 ++++++++------------
 2 files changed, 10 insertions(+), 14 deletions(-)

diff --git a/test/e2e/network_segmentation.go b/test/e2e/network_segmentation.go
index 8bcfba9d783..b9d8a70802a 100644
--- a/test/e2e/network_segmentation.go
+++ b/test/e2e/network_segmentation.go
@@ -697,8 +697,8 @@ var _ = Describe("Network Segmentation", func() {
 				DescribeTable(
 					"can be accessed to from the pods running in the Kubernetes cluster",
 					func(netConfigParams networkAttachmentConfigParams, clientPodConfig podConfiguration) {
-						if isLocalGWModeEnabled() {
-							const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/pull/4554"
+						if netConfigParams.topology == "layer2" && IsGatewayModeLocal() {
+							const upstreamIssue = "https://github.com/ovn-org/ovn-kubernetes/issues/4686"
 							e2eskipper.Skipf(
 								"These tests are known to fail on Local Gateway deployments. Upstream issue: %s", upstreamIssue,
 							)
diff --git a/test/e2e/network_segmentation_services.go b/test/e2e/network_segmentation_services.go
index b849c5696ba..8cb80c72874 100644
--- a/test/e2e/network_segmentation_services.go
+++ b/test/e2e/network_segmentation_services.go
@@ -88,10 +88,6 @@ var _ = Describe("Network Segmentation: services", func() {
 			// -  UDN service --> default-network:
 			//   + clusterIP fails
 			//   + nodeIP:nodePort fails FOR NOW, when we only target the local node (*)
-			//
-			// (*) TODO connect to node ports on other nodes too once ovnkube-node fully supports UDN,
-			//     that is when https://github.com/ovn-org/ovn-kubernetes/pull/4648 and
-			//     https://github.com/ovn-org/ovn-kubernetes/pull/4554 merge
 
 			"should be reachable through their cluster IP and node port",
 			func(
@@ -205,7 +201,7 @@ var _ = Describe("Network Segmentation: services", func() {
 				By(fmt.Sprintf("Creating a backend pod in the default network on node %s", serverPodNodeName))
 				defaultLabels := map[string]string{"app": "default-app"}
 
-				_, err = createPod(f, "backend-pod-default", serverPodNodeName,
+				defaultServerPod, err := createPod(f, "backend-pod-default", serverPodNodeName,
 					defaultNetNamespace, []string{"/agnhost", "netexec", "--udp-port=" + fmt.Sprint(serviceTargetPort)}, defaultLabels,
 					func(pod *v1.Pod) {
 						pod.Spec.Containers[0].Ports = []v1.ContainerPort{{ContainerPort: (serviceTargetPort), Protocol: "UDP"}}
@@ -233,13 +229,13 @@ var _ = Describe("Network Segmentation: services", func() {
 				Expect(err).NotTo(HaveOccurred())
 
 				By("Verify the UDN client connection to the default network service")
-				checkNoConnectionToClusterIPs(f, udnClientPod2, defaultService)
-				// TODO uncomment below when below OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported
-				// checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[0], "server node", defaultServerPod.Name)
-				// TODO change line below to checkConnectionToNodePort when we have full UDN support in ovnkube-node
-				checkNoConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[1], "local node")
-				// TODO uncomment below when OVN_DISABLE_SNAT_MULTIPLE_GWS=true is supported
-				// checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[2], "other node", defaultServerPod.Name)
+				checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[0], "server node", defaultServerPod.Name)
+				checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[1], "local node", defaultServerPod.Name)
+				checkConnectionToNodePort(f, udnClientPod2, defaultService, &nodes.Items[2], "other node", defaultServerPod.Name)
+				// FIXME(tssurya): https://github.com/ovn-org/ovn-kubernetes/issues/4687
+				if !IsGatewayModeLocal() {
+					checkNoConnectionToClusterIPs(f, udnClientPod2, defaultService)
+				}
 			},
 
 			Entry(

From eab7fe70c13eb29c9f76a08c2b2fbbe181e7c41e Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Sat, 14 Sep 2024 21:59:20 +0200
Subject: [PATCH 46/48] Fix flaky UT: Reset TestConfig
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fixes the race caused due to wrong
env variables:

"Ipsec"=false "Name"="global" "NbCfg"=0 "NbCfgTimestamp"=0 "Options"={} "SbCfg"=0 "SbCfgTimestamp"=0 "SSL"=null}
2024-09-14T19:19:37.1319444Z I0914 19:19:37.131592   36517 factory.go:426] Starting watch factory
2024-09-14T19:19:37.2336722Z
2024-09-14T19:19:37.2337376Z ␛[91m␛[1m• Failure [0.399 seconds]␛[0m
2024-09-14T19:19:37.2338325Z BaseSecondaryNetworkController
2024-09-14T19:19:37.2340029Z ␛[90m/home/runner/work/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/ovn/base_network_controller_secondary_test.go:15␛[0m
2024-09-14T19:19:37.2341722Z   ␛[91m␛[1mshould return networkID from one of the nodes node [It]␛[0m
2024-09-14T19:19:37.2343462Z   ␛[90m/home/runner/work/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/ovn/base_network_controller_secondary_test.go:20␛[0m
2024-09-14T19:19:37.2344525Z
2024-09-14T19:19:37.2344853Z   ␛[91mExpected success, but got an error:
2024-09-14T19:19:37.2345559Z       <*fmt.wrapError | 0xc0021c1080>:
2024-09-14T19:19:37.2347262Z       invalid subnet cnfiguration: error while parsing subnets: network bluenet is attempting to use ipv4 subnets but the cluster does not support ipv4
2024-09-14T19:19:37.2348820Z       {
2024-09-14T19:19:37.2350533Z           msg: "invalid subnet cnfiguration: error while parsing subnets: network bluenet is attempting to use ipv4 subnets but the cluster does not support ipv4",
2024-09-14T19:19:37.2352311Z           err: <*errors.errorString | 0xc002846550>{
2024-09-14T19:19:37.2354219Z               s: "error while parsing subnets: network bluenet is attempting to use ipv4 subnets but the cluster does not support ipv4",
2024-09-14T19:19:37.2355424Z           },
2024-09-14T19:19:37.2355894Z       }␛[0m
2024-09-14T19:19:37.2356120Z
2024-09-14T19:19:37.2357207Z   /home/runner/work/ovn-kubernetes/ovn-kubernetes/go-controller/pkg/ovn/base_network_controller_secondary_test.go:30

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 .../pkg/ovn/base_network_controller_secondary_test.go        | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/go-controller/pkg/ovn/base_network_controller_secondary_test.go b/go-controller/pkg/ovn/base_network_controller_secondary_test.go
index 9487aa77692..c51b15587c8 100644
--- a/go-controller/pkg/ovn/base_network_controller_secondary_test.go
+++ b/go-controller/pkg/ovn/base_network_controller_secondary_test.go
@@ -7,6 +7,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
 	ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
 	"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
@@ -17,6 +18,10 @@ var _ = Describe("BaseSecondaryNetworkController", func() {
 		nad = ovntest.GenerateNAD("bluenet", "rednad", "greenamespace",
 			types.Layer3Topology, "100.128.0.0/16", types.NetworkRolePrimary)
 	)
+	BeforeEach(func() {
+		// Restore global default values before each testcase
+		Expect(config.PrepareTestConfig()).To(Succeed())
+	})
 	It("should return networkID from one of the nodes node", func() {
 		fakeOVN := NewFakeOVN(false)
 		fakeOVN.start(&corev1.Node{

From 4329d4666f344ff29cd353cd4d38405b2ca938e3 Mon Sep 17 00:00:00 2001
From: Surya Seetharaman <suryaseetharaman.9@gmail.com>
Date: Mon, 16 Sep 2024 14:15:27 +0200
Subject: [PATCH 47/48] Use fedora 41 instead of rawhide

Signed-off-by: Surya Seetharaman <suryaseetharaman.9@gmail.com>
---
 dist/images/Dockerfile.fedora | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/images/Dockerfile.fedora b/dist/images/Dockerfile.fedora
index 301a9c8ede1..7fc3b189c34 100644
--- a/dist/images/Dockerfile.fedora
+++ b/dist/images/Dockerfile.fedora
@@ -9,13 +9,13 @@
 # are built locally and included in the image (instead of the rpm)
 #
 
-FROM fedora:rawhide
+FROM fedora:41
 
 USER root
 
 ENV PYTHONDONTWRITEBYTECODE yes
 
-ARG ovnver=ovn-24.03.90-7.fc42
+ARG ovnver=ovn-24.03.90-7.fc41
 # Automatically populated when using docker buildx
 ARG TARGETPLATFORM
 ARG BUILDPLATFORM

From fc3f48f59d8bb594af3146e3444c527b50caf068 Mon Sep 17 00:00:00 2001
From: Nadia Pinaeva <n.m.pinaeva@gmail.com>
Date: Fri, 13 Sep 2024 18:25:59 +0200
Subject: [PATCH 48/48] Add new ovnkube-observ binary to the image.

Signed-off-by: Nadia Pinaeva <n.m.pinaeva@gmail.com>
---
 Dockerfile | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Dockerfile b/Dockerfile
index 77293eb71b8..a147a95d4d2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -57,6 +57,7 @@ COPY --from=builder /go/src/github.com/openshift/ovn-kubernetes/go-controller/_o
 COPY --from=builder /go/src/github.com/openshift/ovn-kubernetes/go-controller/_output/go/bin/ovndbchecker /usr/bin/
 COPY --from=builder /go/src/github.com/openshift/ovn-kubernetes/go-controller/_output/go/bin/ovnkube-trace /usr/bin/
 COPY --from=builder /go/src/github.com/openshift/ovn-kubernetes/go-controller/_output/go/bin/hybrid-overlay-node /usr/bin/
+COPY --from=builder /go/src/github.com/openshift/ovn-kubernetes/go-controller/_output/go/bin/ovnkube-observ /usr/bin/
 
 # Copy RHEL-8 and RHEL-9 shim binaries where the CNO's ovnkube-node container startup script can find them
 RUN mkdir -p /usr/libexec/cni/rhel9