func (h *intentHandler) Rcv(msg bh.Msg, ctx bh.RcvContext) error { hrq := msg.Data().(http.HTTPRequest) if hrq.AppName == "intent" && hrq.Verb == "build" { spd := shortestPathData{} err := json.Unmarshal(hrq.Data, &spd) if err != nil { glog.Errorf("Host list JSON unmarshaling: %v", err) return err } fmt.Println(spd) fmt.Println(discovery.ShortestPathCentralized(spd.From, spd.To, ctx)) hrs := http.HTTPResponse{ AppName: "host", Data: []byte{'A'}, } err = ctx.Reply(msg, hrs) if err != nil { glog.Errorf("Replay error: %v", err) return err } } return nil }
// The bee hander func BeeHandler( beehiveMessage beehive.Msg, beeContext beehive.RcvContext) error { // beehiveMessage is an envelope around the Hello message. // You can retrieve the Hello, using msg.Data() and then // you need to assert that its a MessageToBee. message := beehiveMessage.Data().(MessageToBee) // Using ctx.Dict you can get (or create) a dictionary. dict := beeContext.Dict("beehive-app-dict") value, err := dict.Get(message.DestinationBee) logger.Trace.Printf("[BeeHandler] Message sent to bee with id (%s) \n", message.DestinationBee) count := 0 if err == nil { // No error mean there is already an item with given key count = value.(int) } count++ logger.Trace.Printf("[BeeHandler] Count = %d\n", count) logger.Trace.Printf("[BeeHandler] Calculate fib number %d\n", message.FibNumber) Fib(message.FibNumber) beeContext.Reply(beehiveMessage, count) return dict.Put(message.DestinationBee, count) }
func (h *httpHostListHandler) Rcv(msg bh.Msg, ctx bh.RcvContext) error { hrq := msg.Data().(http.HTTPRequest) if hrq.AppName == "host" && hrq.Verb == "list" { dict := ctx.Dict(hostDict) v, err := dict.Get("hsts") hsts := []nom.Host{} if err == nil { hsts = v.([]nom.Host) } data, err := json.Marshal(hsts) if err != nil { glog.Errorf("Host list JSON marshaling: %v", err) return err } fmt.Println(hsts) hrs := http.HTTPResponse{ AppName: "host", Data: data, } err = ctx.Reply(msg, hrs) if err != nil { glog.Errorf("Replay error: %v", err) return err } } return nil }
// RcvfDetached receives the message and the context. func RcvfDetached(msg beehive.Msg, ctx beehive.RcvContext) error { // msg is an envelope around the Hello message. // You can retrieve the Hello, using msg.Data() and then // you need to assert that its a Hello. hello := msg.Data().(HelloDetached) // Using ctx.Dict you can get (or create) a dictionary. dict := ctx.Dict("hello_dict") // Using Get(), you can get the value associated with // a key in the dictionary. Keys are always string // and values are generic interface{}'s. v, err := dict.Get(hello.Name) // If there is an error, the entry is not in the // dictionary. Otherwise, we set cnt based on // the value we already have in the dictionary // for that name. cnt := 0 if err == nil { cnt = v.(int) } // Now we increment the count. cnt++ // Reply to the message with the count of hellos. ctx.Reply(msg, HelloCount{Name: hello.Name, Count: cnt}) // Finally we update the count stored in the dictionary. return dict.Put(hello.Name, cnt) }
func (h AckHandler) Rcv(msg beehive.Msg, ctx beehive.RcvContext) error { ack := msg.Data().(Ack) acked := Acked{ ID: ack.ID, Queue: ack.Queue, TaskID: ack.TaskID, } key := ack.TaskID.String() ddict := ctx.Dict(dequed) if err := ddict.Del(key); err == nil { ctx.Reply(msg, acked) return nil } // The task might have been moved from dequed to out of order, because of a // timeout. So, we need to search the active dictionary as well. odict := ctx.Dict(ooo) if err := odict.Del(key); err == nil { ctx.Reply(msg, acked) return nil } return ErrNoSuchTask }
func (h EnQHandler) Rcv(msg beehive.Msg, ctx beehive.RcvContext) error { enq := msg.Data().(Enque) dict := ctx.Dict(active) next := TaskID(1) if v, err := dict.Get("_next_"); err == nil { next = v.(TaskID) } key := next.String() task := Task{ Queue: enq.Queue, ID: next, Body: enq.Body, } if err := dict.Put(key, task); err != nil { return err } if err := dict.Put("_next_", next+1); err != nil { return err } enqued := Enqued{ ID: enq.ID, Queue: enq.Queue, TaskID: next, } return ctx.Reply(msg, enqued) }
func (c *Calculator) Rcv(msg bh.Msg, ctx bh.RcvContext) error { op := msg.Data().(Op) res := c.calc(op) fmt.Printf("%d %s %d = %d\n", op.Lhs, op.OpT, op.Rhs, res) ctx.Reply(msg, res) return nil }
func (h Hub) Rcv(msg bh.Msg, ctx bh.RcvContext) error { in := msg.Data().(nom.PacketIn) out := nom.PacketOut{ Node: in.Node, InPort: in.InPort, BufferID: in.BufferID, Packet: in.Packet, Actions: []nom.Action{nom.ActionFlood{}}, } ctx.Reply(msg, out) return nil }
// doDequeTask adds a task to the dequed dictionary and replys to the message. func doDequeTask(msg beehive.Msg, ctx beehive.RcvContext, t Task) error { ctx.Reply(msg, Dequed{ ID: msg.Data().(Deque).ID, Task: t, }) ddict := ctx.Dict(dequed) return ddict.Put(t.ID.String(), dqTask{ Task: t, DequeTime: time.Now(), }) }
func (s *KVStore) Rcv(msg bh.Msg, ctx bh.RcvContext) error { switch data := msg.Data().(type) { case Put: return ctx.Dict(dict).Put(data.Key, data.Val) case Get: v, err := ctx.Dict(dict).Get(string(data)) if err != nil { return errKeyNotFound } ctx.Reply(msg, Result{Key: string(data), Val: v.(string)}) return nil case Del: return ctx.Dict(dict).Del(string(data)) } return errInvalid }
func (r LoadBalancer) Rcv(msg bh.Msg, ctx bh.RcvContext) error { switch dm := msg.Data().(type) { case setup: return registerEndhosts2(ctx) case nom.LinkAdded: link := InterAreaLink(dm) ctx.Emit(link) return r.GraphBuilderCentralized.Rcv(msg, ctx) case nom.LinkDeleted: return r.GraphBuilderCentralized.Rcv(msg, ctx) default: in := msg.Data().(nom.PacketIn) src := in.Packet.SrcMAC() dst := in.Packet.DstMAC() d := ctx.Dict(mac2port) load_dict := ctx.Dict(load_on_nodes) if dst.IsLLDP() { return nil } // FIXME: Hardcoding the hardware address at the moment srck := src.Key() if dst.IsBroadcast() || dst.IsMulticast() { fmt.Printf("Load Balancer: Received Broadcast or Multicast from %v\n", src) return nil } sn := in.Node dstk := dst.Key() dst_port, dst_err := d.Get(dstk) if dst_err != nil { fmt.Printf("Load Balancer: Cant find dest node %v\n", dstk) res, query_err := ctx.Sync(context.TODO(), InterAreaQuery{Src: srck, Dst: dstk}) if query_err != nil { fmt.Printf("Load Balancer: received error when querying! %v\n", query_err) } fmt.Printf("Load Balancer: received response succesfully - %v\n", res) dst_port = res.(nom.UID) } dn, _ := nom.ParsePortUID(dst_port.(nom.UID)) p := dst_port.(nom.UID) if sn != nom.UID(dn) { paths, _ := discovery.ShortestPathCentralized(sn, nom.UID(dn), ctx) opt_path := paths[0] min_load := calculate_load(ctx, paths[0]) for _, path := range paths[1:] { load := calculate_load(ctx, path) if load < min_load { opt_path = path min_load = load } } fmt.Printf("Load Balancer: Routing flow from %v to %v - %v, %v\n", sn, nom.UID(dn), opt_path, len(opt_path)) p = opt_path[0].From } // Forward flow entry add_forward := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: dst, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, }, } ctx.Reply(msg, add_forward) // Reverse flow entry add_reverse := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: src, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{in.InPort}, }, }, }, } ctx.Reply(msg, add_reverse) // Updating the load on this node // FIXME: This is a naive approach, ideally should update when flowentry // is added/removed, but flowentry deleted is not implemented yet if load, err := load_dict.Get(string(in.Node)); err == nil { load_dict.Put(string(in.Node), load.(int)+1) } else { load_dict.Put(string(in.Node), 1) } out := nom.PacketOut{ Node: in.Node, InPort: in.InPort, BufferID: in.BufferID, Packet: in.Packet, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, } ctx.Reply(msg, out) } return nil }
func (r RouterM) Rcv(msg bh.Msg, ctx bh.RcvContext) error { switch dm := msg.Data().(type) { case area_setup: fmt.Printf("Adding to ctx: %v\n", ctx) d := ctx.Dict(cDict) d.Put("master", "master") return master_setup(ctx) case InterAreaLink: link := nom.Link(dm) nf, _ := nom.ParsePortUID(link.From) k := string(nf) nt, _ := nom.ParsePortUID(link.To) k2 := string(nt) d := ctx.Dict(node2area) nf_area, err := d.Get(k) nt_area, err2 := d.Get(k2) if err != nil || err2 != nil { fmt.Printf("Node does not belong to any existing areas! %v, %v, %v\n", err, err2, ctx) return nil } if nf_area != nt_area { link.From = nom.UID(nf_area.(string) + "$$" + strings.Replace(string(link.From), "$$", tmpD, 1)) link.To = nom.UID(nt_area.(string) + "$$" + strings.Replace(string(link.To), "$$", tmpD, 1)) fmt.Printf("Received Links from different areas, building graphs for %v, %v\n", link.From, link.To) ctx.Emit(nom.LinkAdded(link)) } return nil case InterAreaQuery: query := msg.Data().(InterAreaQuery) srck := query.Src dstk := query.Dst d := ctx.Dict(mac2area) src_area, src_err := d.Get(srck) dst_area, dst_err := d.Get(dstk) if src_err != nil || dst_err != nil { fmt.Printf("Error retriving area info: %v, %v\n", src_err, dst_err) return nil } paths, shortest_len := discovery.ShortestPathCentralized(nom.UID(src_area.(string)), nom.UID(dst_area.(string)), ctx) if shortest_len <= 0 { fmt.Printf("No route exists between area %v and area %v\n", src_area, dst_area) return nil } fmt.Printf("Path between area %v and area %v returned %v\n", src_area, dst_area, paths) for _, path := range paths { if len(path) != shortest_len { continue } else { _, port := nom.ParsePortUID(path[0].From) port_conv := strings.Replace(string(port), tmpD, "$$", 1) fmt.Printf("Sending converted port: %v\n", port_conv) ctx.Reply(msg, nom.UID(port_conv)) break } } return nil case setupM: m := msg.Data().(setupM) return registerEndhostsAll(ctx, m.area) case nom.LinkAdded: // link := InterAreaLink(dm) cd := ctx.Dict(cDict) _, cerr := cd.Get("master") if cerr != nil { // ctx.Emit(link) return r.GraphBuilderCentralized.Rcv(msg, ctx) } else { link := nom.Link(dm) nf, _ := nom.ParsePortUID(link.From) k := string(nf) nt, _ := nom.ParsePortUID(link.To) k2 := string(nt) d := ctx.Dict(node2area) nf_area, err := d.Get(k) nt_area, err2 := d.Get(k2) if err != nil || err2 != nil { fmt.Printf("Node does not belong to any existing areas! %v, %v, %v\n", err, err2, ctx) return nil } if nf_area != nt_area { link.From = nom.UID(nf_area.(string) + "$$" + strings.Replace(string(link.From), "$$", tmpD, 1)) link.To = nom.UID(nt_area.(string) + "$$" + strings.Replace(string(link.To), "$$", tmpD, 1)) fmt.Printf("Received Links from different areas, building graphs for %v, %v\n", link.From, link.To) InterAreaLinkAdded(link, ctx) } } case nom.LinkDeleted: return r.GraphBuilderCentralized.Rcv(msg, ctx) default: in := msg.Data().(nom.PacketIn) src := in.Packet.SrcMAC() dst := in.Packet.DstMAC() d := ctx.Dict(mac2port) if dst.IsLLDP() { return nil } // FIXME: Hardcoding the hardware address at the moment srck := src.Key() _, src_err := d.Get(srck) if src_err != nil { fmt.Printf("Router: Error retrieving hosts %v\n", src) } if dst.IsBroadcast() || dst.IsMulticast() { fmt.Printf("Router: Received Broadcast or Multicast from %v\n", src) return nil } sn := in.Node dstk := dst.Key() dst_port, dst_err := d.Get(dstk) if dst_err != nil { fmt.Printf("Router: Cant find dest node %v\n", dstk) res, query_err := ctx.Sync(context.TODO(), InterAreaQuery{Src: srck, Dst: dstk}) if query_err != nil { fmt.Printf("Router: received error when querying! %v\n", query_err) } fmt.Printf("Router: received response succesfully - %v\n", res) dst_port = res.(nom.UID) } dn, _ := nom.ParsePortUID(dst_port.(nom.UID)) p := dst_port.(nom.UID) if sn != nom.UID(dn) { paths, shortest_len := discovery.ShortestPathCentralized(sn, nom.UID(dn), ctx) fmt.Printf("Router: Path between %v and %v returns %v, %v\n", sn, nom.UID(dn), paths, shortest_len) for _, path := range paths { if len(path) != shortest_len { continue } else { p = path[0].From break } } } // Forward flow entry add_forward := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: dst, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, }, } ctx.Reply(msg, add_forward) // Reverse flow entry add_reverse := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: src, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{in.InPort}, }, }, }, } ctx.Reply(msg, add_reverse) out := nom.PacketOut{ Node: in.Node, InPort: in.InPort, BufferID: in.BufferID, Packet: in.Packet, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, } ctx.Reply(msg, out) } return nil }
func (m MasterController) Rcv(msg bh.Msg, ctx bh.RcvContext) error { switch dm := msg.Data().(type) { case area_setup: fmt.Printf("Adding to ctx: %v\n", ctx) return master_setup(ctx) case InterAreaLink: link := nom.Link(dm) nf, _ := nom.ParsePortUID(link.From) k := string(nf) nt, _ := nom.ParsePortUID(link.To) k2 := string(nt) d := ctx.Dict(node2area) nf_area, err := d.Get(k) nt_area, err2 := d.Get(k2) if err != nil || err2 != nil { fmt.Printf("Node does not belong to any existing areas! %v, %v, %v\n", err, err2, ctx) return nil } if nf_area != nt_area { link.From = nom.UID(nf_area.(string) + "$$" + strings.Replace(string(link.From), "$$", tmpDelimiter, 1)) link.To = nom.UID(nt_area.(string) + "$$" + strings.Replace(string(link.To), "$$", tmpDelimiter, 1)) fmt.Printf("Received Links from different areas, building graphs for %v, %v\n", link.From, link.To) ctx.Emit(nom.LinkAdded(link)) } return nil case nom.LinkAdded: return m.GraphBuilderCentralized.Rcv(msg, ctx) case InterAreaQuery: query := msg.Data().(InterAreaQuery) srck := query.Src dstk := query.Dst d := ctx.Dict(mac2area) src_area, src_err := d.Get(srck) dst_area, dst_err := d.Get(dstk) if src_err != nil || dst_err != nil { fmt.Printf("Error retriving area info: %v, %v\n", src_err, dst_err) return nil } paths, shortest_len := discovery.ShortestPathCentralized(nom.UID(src_area.(string)), nom.UID(dst_area.(string)), ctx) if shortest_len <= 0 { fmt.Printf("No route exists between area %v and area %v\n", src_area, dst_area) return nil } fmt.Printf("Path between area %v and area %v returned %v\n", src_area, dst_area, paths) for _, path := range paths { if len(path) != shortest_len { continue } else { _, port := nom.ParsePortUID(path[0].From) port_conv := strings.Replace(string(port), tmpDelimiter, "$$", 1) fmt.Printf("Sending converted port: %v\n", port_conv) ctx.Reply(msg, nom.UID(port_conv)) break } } return nil // case nom.LinkDeleted: // return r.GraphBuilderCentralized.Rcv(msg, ctx) // default: // in := msg.Data().(nom.PacketIn) // src := in.Packet.SrcMAC() // dst := in.Packet.DstMAC() // // d := ctx.Dict(mac2port) // // if dst.IsLLDP() { // return nil // } // // // FIXME: Hardcoding the hardware address at the moment // srck := src.Key() // _, src_err := d.Get(srck) // if src_err != nil { // fmt.Printf("Router: Error retrieving hosts %v\n", src) // } // // if dst.IsBroadcast() || dst.IsMulticast() { // fmt.Printf("Router: Received Broadcast or Multicast from %v\n", src) // return nil // } // // sn := in.Node // // dstk := dst.Key() // dst_port, dst_err := d.Get(dstk) // if dst_err != nil { // fmt.Printf("Router: Cant find dest node %v\n", dstk) // dst_port, _ = d.Get("default") // } // dn,_ := nom.ParsePortUID(dst_port.(nom.UID)) // p := dst_port.(nom.UID) // // if (sn != nom.UID(dn)){ // // paths, shortest_len := discovery.ShortestPathCentralized(sn, nom.UID(dn), ctx) // fmt.Printf("Router: Path between %v and %v returns %v, %v\n", sn, nom.UID(dn), paths, shortest_len) // // // if shortest_len == -1 { // // borderDict := ctx.Dict(border_node_dict) // // borderDict.ForEach(func(k string, v interface{}) bool{ // // node, _ := nom.ParsePortUID(v.(nom.UID)) // // paths, shortest_len = discovery.ShortestPathCentralized(sn, nom.UID(node), ctx) // // // // if shortest_len == 0{ // // p = v.(nom.UID) // // } // // // TODO: Find the shortest one // // return false // // }) // // } // fmt.Printf("Router: After adjustment length: %v\n", shortest_len) // for _, path := range paths { // if len(path) != shortest_len { // continue // } else { // // p = path[0].From // break // } // } // } // // if src_err == nil { // // // Forward flow entry // add_forward := nom.AddFlowEntry{ // Flow: nom.FlowEntry{ // Node: in.Node, // Match: nom.Match{ // Fields: []nom.Field{ // nom.EthDst{ // Addr: dst, // Mask: nom.MaskNoneMAC, // }, // }, // }, // Actions: []nom.Action{ // nom.ActionForward{ // Ports: []nom.UID{p}, // }, // }, // }, // } // ctx.Reply(msg, add_forward) // // } // // if dst_err == nil { // // // Reverse flow entry // add_reverse := nom.AddFlowEntry{ // Flow: nom.FlowEntry{ // Node: in.Node, // Match: nom.Match{ // Fields: []nom.Field{ // nom.EthDst{ // Addr: src, // Mask: nom.MaskNoneMAC, // }, // }, // }, // Actions: []nom.Action{ // nom.ActionForward{ // Ports: []nom.UID{in.InPort}, // }, // }, // }, // } // ctx.Reply(msg, add_reverse) // // } // // out := nom.PacketOut{ // Node: in.Node, // InPort: in.InPort, // BufferID: in.BufferID, // Packet: in.Packet, // Actions: []nom.Action{ // nom.ActionForward{ // Ports: []nom.UID{p}, // }, // }, // } // ctx.Reply(msg, out) } return nil }
func (h LearningSwitch) Rcv(msg bh.Msg, ctx bh.RcvContext) error { in := msg.Data().(nom.PacketIn) src := in.Packet.SrcMAC() dst := in.Packet.DstMAC() glog.V(2).Infof("received packet in from %v to %v", src, dst) if dst.IsLLDP() { // TODO(soheil): just drop LLDP. glog.Infof("dropped LLDP packet to %v", dst) return nil } if dst.IsBroadcast() || dst.IsMulticast() { return h.Hub.Rcv(msg, ctx) } d := ctx.Dict("mac2port") srck := src.Key() update := false if v, err := d.Get(srck); err == nil { p := v.(nom.UID) if p != in.InPort { update = true // TODO(soheil): maybe add support for multi ports. glog.Infof("%v is moved from port %v to port %v", src, p, in.InPort) } } else { update = true } if update { if err := d.Put(srck, in.InPort); err != nil { glog.Fatalf("cannot serialize port: %v", err) } } dstk := dst.Key() v, err := d.Get(dstk) if err != nil { return h.Hub.Rcv(msg, ctx) } p := v.(nom.UID) add := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: dst, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, }, } ctx.Reply(msg, add) out := nom.PacketOut{ Node: in.Node, InPort: in.InPort, BufferID: in.BufferID, Packet: in.Packet, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, } ctx.Reply(msg, out) return nil }
func (r Router) Rcv(msg bh.Msg, ctx bh.RcvContext) error { switch dm := msg.Data().(type) { case setup: return registerEndhosts(ctx) case nom.LinkAdded: link := InterAreaLink(dm) ctx.Emit(link) return r.GraphBuilderCentralized.Rcv(msg, ctx) case nom.LinkDeleted: return r.GraphBuilderCentralized.Rcv(msg, ctx) default: in := msg.Data().(nom.PacketIn) src := in.Packet.SrcMAC() dst := in.Packet.DstMAC() d := ctx.Dict(mac2port) if dst.IsLLDP() { return nil } // FIXME: Hardcoding the hardware address at the moment srck := src.Key() _, src_err := d.Get(srck) if src_err != nil { fmt.Printf("Router: Error retrieving hosts %v\n", src) } if dst.IsBroadcast() || dst.IsMulticast() { fmt.Printf("Router: Received Broadcast or Multicast from %v\n", src) return nil } sn := in.Node dstk := dst.Key() dst_port, dst_err := d.Get(dstk) if dst_err != nil { fmt.Printf("Router: Cant find dest node %v\n", dstk) res, query_err := ctx.Sync(context.TODO(), InterAreaQuery{Src: srck, Dst: dstk}) if query_err != nil { fmt.Printf("Router: received error when querying! %v\n", query_err) } fmt.Printf("Router: received response succesfully - %v\n", res) dst_port = res.(nom.UID) } dn, _ := nom.ParsePortUID(dst_port.(nom.UID)) p := dst_port.(nom.UID) if sn != nom.UID(dn) { paths, shortest_len := discovery.ShortestPathCentralized(sn, nom.UID(dn), ctx) fmt.Printf("Router: Path between %v and %v returns %v, %v\n", sn, nom.UID(dn), paths, shortest_len) for _, path := range paths { if len(path) != shortest_len { continue } else { p = path[0].From break } } } // Forward flow entry add_forward := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: dst, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, }, } ctx.Reply(msg, add_forward) // Reverse flow entry add_reverse := nom.AddFlowEntry{ Flow: nom.FlowEntry{ Node: in.Node, Match: nom.Match{ Fields: []nom.Field{ nom.EthDst{ Addr: src, Mask: nom.MaskNoneMAC, }, }, }, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{in.InPort}, }, }, }, } ctx.Reply(msg, add_reverse) out := nom.PacketOut{ Node: in.Node, InPort: in.InPort, BufferID: in.BufferID, Packet: in.Packet, Actions: []nom.Action{ nom.ActionForward{ Ports: []nom.UID{p}, }, }, } ctx.Reply(msg, out) } return nil }