Skip to content

Commit 4a6a687

Browse files
authored
Merge pull request #116 from sjafferali/supportovs15
Support other openflow protocols more consistently
2 parents e2f1bc6 + 133d69e commit 4a6a687

File tree

2 files changed

+145
-13
lines changed

2 files changed

+145
-13
lines changed

ovs/openflow.go

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -186,24 +186,33 @@ func (o *OpenFlowService) AddFlowBundle(bridge string, fn func(tx *FlowTransacti
186186
//
187187
// If flow is nil, all flows will be deleted from the specified bridge.
188188
func (o *OpenFlowService) DelFlows(bridge string, flow *MatchFlow) error {
189+
args := []string{"del-flows"}
190+
args = append(args, o.c.ofctlFlags...)
191+
args = append(args, bridge)
192+
189193
if flow == nil {
190194
// This means we'll flush the entire flows
191-
// from the specifided bridge.
192-
_, err := o.exec("del-flows", bridge)
195+
// from the specified bridge.
196+
_, err := o.exec(args...)
193197
return err
194198
}
195199
fb, err := flow.MarshalText()
196200
if err != nil {
197201
return err
198202
}
199203

200-
_, err = o.exec("del-flows", bridge, string(fb))
204+
args = append(args, string(fb))
205+
_, err = o.exec(args...)
201206
return err
202207
}
203208

204209
// ModPort modifies the specified characteristics for the specified port.
205210
func (o *OpenFlowService) ModPort(bridge string, port string, action PortAction) error {
206-
_, err := o.exec("mod-port", bridge, string(port), string(action))
211+
args := []string{"mod-port"}
212+
args = append(args, o.c.ofctlFlags...)
213+
args = append(args, []string{bridge, port, string(action)}...)
214+
215+
_, err := o.exec(args...)
207216
return err
208217
}
209218

@@ -231,7 +240,10 @@ func (o *OpenFlowService) DumpPorts(bridge string) ([]*PortStats, error) {
231240
// If a table has no active flows and has not been used for a lookup or matched
232241
// by an incoming packet, it is filtered from the output.
233242
func (o *OpenFlowService) DumpTables(bridge string) ([]*Table, error) {
234-
out, err := o.exec("dump-tables", bridge)
243+
args := []string{"dump-tables", bridge}
244+
args = append(args, o.c.ofctlFlags...)
245+
246+
out, err := o.exec(args...)
235247
if err != nil {
236248
return nil, err
237249
}
@@ -259,15 +271,18 @@ func (o *OpenFlowService) DumpTables(bridge string) ([]*Table, error) {
259271
// If a table has no active flows and has not been used for a lookup or matched
260272
// by an incoming packet, it is filtered from the output.
261273
func (o *OpenFlowService) DumpFlows(bridge string) ([]*Flow, error) {
262-
out, err := o.exec("dump-flows", bridge)
274+
args := []string{"dump-flows", bridge}
275+
args = append(args, o.c.ofctlFlags...)
276+
277+
out, err := o.exec(args...)
263278
if err != nil {
264279
return nil, err
265280
}
266281

267282
var flows []*Flow
268283
err = parseEachLine(out, dumpFlowsPrefix, func(b []byte) error {
269-
// Do not attempt to parse NXST_FLOW messages.
270-
if bytes.HasPrefix(b, dumpFlowsPrefix) {
284+
// Do not attempt to parse ST_FLOW messages.
285+
if bytes.Contains(b, dumpFlowsPrefix) {
271286
return nil
272287
}
273288

@@ -303,9 +318,12 @@ var (
303318
// the output from 'ovs-ofctl dump-tables'.
304319
dumpTablesPrefix = []byte("OFPST_TABLE reply")
305320

306-
// dumpFlowsPrefix is a sentinel value returned at the beginning of
307-
// the output from 'ovs-ofctl dump-flows'.
308-
dumpFlowsPrefix = []byte("NXST_FLOW reply")
321+
// dumpFlowsPrefix is a sentinel value returned at the beginning of the output
322+
// from 'ovs-ofctl dump-flows'. The value returned when using protocol version
323+
// 1.0 is "NXST_FLOW reply". The value returned when using protocol version > 1.1
324+
// is "OFPST_FLOW reply". However, we use ST_FLOW here to be able to match both
325+
// of these.
326+
dumpFlowsPrefix = []byte("ST_FLOW reply")
309327

310328
// dumpAggregatePrefix is a sentinel value returned at the beginning of
311329
// the output from "ovs-ofctl dump-aggregate"
@@ -387,8 +405,8 @@ func parseEachLine(in []byte, prefix []byte, fn func(b []byte) error) error {
387405
return io.ErrUnexpectedEOF
388406
}
389407

390-
// First line must contain prefix returned by OVS
391-
if !bytes.HasPrefix(scanner.Bytes(), prefix) {
408+
// First line must contain the prefix returned by OVS
409+
if !bytes.Contains(scanner.Bytes(), prefix) {
392410
return io.ErrUnexpectedEOF
393411
}
394412

ovs/openflow_test.go

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

1061+
func TestClientOpenFlowDumpFlows15(t *testing.T) {
1062+
tests := []struct {
1063+
name string
1064+
input string
1065+
flows string
1066+
want []*Flow
1067+
err error
1068+
}{
1069+
{
1070+
name: "test single flow",
1071+
input: "br0",
1072+
flows: `OFPST_FLOW reply (OF1.5) (xid=0x2):
1073+
cookie=0x0, duration=82301.183s, table=0, n_packets=1127501, n_bytes=1595250938, idle_age=0, priority=100,ct_state=-trk,ip actions=ct(table=1)
1074+
`,
1075+
want: []*Flow{
1076+
{
1077+
Priority: 100,
1078+
Protocol: ProtocolIPv4,
1079+
Matches: []Match{
1080+
ConnectionTrackingState(UnsetState(CTStateTracked)),
1081+
},
1082+
Table: 0,
1083+
Actions: []Action{
1084+
ConnectionTracking("table=1"),
1085+
},
1086+
},
1087+
},
1088+
err: nil,
1089+
},
1090+
{
1091+
name: "test multiple flows",
1092+
input: "br0",
1093+
flows: `OFPST_FLOW reply (OF1.5) (xid=0x2):
1094+
cookie=0x0, duration=82301.183s, table=0, n_packets=1127501, n_bytes=1595250938, idle_age=0, priority=100,ct_state=-trk,ip actions=ct(table=1)
1095+
cookie=0x0, duration=82301.183s, table=0, n_packets=7370490, n_bytes=893401420, idle_age=0, priority=0 actions=NORMAL
1096+
cookie=0x0, duration=82301.183s, table=100, n_packets=1068, n_bytes=388186, idle_age=3191, priority=1000,udp,tp_dst=53 actions=ct(commit),NORMAL
1097+
`,
1098+
want: []*Flow{
1099+
{
1100+
Priority: 100,
1101+
Protocol: ProtocolIPv4,
1102+
Matches: []Match{
1103+
ConnectionTrackingState(UnsetState(CTStateTracked)),
1104+
},
1105+
Table: 0,
1106+
Actions: []Action{
1107+
ConnectionTracking("table=1"),
1108+
},
1109+
},
1110+
{
1111+
Priority: 0,
1112+
Matches: []Match{},
1113+
Table: 0,
1114+
Actions: []Action{
1115+
Normal(),
1116+
},
1117+
},
1118+
{
1119+
Priority: 1000,
1120+
Protocol: ProtocolUDPv4,
1121+
Matches: []Match{
1122+
TransportDestinationPort(53),
1123+
},
1124+
Table: 100,
1125+
Actions: []Action{
1126+
ConnectionTracking("commit"),
1127+
Normal(),
1128+
},
1129+
},
1130+
},
1131+
err: nil,
1132+
},
1133+
}
1134+
1135+
options := []OptionFunc{
1136+
Protocols([]string{ProtocolOpenFlow15}),
1137+
Timeout(1),
1138+
}
1139+
1140+
for _, tt := range tests {
1141+
t.Run(tt.name, func(t *testing.T) {
1142+
got, _ := testClient(options, func(cmd string, args ...string) ([]byte, error) {
1143+
if want, got := "ovs-ofctl", cmd; want != got {
1144+
t.Fatalf("incorrect command:\n- want: %v\n- got: %v",
1145+
want, got)
1146+
}
1147+
wantArgs := []string{
1148+
"--timeout=1",
1149+
"dump-flows",
1150+
string(tt.input),
1151+
"--protocols=OpenFlow15",
1152+
}
1153+
if want, got := wantArgs, args; !reflect.DeepEqual(want, got) {
1154+
t.Fatalf("incorrect arguments\n- want: %v\n- got: %v",
1155+
want, got)
1156+
}
1157+
return []byte(tt.flows), tt.err
1158+
}).OpenFlow.DumpFlows(tt.input)
1159+
if len(tt.want) != len(got) {
1160+
t.Errorf("got %d", len(got))
1161+
t.Errorf("want %d", len(tt.want))
1162+
t.Fatal("expected return value to be equal")
1163+
}
1164+
for i := range tt.want {
1165+
if !flowsEqual(tt.want[i], got[i]) {
1166+
t.Errorf("got %v", got[i])
1167+
t.Errorf("want %v", tt.want[i])
1168+
t.Fatal("expected return value to be equal")
1169+
}
1170+
}
1171+
})
1172+
}
1173+
}
1174+
10611175
func mustVerifyFlowBundle(t *testing.T, stdin io.Reader, flows []*Flow, matchFlows []*MatchFlow) {
10621176
s := bufio.NewScanner(stdin)
10631177
var gotFlows []*Flow

0 commit comments

Comments
 (0)