Skip to content

Commit 44e0cbc

Browse files
committed
ovs: add conjunction action
1 parent ad1cd75 commit 44e0cbc

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed

ovs/action.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ var (
4545
// errResubmitPortInvalid is returned when ResubmitPort is given a port number that is
4646
// invalid per the openflow spec.
4747
errResubmitPortInvalid = errors.New("resubmit port must be between 0 and 65279 inclusive")
48+
49+
// errTooManyDimensions is returned when the specified dimension exceeds the total dimension
50+
// in a conjunction action.
51+
errDimensionTooLarge = errors.New("dimension number exceeds total number of dimensions")
4852
)
4953

5054
// Action strings in lower case, as those are compared to the lower case letters
@@ -145,6 +149,7 @@ func StripVLAN() Action {
145149
// printf-style patterns for marshaling and unmarshaling actions.
146150
const (
147151
patConnectionTracking = "ct(%s)"
152+
patConjunction = "conjunction(%d, %d/%d)"
148153
patModDataLinkDestination = "mod_dl_dst:%s"
149154
patModDataLinkSource = "mod_dl_src:%s"
150155
patModNetworkDestination = "mod_nw_dst:%s"
@@ -372,6 +377,37 @@ func (a *outputAction) GoString() string {
372377
return fmt.Sprintf("ovs.Output(%d)", a.port)
373378
}
374379

380+
// Conjunction associates a flow with a certain conjunction ID to match on more than
381+
// one dimension across multiple set matches.
382+
func Conjunction(id int, dimensionNumber int, dimensionSize int) Action {
383+
return &conjunctionAction{
384+
id: id,
385+
dimensionNumber: dimensionNumber,
386+
dimensionSize: dimensionSize,
387+
}
388+
}
389+
390+
// A conjuctionAction is an Action which is used by Conjunction.
391+
type conjunctionAction struct {
392+
id int
393+
dimensionNumber int
394+
dimensionSize int
395+
}
396+
397+
// MarshalText implements Action.
398+
func (a *conjunctionAction) MarshalText() ([]byte, error) {
399+
if a.dimensionNumber > a.dimensionSize {
400+
return nil, errDimensionTooLarge
401+
}
402+
403+
return bprintf(patConjunction, a.id, a.dimensionNumber, a.dimensionSize), nil
404+
}
405+
406+
// GoString implements Action.
407+
func (a *conjunctionAction) GoString() string {
408+
return fmt.Sprintf("ovs.Conjunction(%d, %d, %d)", a.id, a.dimensionNumber, a.dimensionSize)
409+
}
410+
375411
// Resubmit resubmits a packet for further processing by matching
376412
// flows with the specified port and table. If port or table are zero,
377413
// they are set to empty in the output Action. If both are zero, an

ovs/action_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,50 @@ func TestSetTunnel(t *testing.T) {
531531
}
532532
}
533533

534+
func TestConjunction(t *testing.T) {
535+
var tests = []struct {
536+
desc string
537+
a Action
538+
action string
539+
err error
540+
}{
541+
{
542+
desc: "set conjunction 1/2",
543+
a: Conjunction(123, 1, 2),
544+
action: "conjunction(123, 1/2)",
545+
},
546+
{
547+
desc: "set conjunction 2/2",
548+
a: Conjunction(123, 2, 2),
549+
action: "conjunction(123, 2/2)",
550+
},
551+
{
552+
desc: "set conjunction 3/2",
553+
a: Conjunction(123, 3, 2),
554+
err: errDimensionTooLarge,
555+
},
556+
}
557+
558+
for _, tt := range tests {
559+
t.Run(tt.desc, func(t *testing.T) {
560+
action, err := tt.a.MarshalText()
561+
562+
if want, got := tt.err, err; want != got {
563+
t.Fatalf("unexpected error:\n- want: %v\n- got: %v",
564+
want, got)
565+
}
566+
if err != nil {
567+
return
568+
}
569+
570+
if want, got := tt.action, string(action); want != got {
571+
t.Fatalf("unexpected Action:\n- want: %q\n- got: %q",
572+
want, got)
573+
}
574+
})
575+
}
576+
}
577+
534578
func TestActionGoString(t *testing.T) {
535579
tests := []struct {
536580
a Action
@@ -600,6 +644,10 @@ func TestActionGoString(t *testing.T) {
600644
a: SetTunnel(10),
601645
s: `ovs.SetTunnel(0xa)`,
602646
},
647+
{
648+
a: Conjunction(123, 1, 2),
649+
s: `ovs.Conjunction(123, 1, 2)`,
650+
},
603651
}
604652

605653
for _, tt := range tests {

ovs/actionparser.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,18 @@ func parseAction(s string) (Action, error) {
297297
}
298298
}
299299

300+
// ActionConjunction, with it's id, dimension number, and dimension size
301+
if strings.HasPrefix(s, patConjunction[:len(patConjunction)-10]) {
302+
var id, dimensionNumber, dimensionSize int
303+
n, err := fmt.Sscanf(s, patConjunction, &id, &dimensionNumber, &dimensionSize)
304+
if err != nil {
305+
return nil, err
306+
}
307+
if n > 0 {
308+
return Conjunction(id, dimensionNumber, dimensionSize), nil
309+
}
310+
}
311+
300312
// ActionOutput, with its port number
301313
if strings.HasPrefix(s, patOutput[:len(patOutput)-2]) {
302314
var port int

ovs/actionparser_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,22 @@ func Test_parseAction(t *testing.T) {
281281
s: "set_field:192.168.1.1->arp_spa",
282282
a: SetField("192.168.1.1", "arp_spa"),
283283
},
284+
{
285+
s: "conjunction(123, 1/2)",
286+
a: Conjunction(123, 1, 2),
287+
},
288+
{
289+
s: "conjunction(123, 2/2)",
290+
a: Conjunction(123, 2, 2),
291+
},
292+
{
293+
s: "conjunction(123, 3/2)",
294+
invalid: true,
295+
},
296+
{
297+
s: "conjunxxxxx(123, 3/2)",
298+
invalid: true,
299+
},
284300
}
285301

286302
for _, tt := range tests {

0 commit comments

Comments
 (0)