Skip to content

Commit

Permalink
Merge pull request #127 from digitalocean/Dumpflows
Browse files Browse the repository at this point in the history
Added a method to dump flows matching a flow
  • Loading branch information
dpraveenraja authored Jan 30, 2024
2 parents 977d985 + aee6491 commit c0f7d42
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 4 deletions.
2 changes: 1 addition & 1 deletion ovs/matchflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (f *MatchFlow) MarshalText() ([]byte, error) {
b = append(b, ',')
}

if f.Cookie > 0 {
if f.Cookie > 0 || f.CookieMask > 0 {
// Hexadecimal cookies and masks are much easier to read.
b = append(b, cookie+"="...)
b = append(b, paddedHexUint64(f.Cookie)...)
Expand Down
18 changes: 18 additions & 0 deletions ovs/matchflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,24 @@ func TestMatchFlowMarshalText(t *testing.T) {
},
s: "udp,in_port=33,nw_dst=192.0.2.1,tp_dst=0xea60/0xffe0,table=55",
},
{
desc: "Test zero cookie match",
f: &MatchFlow{
Cookie: 0,
CookieMask: 0xffffffffffffffff,
Table: 45,
},
s: "cookie=0x0000000000000000/0xffffffffffffffff,table=45",
},
{
desc: "Test match any cookie",
f: &MatchFlow{
Cookie: 0,
CookieMask: 0,
Table: 45,
},
s: "table=45",
},
}

for _, tt := range tests {
Expand Down
21 changes: 18 additions & 3 deletions ovs/openflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,21 @@ func (o *OpenFlowService) DumpTables(bridge string) ([]*Table, error) {
return tables, err
}

// DumpFlows retrieves statistics about all flows for the specified bridge.
// DumpFlowsWithFlowArgs retrieves statistics about all flows for the specified bridge,
// filtering on the specified flow(s), if provided.
// If a table has no active flows and has not been used for a lookup or matched
// by an incoming packet, it is filtered from the output.
func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
// We neeed to add a Matchflow to filter the dumpflow results. For example filter based on table, cookie.
func (o *OpenFlowService) DumpFlowsWithFlowArgs(bridge string, flow *MatchFlow) ([]*Flow, error) {
args := []string{"dump-flows", bridge}
args = append(args, o.c.ofctlFlags...)

if flow != nil {
fb, err := flow.MarshalText()
if err != nil {
return nil, err
}
args = append(args, string(fb))
}
out, err := o.exec(args...)
if err != nil {
return nil, err
Expand All @@ -298,6 +306,13 @@ func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
return flows, err
}

// DumpFlows retrieves statistics about all flows for the specified bridge.
// If a table has no active flows and has not been used for a lookup or matched
// by an incoming packet, it is filtered from the output.
func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
return o.DumpFlowsWithFlowArgs(bridge, nil)
}

// DumpAggregate retrieves statistics about the specified flow attached to the
// specified bridge.
func (o *OpenFlowService) DumpAggregate(bridge string, flow *MatchFlow) (*FlowStats, error) {
Expand Down
110 changes: 110 additions & 0 deletions ovs/openflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,116 @@ NXST_FLOW reply (xid=0x4):
}
}

func TestClientOpenFlowDumpFlowsWithFlowArgs(t *testing.T) {
tests := []struct {
name string
table string
cookie uint64
cookieMask uint64
input string
flows string
want []*Flow
err error
}{
{
name: "test single flow",
input: "br0",
table: "45",
cookie: 0,
cookieMask: 0x0,
flows: `NXST_FLOW reply (xid=0x4):
cookie=0x01, duration=9215.748s, table=45, n_packets=6, n_bytes=480, idle_age=9206, priority=820,in_port=LOCAL actions=mod_vlan_vid:10,output:1
`,
want: []*Flow{
{
Priority: 820,
InPort: PortLOCAL,
Matches: []Match{},
Table: 45,
Cookie: 1,
Actions: []Action{
ModVLANVID(10),
Output(1),
},
},
},
err: nil,
},
{
name: "test multiple flows",
input: "br0",
table: "45",
cookie: 0,
cookieMask: 0x1,
flows: `NXST_FLOW reply (xid=0x4):
cookie=0x0, duration=9215.748s, table=45, n_packets=6, n_bytes=480, idle_age=9206, priority=820,in_port=LOCAL actions=mod_vlan_vid:10,output:1
cookie=0x0, duration=1121991.329s, table=45, n_packets=0, n_bytes=0, priority=110,ip,dl_src=f1:f2:f3:f4:f5:f6 actions=ct(table=51)
`,
want: []*Flow{
{
Priority: 820,
InPort: PortLOCAL,
Matches: []Match{},
Table: 45,
Cookie: 0,
Actions: []Action{
ModVLANVID(10),
Output(1),
},
},
{
Priority: 110,
Protocol: ProtocolIPv4,
Matches: []Match{
DataLinkSource("f1:f2:f3:f4:f5:f6"),
},
Table: 45,
Actions: []Action{
ConnectionTracking("table=51"),
},
},
},
err: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _ := testClient([]OptionFunc{Timeout(1)}, func(cmd string, args ...string) ([]byte, error) {
if want, got := "ovs-ofctl", cmd; want != got {
t.Fatalf("incorrect command:\n- want: %v\n- got: %v",
want, got)
}
filterArg := "cookie=0x0000000000000000/0xffffffffffffffff," + "table=" + tt.table
wantArgs := []string{
"--timeout=1",
"dump-flows",
string(tt.input),
filterArg,
}
if want, got := wantArgs, args; !reflect.DeepEqual(want, got) {
t.Fatalf("incorrect arguments\n- want: %v\n- got: %v",
want, got)
}
return []byte(tt.flows), tt.err
}).OpenFlow.DumpFlowsWithFlowArgs(tt.input, &MatchFlow{Cookie: 0,
CookieMask: 0xffffffffffffffff,
Table: 45})
if len(tt.want) != len(got) {
t.Errorf("got %d", len(got))
t.Errorf("want %d", len(tt.want))
t.Fatal("expected return value to be equal")
}
for i := range tt.want {
if !flowsEqual(tt.want[i], got[i]) {
t.Errorf("got %+v", got[i])
t.Errorf("want %+v", tt.want[i])
t.Fatal("expected return value to be equal")
}
}
})
}
}

func TestClientOpenFlowDumpFlows15(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit c0f7d42

Please sign in to comment.