Пример #1
0
func (o *OnDemandProbeServer) getProbe(n *graph.Node, capture *api.Capture) (*probes.FlowProbe, error) {
	capType := ""
	if capture.Type != "" {
		types := common.CaptureTypes[n.Metadata()["Type"].(string)].Allowed
		for _, t := range types {
			if t == capture.Type {
				capType = t
				break
			}
		}
		if capType == "" {
			return nil, fmt.Errorf("Capture type %v not allowed on this node: %v", capture, n)
		}
	} else {
		// no capture type defined for this type of node, ex: ovsport
		c, ok := common.CaptureTypes[n.Metadata()["Type"].(string)]
		if !ok {
			return nil, nil
		}
		capType = c.Default
	}
	probe := o.Probes.GetProbe(capType)
	if probe == nil {
		return nil, fmt.Errorf("Unable to find probe for this capture type: %v", capType)
	}

	fprobe := probe.(probes.FlowProbe)
	return &fprobe, nil
}
Пример #2
0
func (o *OnDemandProbeListener) registerProbe(n *graph.Node, capture *api.Capture) bool {
	o.Lock()
	defer o.Unlock()

	if _, ok := o.activeProbes[n.ID]; ok {
		logging.GetLogger().Debugf("A probe already exists for %s", n.ID)
		return false
	}

	if _, ok := n.Metadata()["Type"]; !ok {
		logging.GetLogger().Infof("Do not register flow probe, type not supported %v", n)
		return false
	}

	fprobe := o.getProbe(n, capture)
	if fprobe == nil {
		logging.GetLogger().Errorf("Failed to register flow probe, unknown type %v", n)
		return false
	}

	ft := o.fta.Alloc(fprobe.AsyncFlowPipeline)
	if err := fprobe.RegisterProbe(n, capture, ft); err != nil {
		logging.GetLogger().Debugf("Failed to register flow probe: %s", err.Error())
		o.fta.Release(ft)
		return false
	}

	o.activeProbes[n.ID] = ft
	o.captures[n.ID] = capture

	logging.GetLogger().Debugf("New active probe on: %v", n)
	return true
}
Пример #3
0
func (o *OvsSFlowProbesHandler) RegisterProbe(n *graph.Node, capture *api.Capture, ft *flow.Table) error {
	if isOvsBridge(n) {
		err := o.RegisterProbeOnBridge(n.Metadata()["UUID"].(string), string(n.ID), ft)
		if err != nil {
			return err
		}
	}
	return nil
}
Пример #4
0
func (o *OvsSFlowProbesHandler) UnregisterProbe(n *graph.Node) error {
	if isOvsBridge(n) {
		err := o.unregisterProbe(n.Metadata()["UUID"].(string))
		if err != nil {
			return err
		}
	}
	return nil
}
Пример #5
0
func (mapper *OpenContrailMapper) OnNodeDeleted(n *graph.Node) {
	name, ok := n.Metadata()["Name"]
	if !ok {
		return
	}
	if n.ID == mapper.vHost.ID {
		logging.GetLogger().Debugf("Removed %s", name.(string))
		mapper.vHost = nil
	}
}
Пример #6
0
func (o *OnDemandProbeClient) unregisterProbe(node *graph.Node) bool {
	msg := shttp.NewWSMessage(ondemand.Namespace, "CaptureStop", ondemand.CaptureQuery{NodeID: string(node.ID)})

	if !o.wsServer.SendWSMessageTo(msg, node.Host()) {
		logging.GetLogger().Errorf("Unable to send message to agent: %s", node.Host())
		return false
	}

	return true
}
Пример #7
0
func (o *OnDemandProbeClient) onNodeEvent(n *graph.Node) {
	if state, ok := n.Metadata()["State/FlowCapture"]; ok && state.(string) == "ON" {
		return
	}

	for _, capture := range o.captures {
		if o.matchGremlinExpr(n, capture.GremlinQuery) {
			go o.registerProbe(n, capture)
		}
	}
}
Пример #8
0
func (o *OnDemandProbeServer) OnNodeDeleted(n *graph.Node) {
	if state, ok := n.Metadata()["State/FlowCapture"]; !ok || state.(string) == "OFF" {
		return
	}

	if o.unregisterProbe(n) {
		metadata := n.Metadata()
		metadata["State/FlowCapture"] = "OFF"
		delete(metadata, "CaptureID")
		o.Graph.SetMetadata(n, metadata)
	}
}
Пример #9
0
func (o *OnDemandProbeListener) OnNodeDeleted(n *graph.Node) {
	if !o.isActive(n) {
		return
	}

	if o.unregisterProbe(n) {
		metadata := n.Metadata()
		metadata["State/FlowCapture"] = "OFF"
		delete(metadata, "CaptureID")
		o.Graph.SetMetadata(n, metadata)
	}
}
Пример #10
0
func (mapper *OpenContrailMapper) linkToVhost(node *graph.Node) {
	name := node.Metadata()["Name"].(string)
	if mapper.vHost != nil {
		md := graph.Metadata{"RelationType": "layer2"}
		if !mapper.graph.AreLinked(node, mapper.vHost, md) {
			logging.GetLogger().Debugf("Link %s to %s", name, mapper.vHost.Metadata()["Name"].(string))
			mapper.graph.Link(node, mapper.vHost, md)
		}
	} else {
		logging.GetLogger().Debugf("Add node %s to pending link list", name)
		mapper.pendingLinks = append(mapper.pendingLinks, node)
	}
}
Пример #11
0
func (mapper *NeutronMapper) EnhanceNode(node *graph.Node) {
	mac, ok := node.Metadata()["MAC"]
	if !ok {
		return
	}

	portMd, f := mapper.cache.Get(mac.(string))
	// If port metadatas have not changed, we return
	if f && portMd == retrievePortMetadata(node.Metadata()) {
		return
	}

	mapper.nodeUpdaterChan <- node.ID
}
Пример #12
0
func (o *OvsSFlowProbesHandler) RegisterProbe(n *graph.Node, capture *api.Capture, ft *flow.Table) error {
	tid, ok := n.Metadata()["TID"]
	if !ok {
		return fmt.Errorf("No TID for node %v", n)
	}

	if isOvsBridge(n) {
		err := o.RegisterProbeOnBridge(n.Metadata()["UUID"].(string), tid.(string), ft)
		if err != nil {
			return err
		}
	}
	return nil
}
Пример #13
0
func getGoPacketFirstLayerType(n *graph.Node) gopacket.LayerType {
	switch n.Metadata()["Type"].(string) {
	case "bridge", "bond", "can", "dummy", "hsr", "ifb", "macvlan", "macvtap",
		"veth", "vlan", "vxlan", "gretap", "ip6gretap", "geneve":
		return layers.LayerTypeEthernet
	case "ipoib", "vcan", "ipip", "ipvlan", "lowpan":
		return layers.LayerTypeIPv4
	case "gre":
		return flow.LayerTypeInGRE
	case "ip6tnl", "ip6gre", "sit":
		return layers.LayerTypeIPv6
	}
	return layers.LayerTypeLinuxSLL
}
Пример #14
0
func (u *NetLinkProbe) handleIntfIsChild(intf *graph.Node, link netlink.Link) {
	// handle pending relationship
	u.linkPendingChildren(intf, int64(link.Attrs().Index))

	// interface being a part of a bridge
	if link.Attrs().MasterIndex != 0 {
		u.linkIntfToIndex(intf, int64(link.Attrs().MasterIndex))
	}

	if link.Attrs().ParentIndex != 0 {
		if _, ok := intf.Metadata()["Vlan"]; ok {
			u.linkIntfToIndex(intf, int64(int64(link.Attrs().ParentIndex)))
		}
	}
}
Пример #15
0
func (mapper *OpenContrailMapper) enhanceNode(node *graph.Node) {
	// To break update loops
	if _, ok := node.Metadata()["ExtID/attached-mac"]; ok {
		return
	}

	ifType, ok := node.Metadata()["Type"]
	if !ok {
		return
	}

	if ifType != "host" && ifType != "netns" {
		mapper.nodeUpdaterChan <- node.ID
	}
}
Пример #16
0
func (mapper *NeutronMapper) updateNode(node *graph.Node, attrs *Attributes) {
	mapper.graph.Lock()

	tr := mapper.graph.StartMetadataTransaction(node)
	tr.AddMetadata("Manager", "neutron")

	if attrs.PortID != "" {
		tr.AddMetadata("Neutron/PortID", attrs.PortID)
	}

	if attrs.TenantID != "" {
		tr.AddMetadata("Neutron/TenantID", attrs.TenantID)
	}

	if attrs.NetworkID != "" {
		tr.AddMetadata("Neutron/NetworkID", attrs.NetworkID)
	}

	if attrs.NetworkName != "" {
		tr.AddMetadata("Neutron/NetworkName", attrs.NetworkName)
	}

	if segID, err := strconv.Atoi(attrs.VNI); err != nil && segID > 0 {
		tr.AddMetadata("Neutron/VNI", uint64(segID))
	}

	var registerLink *FabricRegisterLinkWSMessage
	if vm, ok := node.Metadata()["ExtID/vm-uuid"]; ok {
		if mac, ok := node.Metadata()["ExtID/attached-mac"]; ok {
			if path := mapper.graph.LookupShortestPath(node, graph.Metadata{"Type": "tun"}, graph.Metadata{"RelationType": "layer2"}); len(path) > 0 {
				registerLink = &FabricRegisterLinkWSMessage{
					ParentNodeID:   path[len(path)-1].ID,
					ParentMetadata: graph.Metadata{"Type": "host", "InstanceID": vm},
					ChildMetadata:  graph.Metadata{"Type": "device", "MAC": mac},
				}
			}
		}
	}
	tr.Commit()
	mapper.graph.Unlock()

	if registerLink != nil {
		msg := shttp.NewWSMessage(FabricNamespace, "RegisterLink", registerLink)
		mapper.wsClient.SendWSMessage(msg)
	}
}
Пример #17
0
func (t *TIDMapper) setTID(parent, child *graph.Node) {
	if t, ok := child.Metadata()["Type"]; !ok || t == "" {
		return
	}

	if tid, ok := parent.Metadata()["TID"]; ok {
		tid = tid.(string) + child.Metadata()["Name"].(string) + child.Metadata()["Type"].(string)
		u, _ := uuid.NewV5(uuid.NamespaceOID, []byte(tid.(string)))
		t.Graph.AddMetadata(child, "TID", u.String())
	}
}
Пример #18
0
func getGoPacketFirstLayerType(n *graph.Node) gopacket.LayerType {
	if encapType, ok := n.Metadata()["EncapType"]; ok {
		switch encapType.(string) {
		case "ether":
			return layers.LayerTypeEthernet
		case "gre":
			return flow.LayerTypeInGRE
		case "sit", "ipip":
			return layers.LayerTypeIPv4
		case "tunnel6", "gre6":
			return layers.LayerTypeIPv6
		default:
			logging.GetLogger().Warningf("Encapsulation unknown %s on link %s, defaulting to Ethernet", encapType, n.Metadata()["Name"])
		}
	} else {
		logging.GetLogger().Warningf("EncapType not found on link %s, defaulting to Ethernet", n.Metadata()["Name"])
	}
	return layers.LayerTypeEthernet
}
Пример #19
0
func (p *GoPacketProbesHandler) RegisterProbe(n *graph.Node, capture *api.Capture, ft *flow.Table) error {
	name, ok := n.Metadata()["Name"]
	if !ok || name == "" {
		return fmt.Errorf("No name for node %v", n)
	}

	encapType, ok := n.Metadata()["EncapType"]
	if !ok || encapType == "" {
		return fmt.Errorf("No EncapType for node %v", n)
	}

	tid, ok := n.Metadata()["TID"]
	if !ok {
		return fmt.Errorf("No TID for node %v", n)
	}

	id := string(n.ID)
	ifName := name.(string)

	if _, ok = p.probes[id]; ok {
		return fmt.Errorf("Already registered %s", ifName)
	}

	port, ok := n.Metadata()["MPLSUDPPort"].(int)
	if ok {
		// All gopacket instance of this agent will classify UDP packets coming
		// from UDP port MPLSUDPPort as MPLS whatever the source interface
		layers.RegisterUDPPortLayerType(layers.UDPPort(port), layers.LayerTypeMPLS)
		logging.GetLogger().Infof("MPLSoUDP port: %v", port)
	}

	probe := &GoPacketProbe{
		NodeTID:   tid.(string),
		state:     common.StoppedState,
		flowTable: ft,
	}

	p.probesLock.Lock()
	p.probes[id] = probe
	p.probesLock.Unlock()
	p.wg.Add(1)

	go func() {
		defer p.wg.Done()

		probe.run(p.graph, n, capture)
	}()

	return nil
}
Пример #20
0
func NewNetNSContextByNode(g *graph.Graph, n *graph.Node) (*common.NetNSContext, error) {
	name, ok := n.Metadata()["Name"]
	if !ok || name == "" {
		return nil, fmt.Errorf("No name for node %v", n)
	}
	ifName := name.(string)

	nodes := g.LookupShortestPath(n, graph.Metadata{"Type": "host"}, graph.Metadata{"RelationType": "ownership"})
	if len(nodes) == 0 {
		return nil, fmt.Errorf("Failed to determine probePath for %s", ifName)
	}

	for _, node := range nodes {
		if node.Metadata()["Type"] == "netns" {
			name := node.Metadata()["Name"].(string)
			path := node.Metadata()["Path"].(string)
			logging.GetLogger().Debugf("Switching to namespace %s (path: %s)", name, path)

			return common.NewNetNsContext(path)
		}
	}

	return nil, nil
}
Пример #21
0
// onNodeEvent set TID
// TID is UUIDV5(ID/UUID) of "root" node like host, netns, ovsport, fabric
// for other nodes TID is UUIDV5(rootTID + Name + Type)
func (t *TIDMapper) onNodeEvent(n *graph.Node) {
	if _, ok := n.Metadata()["TID"]; !ok {
		if tp, ok := n.Metadata()["Type"]; ok {
			switch tp.(string) {
			case "host":
				t.hostID = n.ID
				t.Graph.AddMetadata(n, "TID", string(n.ID))

				t.setChildrenTID(n)
			case "netns":
				tid := string(t.hostID) + n.Metadata()["Path"].(string) + tp.(string)
				u, _ := uuid.NewV5(uuid.NamespaceOID, []byte(tid))
				t.Graph.AddMetadata(n, "TID", u.String())

				t.setChildrenTID(n)
			case "ovsport":
				tid := string(t.hostID) + n.Metadata()["UUID"].(string) + tp.(string)
				u, _ := uuid.NewV5(uuid.NamespaceOID, []byte(tid))
				t.Graph.AddMetadata(n, "TID", u.String())

				t.setChildrenTID(n)
			default:
				if n.Metadata()["Probe"] == "fabric" {
					t.Graph.AddMetadata(n, "TID", string(n.ID))
				} else {
					parents := t.Graph.LookupParents(n, graph.Metadata{}, graph.Metadata{"RelationType": "ownership"})
					if len(parents) > 1 {
						logging.GetLogger().Errorf("A should always only have one ownership parent: %v", n)
					} else if len(parents) == 1 {
						t.setTID(parents[0], n)
					}
				}
			}
		}
	}
}
Пример #22
0
func (u *NetLinkProbe) handleIntfIsVeth(intf *graph.Node, link netlink.Link) {
	if link.Type() != "veth" {
		return
	}

	if index, ok := intf.Metadata()["PeerIfIndex"]; ok {
		peerResolver := func() error {
			// re get the interface from the graph since the interface could have been deleted
			if u.Graph.GetNode(intf.ID) == nil {
				return errors.New("Node not found")
			}

			// got more than 1 peer, unable to find the right one, wait for the other to discover
			peer := u.Graph.LookupFirstNode(graph.Metadata{"IfIndex": index.(int64), "Type": "veth"})
			if peer != nil && !u.Graph.AreLinked(peer, intf) {
				u.Graph.Link(peer, intf, graph.Metadata{"RelationType": "layer2", "Type": "veth"})
				return nil
			}
			return errors.New("Nodes not linked")
		}

		if index.(int64) > intf.Metadata()["IfIndex"].(int64) {
			if err := peerResolver(); err != nil {
				retryFnc := func() error {
					if u.isRunning() == false {
						return nil
					}
					u.Graph.Lock()
					defer u.Graph.Unlock()
					return peerResolver()
				}
				go common.Retry(retryFnc, 10, 200*time.Millisecond)
			}
		}
	}
}
Пример #23
0
func (o *OnDemandProbeListener) getProbe(n *graph.Node, capture *api.Capture) *FlowProbe {
	capType := ""
	if capture.Type != "" {
		types := common.CaptureTypes[n.Metadata()["Type"].(string)].Allowed
		for _, t := range types {
			if t == capture.Type {
				capType = t
				break
			}
		}
		if capType == "" {
			return nil
		}
	} else {
		capType = common.CaptureTypes[n.Metadata()["Type"].(string)].Default
	}
	probe := o.Probes.GetProbe(capType)
	if probe == nil {
		return nil
	}

	fprobe := probe.(FlowProbe)
	return &fprobe
}
Пример #24
0
func (o *OnDemandProbeServer) registerProbe(n *graph.Node, capture *api.Capture) bool {
	logging.GetLogger().Debugf("Attempting to register probe on node %s", n.Metadata()["Name"].(string))

	if o.isActive(n) {
		logging.GetLogger().Debugf("A probe already exists for %s", n.ID)
		return false
	}

	if _, ok := n.Metadata()["Type"]; !ok {
		logging.GetLogger().Infof("Unable to register flow probe type of node unknown %v", n)
		return false
	}

	if _, ok := n.Metadata()["TID"]; !ok {
		logging.GetLogger().Infof("Unable to register flow probe without node TID %v", n)
		return false
	}

	o.Lock()
	defer o.Unlock()

	fprobe, err := o.getProbe(n, capture)
	if fprobe == nil {
		if err != nil {
			logging.GetLogger().Error(err.Error())
		}
		return false
	}

	ft := o.fta.Alloc(fprobe.AsyncFlowPipeline)
	ft.SetNodeTID(n.Metadata()["TID"].(string))

	if err := fprobe.RegisterProbe(n, capture, ft); err != nil {
		logging.GetLogger().Debugf("Failed to register flow probe: %s", err.Error())
		o.fta.Release(ft)
		return false
	}

	o.activeProbes[n.ID] = ft
	o.captures[n.ID] = capture

	logging.GetLogger().Debugf("New active probe on: %v", n)
	return true
}
Пример #25
0
func (p *GoPacketProbesHandler) RegisterProbe(n *graph.Node, capture *api.Capture, ft *flow.Table) error {
	name, ok := n.Metadata()["Name"]
	if !ok || name == "" {
		return fmt.Errorf("No name for node %v", n)
	}

	id := string(n.ID)
	ifName := name.(string)

	if _, ok := p.probes[id]; ok {
		return fmt.Errorf("Already registered %s", ifName)
	}

	nodes := p.graph.LookupShortestPath(n, graph.Metadata{"Type": "host"}, graph.Metadata{"RelationType": "ownership"})
	if len(nodes) == 0 {
		return fmt.Errorf("Failed to determine probePath for %s", ifName)
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	origns, err := netns.Get()
	if err != nil {
		return fmt.Errorf("Error while getting current ns: %s", err.Error())
	}
	defer origns.Close()

	for _, node := range nodes {
		if node.Metadata()["Type"] == "netns" {
			name := node.Metadata()["Name"].(string)
			path := node.Metadata()["Path"].(string)
			logging.GetLogger().Debugf("Switching to namespace %s (path: %s)", name, path)

			newns, err := netns.GetFromPath(path)
			if err != nil {
				return fmt.Errorf("Error while opening ns %s (path: %s): %s", name, path, err.Error())
			}
			defer newns.Close()

			if err := netns.Set(newns); err != nil {
				return fmt.Errorf("Error while switching from root ns to %s (path: %s): %s", name, path, err.Error())
			}
			defer netns.Set(origns)
		}
	}

	probe := &GoPacketProbe{
		NodeUUID:  id,
		state:     common.StoppedState,
		flowTable: ft,
	}

	switch capture.Type {
	case "pcap":
		handle, err := pcap.OpenLive(ifName, snaplen, true, time.Second)
		if err != nil {
			return fmt.Errorf("Error while opening device %s: %s", ifName, err.Error())
		}

		if err := handle.SetBPFFilter(capture.BPFFilter); err != nil {
			return fmt.Errorf("BPF Filter failed: %s", err)
		}

		probe.handle = handle
		probe.packetSource = gopacket.NewPacketSource(handle, getGoPacketFirstLayerType(n))

		logging.GetLogger().Infof("PCAP Capture type %s started on %s", capture.Type, n.Metadata()["Name"])
	default:
		var handle *AFPacketHandle
		fnc := func() error {
			handle, err = NewAFPacketHandle(ifName, snaplen)
			if err != nil {
				return fmt.Errorf("Error while opening device %s: %s", ifName, err.Error())
			}
			return nil
		}

		if err = common.Retry(fnc, 2, 100*time.Millisecond); err != nil {
			return err
		}

		probe.handle = handle
		probe.packetSource = gopacket.NewPacketSource(handle, getGoPacketFirstLayerType(n))

		logging.GetLogger().Infof("AfPacket Capture started on %s", n.Metadata()["Name"])
	}

	p.probesLock.Lock()
	p.probes[id] = probe
	p.probesLock.Unlock()
	p.wg.Add(1)

	go func() {
		defer p.wg.Done()

		probe.start()
	}()

	return nil
}
Пример #26
0
func isOvsBridge(n *graph.Node) bool {
	return n.Metadata()["UUID"] != "" && n.Metadata()["Type"] == "ovsbridge"
}
Пример #27
0
func (p *GoPacketProbe) run(g *graph.Graph, n *graph.Node, capture *api.Capture) error {
	atomic.StoreInt64(&p.state, common.RunningState)

	g.RLock()
	ifName := n.Metadata()["Name"].(string)
	firstLayerType := getGoPacketFirstLayerType(n)

	nscontext, err := topology.NewNetNSContextByNode(g, n)
	g.RUnlock()

	defer nscontext.Close()

	if err != nil {
		return err
	}

	switch capture.Type {
	case "pcap":
		handle, err := pcap.OpenLive(ifName, snaplen, true, time.Second)
		if err != nil {
			return fmt.Errorf("Error while opening device %s: %s", ifName, err.Error())
		}

		if err := handle.SetBPFFilter(capture.BPFFilter); err != nil {
			return fmt.Errorf("BPF Filter failed: %s", err)
		}

		p.handle = handle
		p.packetSource = gopacket.NewPacketSource(handle, handle.LinkType())

		logging.GetLogger().Infof("PCAP Capture started on %s with First layer: %s", ifName, firstLayerType)
	default:
		var handle *AFPacketHandle
		fnc := func() error {
			handle, err = NewAFPacketHandle(ifName, snaplen)
			if err != nil {
				return fmt.Errorf("Error while opening device %s: %s", ifName, err.Error())
			}
			return nil
		}

		if err = common.Retry(fnc, 2, 100*time.Millisecond); err != nil {
			return err
		}

		p.handle = handle
		p.packetSource = gopacket.NewPacketSource(handle, firstLayerType)

		logging.GetLogger().Infof("AfPacket Capture started on %s with First layer: %s", ifName, firstLayerType)
	}

	// leave the namespace, stay lock in the current thread
	nscontext.Quit()

	packetsChan := p.flowTable.Start()
	defer p.flowTable.Stop()

	p.feedFlowTable(packetsChan)

	return nil
}
Пример #28
0
func (u *NetLinkProbe) addLinkToTopology(link netlink.Link) {
	logging.GetLogger().Debugf("Link \"%s(%d)\" added", link.Attrs().Name, link.Attrs().Index)

	u.Graph.Lock()
	defer u.Graph.Unlock()

	driver, _ := u.ethtool.DriverName(link.Attrs().Name)
	if driver == "" && link.Type() == "bridge" {
		driver = "bridge"
	}

	metadata := graph.Metadata{
		"Name":      link.Attrs().Name,
		"Type":      link.Type(),
		"EncapType": link.Attrs().EncapType,
		"IfIndex":   int64(link.Attrs().Index),
		"MAC":       link.Attrs().HardwareAddr.String(),
		"MTU":       int64(link.Attrs().MTU),
		"Driver":    driver,
	}

	if link.Type() == "veth" {
		stats, err := u.ethtool.Stats(link.Attrs().Name)
		if err != nil {
			logging.GetLogger().Errorf("Unable get stats from ethtool (%s): %s", link.Attrs().Name, err.Error())
		} else if index, ok := stats["peer_ifindex"]; ok {
			metadata["PeerIfIndex"] = int64(index)
		}
	}

	ipv4 := u.getLinkIPs(link, netlink.FAMILY_V4)
	if len(ipv4) > 0 {
		metadata["IPV4"] = ipv4
	}

	ipv6 := u.getLinkIPs(link, netlink.FAMILY_V6)
	if len(ipv6) > 0 {
		metadata["IPV6"] = ipv6
	}

	if vlan, ok := link.(*netlink.Vlan); ok {
		metadata["Vlan"] = vlan.VlanId
	}

	if (link.Attrs().Flags & net.FlagUp) > 0 {
		metadata["State"] = "UP"
	} else {
		metadata["State"] = "DOWN"
	}

	var intf *graph.Node

	switch driver {
	case "bridge":
		intf = u.addBridgeLinkToTopology(link, metadata)
	case "openvswitch":
		intf = u.addOvsLinkToTopology(link, metadata)
		// always prefer Type from ovs
		metadata["Type"] = intf.Metadata()["Type"]
	default:
		intf = u.addGenericLinkToTopology(link, metadata)
	}

	// merge metadata if the interface returned is not a new one
	if intf != nil {
		m := intf.Metadata()

		updated := false
		for k, nv := range metadata {
			if ov, ok := m[k]; ok && nv == ov {
				continue
			}
			m[k] = nv
			updated = true
		}

		if updated {
			u.Graph.SetMetadata(intf, m)
		}
	}
}