diff --git a/ovs/client.go b/ovs/client.go index 0c84664..72be5e0 100644 --- a/ovs/client.go +++ b/ovs/client.go @@ -278,6 +278,14 @@ func Pipe(fn PipeFunc) OptionFunc { } } +// Strict deactivates wildcards for matching purposes when shelling to +// 'ovs-ofctl'. +func Strict() OptionFunc { + return func(c *Client) { + c.ofctlFlags = append(c.ofctlFlags, "--strict") + } +} + const ( // FlowFormatNXMTableID is a flow format which allows Nicira Extended match // with the ability to place a flow in a specific table. diff --git a/ovs/flow.go b/ovs/flow.go index 60f563c..468395d 100644 --- a/ovs/flow.go +++ b/ovs/flow.go @@ -437,6 +437,14 @@ func (f *Flow) MatchFlow() *MatchFlow { } } +// MatchFlowStrict converts Flow into a strict-matching-aware MatchFlow +func (f *Flow) MatchFlowStrict() *MatchFlow { + mf := f.MatchFlow() + mf.Priority = f.Priority + mf.Strict = true + return mf +} + // marshalActions marshals all provided Actions to their text form. func marshalActions(aa []Action) ([]string, error) { fns := make([]func() ([]byte, error), 0, len(aa)) diff --git a/ovs/flow_test.go b/ovs/flow_test.go index 7573203..240b9d0 100644 --- a/ovs/flow_test.go +++ b/ovs/flow_test.go @@ -1203,6 +1203,18 @@ func TestFlowMatchFlow(t *testing.T) { } }) } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + want, got := tt.m, tt.f.MatchFlowStrict() + wantStrict := &(*want) + wantStrict.Strict = true + wantStrict.Priority = tt.f.Priority + if !reflect.DeepEqual(wantStrict, got) { + t.Fatalf("unexpected MatchFlowStrict:\n- want: %#v\n- got: %#v", + wantStrict, got) + } + }) + } } // flowsEqual determines if two possible Flows are equal. diff --git a/ovs/matchflow.go b/ovs/matchflow.go index 445afe9..4db1a1e 100644 --- a/ovs/matchflow.go +++ b/ovs/matchflow.go @@ -37,8 +37,10 @@ var ( // A MatchFlow is an OpenFlow flow intended for flow deletion. It can be marshaled to its textual // form for use with Open vSwitch. type MatchFlow struct { - Protocol Protocol + Strict bool InPort int + Priority int + Protocol Protocol Matches []Match Table int @@ -83,6 +85,11 @@ func (f *MatchFlow) MarshalText() ([]byte, error) { var b []byte + if f.Strict { + b = append(b, priority+"="...) + b = strconv.AppendInt(b, int64(f.Priority), 10) + b = append(b, ',') + } if f.Protocol != "" { b = append(b, f.Protocol...) b = append(b, ',') diff --git a/ovs/openflow.go b/ovs/openflow.go index 2171c26..e318356 100644 --- a/ovs/openflow.go +++ b/ovs/openflow.go @@ -71,8 +71,9 @@ type flowDirective struct { // Possible flowDirective directive values. const ( - dirAdd = "add" - dirDelete = "delete" + dirAdd = "add" + dirDelete = "delete" + dirDeleteStrict = "delete_strict" ) // Add pushes zero or more Flows on to the transaction, to be added by @@ -107,6 +108,21 @@ func (tx *FlowTransaction) Delete(flows ...*MatchFlow) { tx.push(dirDelete, tms...) } +// DeleteStrict is almost the same as Delete, except that the matching process +// will be strict. +func (tx *FlowTransaction) DeleteStrict(flows ...*MatchFlow) { + if tx.err != nil { + return + } + + tms := make([]encoding.TextMarshaler, 0, len(flows)) + for _, f := range flows { + tms = append(tms, f) + } + + tx.push(dirDeleteStrict, tms...) +} + // push pushes zero or more encoding.TextMarshalers on to the transaction // (typically a Flow or MatchFlow). func (tx *FlowTransaction) push(directive string, flows ...encoding.TextMarshaler) { diff --git a/ovs/openflow_test.go b/ovs/openflow_test.go index 6c21276..e659be9 100644 --- a/ovs/openflow_test.go +++ b/ovs/openflow_test.go @@ -143,9 +143,15 @@ func TestClientOpenFlowAddFlowBundleOK(t *testing.T) { } // Flows for deletion - matchFlows := []*MatchFlow{{ - Cookie: 0xdeadbeef, - }} + matchFlows := []*MatchFlow{ + { + Cookie: 0xdeadbeef, + }, + { + Strict: true, + Priority: 0, + }, + } pipe := Pipe(func(stdin io.Reader, cmd string, args ...string) ([]byte, error) { if want, got := "ovs-ofctl", cmd; want != got { @@ -1079,7 +1085,7 @@ func mustVerifyFlowBundle(t *testing.T, stdin io.Reader, flows []*Flow, matchFlo } gotFlows = append(gotFlows, flow) - case dirDelete: + case dirDelete, dirDeleteStrict: gotMatchFlows = append(gotMatchFlows, string(bb[1])) default: t.Fatalf("unexpected directive in flow bundle: %q", keyword)