func allPaths(from, to nom.UID, visited map[nom.UID]distAndLinks) ( [][]nom.Link, int) { if from == to { return nil, 0 } todl, ok := visited[to] if !ok { return nil, -1 } var paths [][]nom.Link for _, l := range todl.BackLinks { lfn, _ := nom.ParsePortUID(l.From) prevpaths, _ := allPaths(from, nom.UID(lfn), visited) if len(prevpaths) == 0 { paths = append(paths, []nom.Link{l}) continue } for _, p := range prevpaths { paths = append(paths, append(p, l)) } } return paths, todl.Dist }
func decodeLLDP(b []byte) (nom.Node, nom.Port, error) { h := lldp.NewLinkDiscoveryProtocolWithBuf(b) size := h.Size() tlvb := b[size:] chTLV := lldp.NewChassisMacTLVWithBuf(tlvb) if chTLV.Size() == 0 { return nom.Node{}, nom.Port{}, errors.New("decodeLLDP: no chassis id") } n := nom.Node{ MACAddr: chTLV.MacAddr(), } size += chTLV.Size() tlvb = b[size:] pTLV := lldp.NewLinkDiscoveryTLVWithBuf(tlvb) if pTLV.Size() == 0 || pTLV.Type() != uint8(lldp.TLV_PORT_ID) { return nom.Node{}, nom.Port{}, errors.New("decodeLLDP: no port id") } v := pTLV.Value() if v[0] != uint8(lldp.PORT_TLV_IFACE_NAME) { return nom.Node{}, nom.Port{}, errors.New("decodeLLDP: no port iface name") } portUID := nom.UID(v[1:]) nID, pID := nom.ParsePortUID(portUID) n.ID = nID return n, nom.Port{ID: pID, Node: n.UID()}, nil }
// NodesCentralized returns the nodes with outgoing links so far. // // Note that this methods should be used only when the GraphBuilderCentralized // is in use. func NodesCentralized(ctx bh.RcvContext) (nodes []nom.UID) { ctx.Dict(GraphDict).ForEach(func(k string, v interface{}) bool { nodes = append(nodes, nom.UID(k)) return true }) return nodes }
func TestAddP2PPath(t *testing.T) { ctx := buildTopologyForTest() p := addHandler{} msg := &bh.MockMsg{ MsgData: nom.AddPath{}, } err := p.Rcv(msg, ctx) if err == nil { t.Error("no error on invalid path") } msg.MsgData = nom.AddPath{ Path: nom.Path{ Pathlets: []nom.Pathlet{ { Match: nom.Match{ Fields: []nom.Field{ nom.InPort("n1$$0"), }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{"n6$$3"}, }, }, }, }, Priority: 1, }, } if err := p.Rcv(msg, ctx); err != nil { t.Errorf("cannot install flows for path: %v", err) } if len(ctx.CtxMsgs) == 0 { t.Error("no flows installed") } iports := []nom.UID{"n1$$0", "n2$$1", "n4$$1", "n6$$1"} oports := []nom.UID{"n1$$1", "n2$$2", "n4$$2", "n6$$3"} for i, msg := range ctx.CtxMsgs { add := msg.Data().(nom.AddFlowEntry) if add.Flow.Priority != 1 { t.Errorf("invalid flow priority: actual=%v want=1", add.Flow.Priority) } iport, ok := add.Flow.Match.InPort() if !ok { t.Errorf("flow #%v has no in ports", i) } else if nom.UID(iport) != iports[i] { t.Errorf("invalid input port on flow #%v: actual=%v want=%v", i, iport, iports[i]) } oport := add.Flow.Actions[0].(nom.ActionForward).Ports[0] if oport != oports[i] { t.Errorf("invalid output port on flow #%v: actual=%v want=%v", i, oport, oports[i]) } } }
// ShortestPathCentralized calculates the shortest path from node "from" to node // "to" according to the state stored in GraphDict by the // GraphBuilderCentralized. // // This method is not go-routine safe and must be called within a handler of the // application that uses the GraphBuilderCentralized as a handler. Otherwise, // the user needs to synchronize the two. func ShortestPathCentralized(from, to nom.UID, ctx bh.RcvContext) ( paths [][]nom.Link, length int) { if from == to { return nil, 0 } visited := make(map[nom.UID]distAndLinks) visited[from] = distAndLinks{Dist: 0} pq := nodeAndDistSlice{{Dist: 0, Node: from}} heap.Init(&pq) dict := ctx.Dict(GraphDict) for len(pq) != 0 { nd := heap.Pop(&pq).(nodeAndDist) if nd.Node == to { continue } nodeLinks := make(map[nom.UID][]nom.Link) if v, err := dict.Get(string(nd.Node)); err == nil { nodeLinks = v.(map[nom.UID][]nom.Link) } nd.Dist = visited[nd.Node].Dist for _, links := range nodeLinks { for _, l := range links { nid, _ := nom.ParsePortUID(l.To) ton := nom.UID(nid) if dl, ok := visited[ton]; ok { switch { case nd.Dist+1 < dl.Dist: glog.Fatalf("invalid distance in BFS") case nd.Dist+1 == dl.Dist: dl.BackLinks = append(dl.BackLinks, l) visited[ton] = dl } continue } visited[ton] = distAndLinks{ Dist: nd.Dist + 1, BackLinks: []nom.Link{l}, } ndto := nodeAndDist{ Dist: nd.Dist + 1, Node: ton, } heap.Push(&pq, ndto) } } } return allPaths(from, to, visited) }
func (p Poller) Rcv(msg bh.Msg, ctx bh.RcvContext) error { dict := ctx.Dict(driversDict) var nds []nodeDrivers dict.ForEach(func(k string, v interface{}) bool { node := nom.UID(k) query := nom.FlowStatsQuery{ Node: node, } sendToMaster(query, node, ctx) nd := v.(nodeDrivers) updated := false for i := range nd.Drivers { // TODO(soheil): remove the hardcoded value. if nd.Drivers[i].OutPings > MaxPings { ctx.SendToBee(nom.NodeDisconnected{ Node: nom.Node{ID: nom.NodeID(node)}, Driver: nd.Drivers[i].Driver, }, ctx.ID()) continue } ctx.SendToBee(nom.Ping{}, nd.Drivers[i].BeeID) nd.Drivers[i].OutPings++ updated = true } if updated { nds = append(nds, nd) } return true }) for _, nd := range nds { if err := dict.Put(string(nd.Node.ID), nd); err != nil { glog.Warningf("error in encoding drivers: %v", err) } } return nil }
func genFlowsForPathlet(p nom.Pathlet, inport nom.UID, priority uint16, ctx bh.RcvContext) (flows []nom.FlowEntry, outports []nom.UID, err error) { fwdnps := forwardNodes(p.Actions) for _, ports := range fwdnps { outports = append(outports, ports...) } floodns := floodNodes(p.Actions) for n, p := range floodns { outports = append(outports, outPortsFromFloodNode(n, p, ctx)...) } port, matchHasPort := p.Match.InPort() if matchHasPort { if inport != nom.Nil && inport != nom.UID(port) { return nil, nil, fmt.Errorf("path: two different inports %v and %v", inport, port) } inport = nom.UID(port) } m := p.Match if inport != nom.Nil && !matchHasPort { m = p.Match.Clone() m.Fields = append(m.Fields, nom.InPort(inport)) } noinMatch := p.Match.Clone() for f := range m.Fields { if _, ok := m.Fields[f].(nom.InPort); ok { noinMatch.Fields = append(noinMatch.Fields[:f], m.Fields[f+1:]...) break } } nofwdActions := make([]nom.Action, 0, len(p.Actions)) for _, a := range p.Actions { switch a.(type) { case nom.ActionForward, nom.ActionFlood: continue default: nofwdActions = append(nofwdActions, a) } } var innodes []nom.UID if inport != nom.Nil { innodes = []nom.UID{nom.NodeFromPortUID(inport)} } else { innodes = discovery.NodesCentralized(ctx) } for _, inn := range innodes { for _, outp := range outports { outn := nom.NodeFromPortUID(outp) sps, l := discovery.ShortestPathCentralized(inn, outn, ctx) if l < 0 { // TODO(soheil): maybe just log this and continue installing other // flows. return nil, nil, fmt.Errorf("path: no path found from %v to %v", inport, outp) } if l == 0 { m := noinMatch.Clone() if inport != nom.Nil { m.Fields = append(m.Fields, nom.InPort(inport)) } flow := nom.FlowEntry{ Node: outn, Match: m, Actions: p.Actions, Priority: priority, } flows = append(flows, flow) continue } // TODO(soheil): maybe install multiple paths. lastInPort := inport for _, link := range sps[0] { m := noinMatch.Clone() if lastInPort != nom.Nil { m.Fields = append(m.Fields, nom.InPort(lastInPort)) } var a []nom.Action a = append(a, nofwdActions...) a = append(a, nom.ActionForward{Ports: []nom.UID{link.From}}) flow := nom.FlowEntry{ Node: nom.NodeFromPortUID(link.From), Match: m, Actions: a, Priority: priority, } flows = append(flows, flow) lastInPort = link.To } m := noinMatch.Clone() if lastInPort != nom.Nil { m.Fields = append(m.Fields, nom.InPort(lastInPort)) } flow := nom.FlowEntry{ Node: outn, Match: m, Actions: p.Actions, Priority: priority, } flows = append(flows, flow) } } return flows, outports, nil }
func (d *of12Driver) ofMatch(m nom.Match) (of12.Match, error) { ofm := of12.NewOXMatch() for _, f := range m.Fields { switch f := f.(type) { case nom.InPort: p, ok := d.nomPorts[nom.UID(f)] if !ok { return of12.Match{}, fmt.Errorf("of12Driver: nom port not found %v", f) } off := of12.NewOxmInPort() off.SetInPort(p) ofm.AddFields(off.OxmField) case nom.EthDst: if f.Mask == nom.MaskNoneMAC { off := of12.NewOxmEthDst() off.SetMacAddr([6]byte(f.Addr)) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmEthDstMasked() off.SetMacAddr([6]byte(f.Addr)) off.SetMask([6]byte(f.Mask)) ofm.AddFields(off.OxmField) } case nom.EthSrc: if f.Mask == nom.MaskNoneMAC { off := of12.NewOxmEthSrc() off.SetMacAddr([6]byte(f.Addr)) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmEthSrcMasked() off.SetMacAddr([6]byte(f.Addr)) off.SetMask([6]byte(f.Mask)) ofm.AddFields(off.OxmField) } case nom.EthType: off := of12.NewOxmEthType() off.SetType(uint16(f)) ofm.AddFields(off.OxmField) case nom.IPProto: off := of12.NewOxmIpProto() off.SetProto(uint8(f)) ofm.AddFields(off.OxmField) case nom.IPv4Src: if f.Mask == nom.MaskNoneIPV4 { off := of12.NewOxmIpV4Src() off.SetAddr(f.Addr) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmIpV4SrcMasked() off.SetAddr(f.Addr) off.SetMask(f.Mask) ofm.AddFields(off.OxmField) } case nom.IPv4Dst: if f.Mask == nom.MaskNoneIPV4 { off := of12.NewOxmIpV4Dst() off.SetAddr(f.Addr) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmIpV4DstMasked() off.SetAddr(f.Addr) off.SetMask(f.Mask) ofm.AddFields(off.OxmField) } case nom.IPv6Src: if f.Mask == nom.MaskNoneIPV6 { off := of12.NewOxmIpV6Src() off.SetAddr(f.Addr) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmIpV6SrcMasked() off.SetAddr(f.Addr) off.SetMask(f.Mask) ofm.AddFields(off.OxmField) } case nom.IPv6Dst: if f.Mask == nom.MaskNoneIPV6 { off := of12.NewOxmIpV6Dst() off.SetAddr(f.Addr) ofm.AddFields(off.OxmField) } else { off := of12.NewOxmIpV6DstMasked() off.SetAddr(f.Addr) off.SetMask(f.Mask) ofm.AddFields(off.OxmField) } case nom.TransportPortSrc: off := of12.NewOxmTcpSrc() off.SetPort(uint16(f)) ofm.AddFields(off.OxmField) case nom.TransportPortDst: off := of12.NewOxmTcpDst() off.SetPort(uint16(f)) ofm.AddFields(off.OxmField) default: return of12.Match{}, fmt.Errorf("of12Driver: %#v is not supported", f) } } return ofm.Match, nil }
func (d *of10Driver) ofMatch(m nom.Match) (of10.Match, error) { ofm := of10.NewMatch() w := of10.PFW_ALL for _, f := range m.Fields { switch f := f.(type) { case nom.InPort: p, ok := d.nomPorts[nom.UID(f)] if !ok { return of10.Match{}, fmt.Errorf("of10Driver: nom port not found %v", f) } ofm.SetInPort(p) w &= ^of10.PFW_IN_PORT case nom.EthDst: if f.Mask != nom.MaskNoneMAC { return of10.Match{}, fmt.Errorf("of10Driver: masked ethernet address is not supported") } ofm.SetDlDst([6]byte(f.Addr)) w &= ^of10.PFW_DL_DST case nom.EthSrc: if f.Mask != nom.MaskNoneMAC { return of10.Match{}, fmt.Errorf("of10Driver: masked ethernet address is not supported") } ofm.SetDlSrc([6]byte(f.Addr)) w &= ^of10.PFW_DL_SRC case nom.EthType: ofm.SetDlType(uint16(f)) w &= ^of10.PFW_DL_TYPE case nom.IPv4Src: w &= ^of10.PFW_NW_SRC_MASK ofm.SetNwSrc(f.Addr.Uint()) mask := f.Mask.PopCount() if mask == 32 { w |= of10.PFW_NW_SRC_MASK } else { w |= of10.FlowWildcards(mask << uint(of10.PFW_NW_SRC_SHIFT)) } case nom.IPv4Dst: w &= ^of10.PFW_NW_DST_MASK ofm.SetNwDst(f.Addr.Uint()) mask := f.Mask.PopCount() if mask == 32 { w |= of10.PFW_NW_DST_MASK } else { w |= of10.FlowWildcards(mask << uint(of10.PFW_NW_DST_SHIFT)) } case nom.TransportPortSrc: ofm.SetTpSrc(uint16(f)) w &= ^of10.PFW_TP_SRC case nom.TransportPortDst: ofm.SetTpDst(uint16(f)) w &= ^of10.PFW_TP_DST case nom.IPv6Src, nom.IPv6Dst: return of10.Match{}, fmt.Errorf("of10Driver: IPv6 not supported") } } ofm.SetWildcards(uint32(w)) return ofm, nil }