Skip to content

Commit c22432b

Browse files
committed
ovs: open source package ovs
1 parent be839a1 commit c22432b

30 files changed

+10078
-1
lines changed

ovs/action.go

Lines changed: 479 additions & 0 deletions
Large diffs are not rendered by default.

ovs/action_test.go

Lines changed: 544 additions & 0 deletions
Large diffs are not rendered by default.

ovs/actionparser.go

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
package ovs
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"fmt"
7+
"io"
8+
"net"
9+
"regexp"
10+
"strconv"
11+
"strings"
12+
)
13+
14+
// An actionParser is a parser for OVS flow actions.
15+
type actionParser struct {
16+
r *bufio.Reader
17+
s stack
18+
}
19+
20+
// newActionParser creates a new actionParser which wraps the input
21+
// io.Reader.
22+
func newActionParser(r io.Reader) *actionParser {
23+
return &actionParser{
24+
r: bufio.NewReader(r),
25+
s: make(stack, 0),
26+
}
27+
}
28+
29+
// eof is a sentinel rune for end of file.
30+
var eof = rune(0)
31+
32+
// read reads a single rune from the wrapped io.Reader. It returns eof
33+
// if no more runes are present.
34+
func (p *actionParser) read() rune {
35+
ch, _, err := p.r.ReadRune()
36+
if err != nil {
37+
return eof
38+
}
39+
return ch
40+
}
41+
42+
// Parse parses a slice of Actions using the wrapped io.Reader. The raw
43+
// action strings are also returned for inspection if needed.
44+
func (p *actionParser) Parse() ([]Action, []string, error) {
45+
var actions []Action
46+
var raw []string
47+
48+
for {
49+
a, r, err := p.parseAction()
50+
if err != nil {
51+
// No more actions remain
52+
if err == io.EOF {
53+
break
54+
}
55+
56+
return nil, nil, err
57+
}
58+
59+
actions = append(actions, a)
60+
raw = append(raw, r)
61+
}
62+
63+
return actions, raw, nil
64+
}
65+
66+
// parseAction parses a single Action and its raw text from the wrapped
67+
// io.Reader.
68+
func (p *actionParser) parseAction() (Action, string, error) {
69+
// Track runes encountered
70+
var buf bytes.Buffer
71+
72+
for {
73+
ch := p.read()
74+
75+
// If comma encountered and no open parentheses, at end of this
76+
// action string
77+
if ch == ',' && p.s.len() == 0 {
78+
break
79+
}
80+
81+
// If EOF encountered, at end of string
82+
if ch == eof {
83+
// If no items in buffer, end of this action
84+
if buf.Len() == 0 {
85+
return nil, "", io.EOF
86+
}
87+
88+
// Parse action from buffer
89+
break
90+
}
91+
92+
// Track open and closing parentheses using a stack to ensure
93+
// that they are appropriately matched
94+
switch ch {
95+
case '(':
96+
p.s.push()
97+
case ')':
98+
p.s.pop()
99+
}
100+
101+
_, _ = buf.WriteRune(ch)
102+
}
103+
104+
// Found an unmatched set of parentheses
105+
if p.s.len() > 0 {
106+
return nil, "", fmt.Errorf("invalid action: %q", buf.String())
107+
}
108+
109+
s := buf.String()
110+
act, err := parseAction(s)
111+
return act, s, err
112+
}
113+
114+
// A stack is a basic stack with elements that have no value.
115+
type stack []struct{}
116+
117+
// len returns the current length of the stack.
118+
func (s *stack) len() int {
119+
return len(*s)
120+
}
121+
122+
// push adds an element to the stack.
123+
func (s *stack) push() {
124+
*s = append(*s, struct{}{})
125+
}
126+
127+
// pop removes an element from the stack.
128+
func (s *stack) pop() {
129+
*s = (*s)[:s.len()-1]
130+
}
131+
132+
var (
133+
// resubmitRe is the regex used to match the resubmit action
134+
// with one or more of its parameters.
135+
resubmitRe = regexp.MustCompile(`resubmit\((\d*),(\d*)\)`)
136+
137+
// ctRe is the regex used to match the ct action with its
138+
// parameter list.
139+
ctRe = regexp.MustCompile(`ct\((\S+)\)`)
140+
141+
// loadRe is the regex used to match the load action
142+
// with its parameters.
143+
loadRe = regexp.MustCompile(`load:(\S+)->(\S+)`)
144+
145+
// setFieldRe is the regex used to match the set_field action
146+
// with its parameters.
147+
setFieldRe = regexp.MustCompile(`set_field:(\S+)->(\S+)`)
148+
)
149+
150+
// TODO(mdlayher): replace parsing regex with arguments parsers
151+
152+
// parseAction creates an Action function from the input string.
153+
func parseAction(s string) (Action, error) {
154+
// Simple actions which match a basic string
155+
switch strings.ToLower(s) {
156+
case actionDrop:
157+
return Drop(), nil
158+
case actionFlood:
159+
return Flood(), nil
160+
case actionInPort:
161+
return InPort(), nil
162+
case actionLocal:
163+
return Local(), nil
164+
case actionNormal:
165+
return Normal(), nil
166+
case actionStripVLAN:
167+
return StripVLAN(), nil
168+
}
169+
170+
// ActionCT, with its arguments
171+
if ss := ctRe.FindAllStringSubmatch(s, 1); len(ss) > 0 && len(ss[0]) == 2 {
172+
// Results are:
173+
// - full string
174+
// - arguments list
175+
return ConnectionTracking(ss[0][1]), nil
176+
}
177+
178+
// ActionModDataLinkDestination, with its hardware address.
179+
if strings.HasPrefix(s, patModDataLinkDestination[:len(patModDataLinkDestination)-2]) {
180+
var addr string
181+
n, err := fmt.Sscanf(s, patModDataLinkDestination, &addr)
182+
if err != nil {
183+
return nil, err
184+
}
185+
if n > 0 {
186+
mac, err := net.ParseMAC(addr)
187+
if err != nil {
188+
return nil, err
189+
}
190+
191+
return ModDataLinkDestination(mac), nil
192+
}
193+
}
194+
195+
// ActionModDataLinkSource, with its hardware address.
196+
if strings.HasPrefix(s, patModDataLinkSource[:len(patModDataLinkSource)-2]) {
197+
var addr string
198+
n, err := fmt.Sscanf(s, patModDataLinkSource, &addr)
199+
if err != nil {
200+
return nil, err
201+
}
202+
if n > 0 {
203+
mac, err := net.ParseMAC(addr)
204+
if err != nil {
205+
return nil, err
206+
}
207+
208+
return ModDataLinkSource(mac), nil
209+
}
210+
}
211+
212+
// ActionModNetworkDestination, with it hardware address
213+
if strings.HasPrefix(s, patModNetworkDestination[:len(patModNetworkDestination)-2]) {
214+
var ip string
215+
n, err := fmt.Sscanf(s, patModNetworkDestination, &ip)
216+
if err != nil {
217+
return nil, err
218+
}
219+
if n > 0 {
220+
ip4 := net.ParseIP(ip).To4()
221+
if ip4 == nil {
222+
return nil, fmt.Errorf("invalid IPv4 address: %s", ip)
223+
}
224+
225+
return ModNetworkDestination(ip4), nil
226+
}
227+
}
228+
229+
// ActionModNetworkSource, with it hardware address
230+
if strings.HasPrefix(s, patModNetworkSource[:len(patModNetworkSource)-2]) {
231+
var ip string
232+
n, err := fmt.Sscanf(s, patModNetworkSource, &ip)
233+
if err != nil {
234+
return nil, err
235+
}
236+
if n > 0 {
237+
ip4 := net.ParseIP(ip).To4()
238+
if ip4 == nil {
239+
return nil, fmt.Errorf("invalid IPv4 address: %s", ip)
240+
}
241+
242+
return ModNetworkSource(ip4), nil
243+
}
244+
}
245+
246+
// ActionModTransportDestinationPort, with its port.
247+
if strings.HasPrefix(s, patModTransportDestinationPort[:len(patModTransportDestinationPort)-2]) {
248+
var port uint16
249+
n, err := fmt.Sscanf(s, patModTransportDestinationPort, &port)
250+
if err != nil {
251+
return nil, err
252+
}
253+
if n > 0 {
254+
return ModTransportDestinationPort(port), nil
255+
}
256+
}
257+
258+
// ActionModTransportSourcePort, with its port.
259+
if strings.HasPrefix(s, patModTransportSourcePort[:len(patModTransportSourcePort)-2]) {
260+
var port uint16
261+
n, err := fmt.Sscanf(s, patModTransportSourcePort, &port)
262+
if err != nil {
263+
return nil, err
264+
}
265+
if n > 0 {
266+
return ModTransportSourcePort(port), nil
267+
}
268+
}
269+
270+
// ActionModVLANVID, with its VLAN ID
271+
if strings.HasPrefix(s, patModVLANVID[:len(patModVLANVID)-2]) {
272+
var vlan int
273+
n, err := fmt.Sscanf(s, patModVLANVID, &vlan)
274+
if err != nil {
275+
return nil, err
276+
}
277+
if n > 0 {
278+
return ModVLANVID(vlan), nil
279+
}
280+
}
281+
282+
// ActionOutput, with its port number
283+
if strings.HasPrefix(s, patOutput[:len(patOutput)-2]) {
284+
var port int
285+
n, err := fmt.Sscanf(s, patOutput, &port)
286+
if err != nil {
287+
return nil, err
288+
}
289+
if n > 0 {
290+
return Output(port), nil
291+
}
292+
}
293+
294+
// ActionResubmit, with one or both of port number and table number
295+
if ss := resubmitRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 {
296+
var (
297+
port int
298+
table int
299+
300+
err error
301+
)
302+
303+
// Results are:
304+
// - full string
305+
// - port
306+
// - table
307+
if s := ss[0][1]; s != "" {
308+
port, err = strconv.Atoi(s)
309+
if err != nil {
310+
return nil, err
311+
}
312+
}
313+
if s := ss[0][2]; s != "" {
314+
table, err = strconv.Atoi(ss[0][2])
315+
if err != nil {
316+
return nil, err
317+
}
318+
}
319+
320+
return Resubmit(port, table), nil
321+
}
322+
323+
if ss := loadRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 {
324+
// Results are:
325+
// - full string
326+
// - value
327+
// - field
328+
return Load(ss[0][1], ss[0][2]), nil
329+
}
330+
331+
if ss := setFieldRe.FindAllStringSubmatch(s, 2); len(ss) > 0 && len(ss[0]) == 3 {
332+
// Results are:
333+
// - full string
334+
// - value
335+
// - field
336+
return SetField(ss[0][1], ss[0][2]), nil
337+
}
338+
339+
return nil, fmt.Errorf("no action matched for %q", s)
340+
}

0 commit comments

Comments
 (0)