Skip to content

Commit c0f7d42

Browse files
authored
Merge pull request digitalocean#127 from digitalocean/Dumpflows
Added a method to dump flows matching a flow
2 parents 977d985 + aee6491 commit c0f7d42

File tree

4 files changed

+147
-4
lines changed

4 files changed

+147
-4
lines changed

ovs/matchflow.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func (f *MatchFlow) MarshalText() ([]byte, error) {
105105
b = append(b, ',')
106106
}
107107

108-
if f.Cookie > 0 {
108+
if f.Cookie > 0 || f.CookieMask > 0 {
109109
// Hexadecimal cookies and masks are much easier to read.
110110
b = append(b, cookie+"="...)
111111
b = append(b, paddedHexUint64(f.Cookie)...)

ovs/matchflow_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,24 @@ func TestMatchFlowMarshalText(t *testing.T) {
232232
},
233233
s: "udp,in_port=33,nw_dst=192.0.2.1,tp_dst=0xea60/0xffe0,table=55",
234234
},
235+
{
236+
desc: "Test zero cookie match",
237+
f: &MatchFlow{
238+
Cookie: 0,
239+
CookieMask: 0xffffffffffffffff,
240+
Table: 45,
241+
},
242+
s: "cookie=0x0000000000000000/0xffffffffffffffff,table=45",
243+
},
244+
{
245+
desc: "Test match any cookie",
246+
f: &MatchFlow{
247+
Cookie: 0,
248+
CookieMask: 0,
249+
Table: 45,
250+
},
251+
s: "table=45",
252+
},
235253
}
236254

237255
for _, tt := range tests {

ovs/openflow.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,21 @@ func (o *OpenFlowService) DumpTables(bridge string) ([]*Table, error) {
267267
return tables, err
268268
}
269269

270-
// DumpFlows retrieves statistics about all flows for the specified bridge.
270+
// DumpFlowsWithFlowArgs retrieves statistics about all flows for the specified bridge,
271+
// filtering on the specified flow(s), if provided.
271272
// If a table has no active flows and has not been used for a lookup or matched
272273
// by an incoming packet, it is filtered from the output.
273-
func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
274+
// We neeed to add a Matchflow to filter the dumpflow results. For example filter based on table, cookie.
275+
func (o *OpenFlowService) DumpFlowsWithFlowArgs(bridge string, flow *MatchFlow) ([]*Flow, error) {
274276
args := []string{"dump-flows", bridge}
275277
args = append(args, o.c.ofctlFlags...)
276-
278+
if flow != nil {
279+
fb, err := flow.MarshalText()
280+
if err != nil {
281+
return nil, err
282+
}
283+
args = append(args, string(fb))
284+
}
277285
out, err := o.exec(args...)
278286
if err != nil {
279287
return nil, err
@@ -298,6 +306,13 @@ func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
298306
return flows, err
299307
}
300308

309+
// DumpFlows retrieves statistics about all flows for the specified bridge.
310+
// If a table has no active flows and has not been used for a lookup or matched
311+
// by an incoming packet, it is filtered from the output.
312+
func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
313+
return o.DumpFlowsWithFlowArgs(bridge, nil)
314+
}
315+
301316
// DumpAggregate retrieves statistics about the specified flow attached to the
302317
// specified bridge.
303318
func (o *OpenFlowService) DumpAggregate(bridge string, flow *MatchFlow) (*FlowStats, error) {

ovs/openflow_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,116 @@ NXST_FLOW reply (xid=0x4):
10581058
}
10591059
}
10601060

1061+
func TestClientOpenFlowDumpFlowsWithFlowArgs(t *testing.T) {
1062+
tests := []struct {
1063+
name string
1064+
table string
1065+
cookie uint64
1066+
cookieMask uint64
1067+
input string
1068+
flows string
1069+
want []*Flow
1070+
err error
1071+
}{
1072+
{
1073+
name: "test single flow",
1074+
input: "br0",
1075+
table: "45",
1076+
cookie: 0,
1077+
cookieMask: 0x0,
1078+
flows: `NXST_FLOW reply (xid=0x4):
1079+
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
1080+
`,
1081+
want: []*Flow{
1082+
{
1083+
Priority: 820,
1084+
InPort: PortLOCAL,
1085+
Matches: []Match{},
1086+
Table: 45,
1087+
Cookie: 1,
1088+
Actions: []Action{
1089+
ModVLANVID(10),
1090+
Output(1),
1091+
},
1092+
},
1093+
},
1094+
err: nil,
1095+
},
1096+
{
1097+
name: "test multiple flows",
1098+
input: "br0",
1099+
table: "45",
1100+
cookie: 0,
1101+
cookieMask: 0x1,
1102+
flows: `NXST_FLOW reply (xid=0x4):
1103+
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
1104+
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)
1105+
`,
1106+
want: []*Flow{
1107+
{
1108+
Priority: 820,
1109+
InPort: PortLOCAL,
1110+
Matches: []Match{},
1111+
Table: 45,
1112+
Cookie: 0,
1113+
Actions: []Action{
1114+
ModVLANVID(10),
1115+
Output(1),
1116+
},
1117+
},
1118+
{
1119+
Priority: 110,
1120+
Protocol: ProtocolIPv4,
1121+
Matches: []Match{
1122+
DataLinkSource("f1:f2:f3:f4:f5:f6"),
1123+
},
1124+
Table: 45,
1125+
Actions: []Action{
1126+
ConnectionTracking("table=51"),
1127+
},
1128+
},
1129+
},
1130+
err: nil,
1131+
},
1132+
}
1133+
for _, tt := range tests {
1134+
t.Run(tt.name, func(t *testing.T) {
1135+
got, _ := testClient([]OptionFunc{Timeout(1)}, func(cmd string, args ...string) ([]byte, error) {
1136+
if want, got := "ovs-ofctl", cmd; want != got {
1137+
t.Fatalf("incorrect command:\n- want: %v\n- got: %v",
1138+
want, got)
1139+
}
1140+
filterArg := "cookie=0x0000000000000000/0xffffffffffffffff," + "table=" + tt.table
1141+
wantArgs := []string{
1142+
"--timeout=1",
1143+
"dump-flows",
1144+
string(tt.input),
1145+
filterArg,
1146+
}
1147+
if want, got := wantArgs, args; !reflect.DeepEqual(want, got) {
1148+
t.Fatalf("incorrect arguments\n- want: %v\n- got: %v",
1149+
want, got)
1150+
}
1151+
return []byte(tt.flows), tt.err
1152+
}).OpenFlow.DumpFlowsWithFlowArgs(tt.input, &MatchFlow{Cookie: 0,
1153+
CookieMask: 0xffffffffffffffff,
1154+
Table: 45})
1155+
if len(tt.want) != len(got) {
1156+
t.Errorf("got %d", len(got))
1157+
t.Errorf("want %d", len(tt.want))
1158+
t.Fatal("expected return value to be equal")
1159+
}
1160+
for i := range tt.want {
1161+
if !flowsEqual(tt.want[i], got[i]) {
1162+
t.Errorf("got %+v", got[i])
1163+
t.Errorf("want %+v", tt.want[i])
1164+
t.Fatal("expected return value to be equal")
1165+
}
1166+
}
1167+
})
1168+
}
1169+
}
1170+
10611171
func TestClientOpenFlowDumpFlows15(t *testing.T) {
10621172
tests := []struct {
10631173
name string

0 commit comments

Comments
 (0)