// Send a packet, creating a corresponding ODP flow rule if possible func (fastdp *FastDatapath) send(fops FlowOp, frame []byte, lock *fastDatapathLock) { // Gather the actions from actionFlowOps, execute any others var dec *EthernetDecoder flow := odp.NewFlowSpec() createFlow := true for _, xfop := range FlattenFlowOp(fops) { switch fop := xfop.(type) { case interface { updateFlowSpec(*odp.FlowSpec) }: fop.updateFlowSpec(&flow) case vetoFlowCreationFlowOp: createFlow = false default: // A foreign FlowOp (e.g. a sleeve forwarding // FlowOp), so send the packet through the // FlowOp interface, decoding the packet // lazily. if dec == nil { dec = fastdp.takeDecoder(lock) dec.DecodeLayers(frame) // If we are sending the packet // through the FlowOp interface, we // mustn't create a flow, as that // could prevent the proper handling // of similar packets in the future. createFlow = false } if len(dec.decoded) != 0 { lock.unlock() fop.Process(frame, dec, false) } } } if dec != nil { // put the decoder back lock.relock() fastdp.dec = dec } if len(flow.Actions) != 0 { lock.relock() checkWarn(fastdp.dp.Execute(frame, nil, flow.Actions)) } if createFlow { lock.relock() // if the fastdp's deleteFlowsCount changed since we // initially locked it, then we might have created a // flow on the basis of stale information. It's fine // to handle one packet like that, but it would be bad // to introduce a stale flow. if lock.deleteFlowsCount == fastdp.deleteFlowsCount { log.Debug("Creating ODP flow ", flow) checkWarn(fastdp.dp.CreateFlow(flow)) } } }
func flagsToFlowSpec(f Flags, dpif *odp.Dpif) (dp odp.DatapathHandle, flow odp.FlowSpec, ok bool) { flow = odp.NewFlowSpec() var inPort string f.StringVar(&inPort, "in-port", "", "key: incoming vport") var ethSrc, ethDst string f.StringVar(ðSrc, "eth-src", "", "key: ethernet source MAC") f.StringVar(ðDst, "eth-dst", "", "key: ethernet destination MAC") var tun tunnelFlags addTunnelFlags(f, &tun, "tunnel-", "tunnel ") var setTun tunnelFlags addTunnelFlags(f, &setTun, "set-tunnel-", "action: set tunnel ") var output string f.StringVar(&output, "output", "", "action: output to vports") args := f.Parse(1, 1) dpp, _ := lookupDatapath(dpif, args[0]) if dpp == nil { return } if inPort != "" { vport, err := dpp.LookupVportByName(inPort) if err != nil { printErr("%s", err) return } flow.AddKey(odp.NewInPortFlowKey(vport.ID)) } // The ethernet flow key is mandatory err := handleEthernetFlowKeyOptions(flow, ethSrc, ethDst) if err != nil { printErr("%s", err) return } flowKey, err := parseTunnelFlags(&tun) if err != nil { printErr("%s", err) return } if !flowKey.Ignored() { flow.AddKey(flowKey) } // Actions are ordered, but flags aren't. As a temporary // hack, we already put SET actions before an OUTPUT action. setTunAttrs, err := parseSetTunnelFlags(&setTun) if err != nil { printErr("%s", err) return } if setTunAttrs != nil { flow.AddAction(*setTunAttrs) } if output != "" { for _, vpname := range strings.Split(output, ",") { vport, err := dpp.LookupVportByName(vpname) if err != nil { printErr("%s", err) return } flow.AddAction(odp.NewOutputAction(vport.ID)) } } return *dpp, flow, true }