mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 14:55:18 +00:00
ovs-actions: New document describing OVS actions in detail.
Acked-by: Mark Michelson <mmichels@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
@@ -196,7 +196,7 @@ Q: Open vSwitch does not seem to obey my packet filter rules.
|
||||
|
||||
For simple filtering rules, it might be possible to achieve similar results
|
||||
by installing appropriate OpenFlow flows instead. The OVS conntrack
|
||||
feature (see the "ct" action in ovs-ofctl(8)) can implement a stateful
|
||||
feature (see the "ct" action in ovs-actions(7)) can implement a stateful
|
||||
firewall.
|
||||
|
||||
If the use of a particular packet filter setup is essential, Open vSwitch
|
||||
|
@@ -516,44 +516,6 @@ but the packets are getting dropped instead. Why?
|
||||
|
||||
See also the preceding question.
|
||||
|
||||
Q: The "learn" action can't learn the action I want, can you improve it?
|
||||
|
||||
A: By itself, the "learn" action can only put two kinds of actions into the
|
||||
flows that it creates: "load" and "output" actions. If "learn" is used in
|
||||
isolation, these are severe limits.
|
||||
|
||||
However, "learn" is not meant to be used in isolation. It is a primitive
|
||||
meant to be used together with other Open vSwitch features to accomplish a
|
||||
task. Its existing features are enough to accomplish most tasks.
|
||||
|
||||
Here is an outline of a typical pipeline structure that allows for
|
||||
versatile behavior using "learn":
|
||||
|
||||
- Flows in table A contain a "learn" action, that populates flows in table
|
||||
L, that use a "load" action to populate register R with information about
|
||||
what was learned.
|
||||
|
||||
- Flows in table B contain two sequential resubmit actions: one to table L
|
||||
and another one to table B+1.
|
||||
|
||||
- Flows in table B+1 match on register R and act differently depending on
|
||||
what the flows in table L loaded into it.
|
||||
|
||||
This approach can be used to implement many "learn"-based features. For
|
||||
example:
|
||||
|
||||
- Resubmit to a table selected based on learned information, e.g. see:
|
||||
https://mail.openvswitch.org/pipermail/ovs-discuss/2016-June/021694.html
|
||||
|
||||
- MAC learning in the middle of a pipeline, as described in
|
||||
:doc:`/tutorials/ovs-advanced`
|
||||
|
||||
- TCP state based firewalling, by learning outgoing connections based on
|
||||
SYN packets and matching them up with incoming packets.
|
||||
|
||||
- At least some of the features described in T. A. Hoff, "Extending Open
|
||||
vSwitch to Facilitate Creation of Stateful SDN Applications".
|
||||
|
||||
Q: When using the "ct" action with FTP connections, it doesn't seem to matter
|
||||
if I set the "alg=ftp" parameter in the action. Is this required?
|
||||
|
||||
|
@@ -50,6 +50,10 @@ The remainder are still in roff format can be found below:
|
||||
|
||||
.. list-table::
|
||||
|
||||
* - ovs-actions(7)
|
||||
- `(pdf) <http://www.openvswitch.org/support/dist-docs/ovs-actions.7.pdf>`__
|
||||
- `(html) <http://www.openvswitch.org/support/dist-docs/ovs-actions.7.html>`__
|
||||
- `(plain text) <http://www.openvswitch.org/support/dist-docs/ovs-actions.7.txt>`__
|
||||
* - ovn-architecture(7)
|
||||
- `(pdf) <http://www.openvswitch.org/support/dist-docs/ovn-architecture.7.pdf>`__
|
||||
- `(html) <http://www.openvswitch.org/support/dist-docs/ovn-architecture.7.html>`__
|
||||
|
@@ -1,8 +1,11 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
import getopt
|
||||
import sys
|
||||
import os.path
|
||||
import re
|
||||
import xml.dom.minidom
|
||||
import build.nroff
|
||||
|
||||
OFP_ACTION_ALIGN = 8
|
||||
|
||||
@@ -69,14 +72,21 @@ def usage():
|
||||
argv0 = os.path.basename(sys.argv[0])
|
||||
print('''\
|
||||
%(argv0)s, for extracting OpenFlow action data
|
||||
usage: %(argv0)s OFP_ACTIONS.C [--prototypes | --definitions]
|
||||
usage: %(argv0)s [prototypes | definitions] OFP-ACTIONS.c
|
||||
usage: %(argv0)s ovs-actions OVS-ACTIONS.XML
|
||||
|
||||
This program reads ofp-actions.c to obtain information about OpenFlow
|
||||
actions. With --prototypes, it outputs on stdout a set of prototypes to
|
||||
#include early in ofp-actions.c. With --definitions, it outputs on stdout
|
||||
a set of definitions to #include late in ofp-actions.c
|
||||
Commands:
|
||||
|
||||
OFP_ACTIONS.C should point to lib/ofp-actions.c.\
|
||||
prototypes OFP-ACTIONS.C
|
||||
Reads ofp-actions.c and prints a set of prototypes to #include early in
|
||||
ofp-actions.c.
|
||||
|
||||
definitions OFP-ACTIONS.C
|
||||
Reads ofp-actions.c and prints a set of definitions to #include late in
|
||||
ofp-actions.c.
|
||||
|
||||
ovs-actions OVS-ACTIONS.XML
|
||||
Reads ovs-actions.xml and prints documentation in troff format.\
|
||||
''' % {"argv0": argv0})
|
||||
sys.exit(0)
|
||||
|
||||
@@ -377,19 +387,176 @@ static enum ofperr ofpact_decode(const struct ofp_action_header *,
|
||||
uint64_t arg, const struct vl_mff_map *vl_mff_map,
|
||||
uint64_t *tlv_bitmap, struct ofpbuf *out);
|
||||
""")
|
||||
|
||||
## ------------------------ ##
|
||||
## Documentation Generation ##
|
||||
## ------------------------ ##
|
||||
|
||||
def action_to_xml(action_node, body):
|
||||
syntax = 0
|
||||
for node in action_node.childNodes:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'syntax':
|
||||
if body[-1].strip() == '.PP':
|
||||
del body[-1]
|
||||
if syntax:
|
||||
body += ['.IQ\n']
|
||||
else:
|
||||
body += ['.IP "\\fBSyntax:\\fR"\n']
|
||||
body += [build.nroff.inline_xml_to_nroff(x, r'\fR')
|
||||
for x in node.childNodes] + ['\n']
|
||||
syntax += 1
|
||||
elif (node.nodeType == node.ELEMENT_NODE
|
||||
and node.tagName == 'conformance'):
|
||||
body += ['.IP "\\fBConformance:\\fR"\n']
|
||||
body += [build.nroff.block_xml_to_nroff(node.childNodes)]
|
||||
else:
|
||||
body += [build.nroff.block_xml_to_nroff([node])]
|
||||
|
||||
def group_xml_to_nroff(group_node):
|
||||
title = group_node.attributes['title'].nodeValue
|
||||
|
||||
body = []
|
||||
for node in group_node.childNodes:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'action':
|
||||
action_to_xml(node, body)
|
||||
else:
|
||||
body += [build.nroff.block_xml_to_nroff([node])]
|
||||
|
||||
content = [
|
||||
'.bp\n',
|
||||
'.SH \"%s\"\n' % build.nroff.text_to_nroff(title.upper())]
|
||||
content += body
|
||||
return ''.join(content)
|
||||
|
||||
def make_ovs_actions(ovs_actions_xml):
|
||||
document = xml.dom.minidom.parse(ovs_actions_xml)
|
||||
doc = document.documentElement
|
||||
|
||||
global version
|
||||
if version == None:
|
||||
version = "UNKNOWN"
|
||||
|
||||
print('''\
|
||||
'\\" tp
|
||||
.\\" -*- mode: troff; coding: utf-8 -*-
|
||||
.TH "ovs\-actions" 7 "%s" "Open vSwitch" "Open vSwitch Manual"
|
||||
.fp 5 L CR \\" Make fixed-width font available as \\fL.
|
||||
.de ST
|
||||
. PP
|
||||
. RS -0.15in
|
||||
. I "\\\\$1"
|
||||
. RE
|
||||
..
|
||||
|
||||
.de SU
|
||||
. PP
|
||||
. I "\\\\$1"
|
||||
..
|
||||
|
||||
.de IQ
|
||||
. br
|
||||
. ns
|
||||
. IP "\\\\$1"
|
||||
..
|
||||
|
||||
.de TQ
|
||||
. br
|
||||
. ns
|
||||
. TP "\\\\$1"
|
||||
..
|
||||
.de URL
|
||||
\\\\$2 \\(laURL: \\\\$1 \\(ra\\\\$3
|
||||
..
|
||||
.if \\n[.g] .mso www.tmac
|
||||
.SH NAME
|
||||
ovs\-actions \- OpenFlow actions and instructions with Open vSwitch extensions
|
||||
.
|
||||
.PP
|
||||
''' % version)
|
||||
|
||||
s = ''
|
||||
for node in doc.childNodes:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == "group":
|
||||
s += group_xml_to_nroff(node)
|
||||
elif node.nodeType == node.TEXT_NODE:
|
||||
assert node.data.isspace()
|
||||
elif node.nodeType == node.COMMENT_NODE:
|
||||
pass
|
||||
else:
|
||||
s += build.nroff.block_xml_to_nroff([node])
|
||||
|
||||
if n_errors:
|
||||
sys.exit(1)
|
||||
|
||||
output = []
|
||||
for oline in s.split("\n"):
|
||||
oline = oline.strip()
|
||||
|
||||
# Life is easier with nroff if we don't try to feed it Unicode.
|
||||
# Fortunately, we only use a few characters outside the ASCII range.
|
||||
oline = oline.replace(u'\u2208', r'\[mo]')
|
||||
oline = oline.replace(u'\u2260', r'\[!=]')
|
||||
oline = oline.replace(u'\u2264', r'\[<=]')
|
||||
oline = oline.replace(u'\u2265', r'\[>=]')
|
||||
oline = oline.replace(u'\u00d7', r'\[mu]')
|
||||
if len(oline):
|
||||
output += [oline]
|
||||
|
||||
# nroff tends to ignore .bp requests if they come after .PP requests,
|
||||
# so remove .PPs that precede .bp.
|
||||
for i in range(len(output)):
|
||||
if output[i] == '.bp':
|
||||
j = i - 1
|
||||
while j >= 0 and output[j] == '.PP':
|
||||
output[j] = None
|
||||
j -= 1
|
||||
for i in range(len(output)):
|
||||
if output[i] is not None:
|
||||
print(output[i])
|
||||
|
||||
|
||||
## ------------ ##
|
||||
## Main Program ##
|
||||
## ------------ ##
|
||||
|
||||
if __name__ == '__main__':
|
||||
if '--help' in sys.argv:
|
||||
usage()
|
||||
elif len(sys.argv) != 3:
|
||||
sys.stderr.write("exactly two arguments required; "
|
||||
"use --help for help\n")
|
||||
sys.exit(1)
|
||||
elif sys.argv[2] == '--prototypes':
|
||||
extract_ofp_actions(sys.argv[1], False)
|
||||
elif sys.argv[2] == '--definitions':
|
||||
extract_ofp_actions(sys.argv[1], True)
|
||||
else:
|
||||
sys.stderr.write("invalid arguments; use --help for help\n")
|
||||
argv0 = sys.argv[0]
|
||||
try:
|
||||
options, args = getopt.gnu_getopt(sys.argv[1:], 'h',
|
||||
['help', 'ovs-version='])
|
||||
except getopt.GetoptError as geo:
|
||||
sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
|
||||
sys.exit(1)
|
||||
|
||||
global version
|
||||
version = None
|
||||
for key, value in options:
|
||||
if key in ['-h', '--help']:
|
||||
usage()
|
||||
elif key == '--ovs-version':
|
||||
version = value
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
if not args:
|
||||
sys.stderr.write("%s: missing command argument "
|
||||
"(use --help for help)\n" % argv0)
|
||||
sys.exit(1)
|
||||
|
||||
commands = {"prototypes": (lambda fn: extract_ofp_actions(fn, False), 1),
|
||||
"definitions": (lambda fn: extract_ofp_actions(fn, True), 1),
|
||||
"ovs-actions": (make_ovs_actions, 1)}
|
||||
|
||||
if not args[0] in commands:
|
||||
sys.stderr.write("%s: unknown command \"%s\" "
|
||||
"(use --help for help)\n" % (argv0, args[0]))
|
||||
sys.exit(1)
|
||||
|
||||
func, n_args = commands[args[0]]
|
||||
if len(args) - 1 != n_args:
|
||||
sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
|
||||
"provided\n"
|
||||
% (argv0, args[0], n_args, len(args) - 1))
|
||||
sys.exit(1)
|
||||
|
||||
func(*args[1:])
|
||||
|
@@ -557,9 +557,9 @@ CLEANFILES += lib/meta-flow.inc lib/nx-match.inc
|
||||
EXTRA_DIST += build-aux/extract-ofp-fields
|
||||
|
||||
lib/ofp-actions.inc1: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c
|
||||
$(AM_V_GEN)$(run_python) $^ --prototypes > $@.tmp && mv $@.tmp $@
|
||||
$(AM_V_GEN)$(run_python) $< prototypes $(srcdir)/lib/ofp-actions.c > $@.tmp && mv $@.tmp $@
|
||||
lib/ofp-actions.inc2: $(srcdir)/build-aux/extract-ofp-actions lib/ofp-actions.c
|
||||
$(AM_V_GEN)$(run_python) $^ --definitions > $@.tmp && mv $@.tmp $@
|
||||
$(AM_V_GEN)$(run_python) $< definitions $(srcdir)/lib/ofp-actions.c > $@.tmp && mv $@.tmp $@
|
||||
lib/ofp-actions.lo: lib/ofp-actions.inc1 lib/ofp-actions.inc2
|
||||
CLEANFILES += lib/ofp-actions.inc1 lib/ofp-actions.inc2
|
||||
EXTRA_DIST += build-aux/extract-ofp-actions
|
||||
@@ -601,3 +601,12 @@ lib/ovs-fields.7: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/met
|
||||
$(srcdir)/lib/meta-flow.xml > $@.tmp
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
EXTRA_DIST += lib/meta-flow.xml
|
||||
|
||||
man_MANS += lib/ovs-actions.7
|
||||
CLEANFILES += lib/ovs-actions.7
|
||||
lib/ovs-actions.7: $(srcdir)/build-aux/extract-ofp-actions lib/ovs-actions.xml
|
||||
$(AM_V_GEN)PYTHONIOENCODING=utf8 $(run_python) $< \
|
||||
--ovs-version=$(VERSION) ovs-actions \
|
||||
$(srcdir)/lib/ovs-actions.xml > $@.tmp
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
EXTRA_DIST += lib/ovs-actions.xml
|
||||
|
@@ -461,7 +461,7 @@ learn_parse__(char *orig, char *arg, const struct ofputil_port_map *port_map,
|
||||
}
|
||||
|
||||
/* Parses 'arg' as a set of arguments to the "learn" action and appends a
|
||||
* matching OFPACT_LEARN action to 'ofpacts'. ovs-ofctl(8) describes the
|
||||
* matching OFPACT_LEARN action to 'ofpacts'. ovs-actions(7) describes the
|
||||
* format parsed.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
@@ -483,7 +483,7 @@ learn_parse(char *arg, const struct ofputil_port_map *port_map,
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
|
||||
/* Appends a description of 'learn' to 's', in the format that ovs-actions(7)
|
||||
* describes. */
|
||||
void
|
||||
learn_format(const struct ofpact_learn *learn,
|
||||
|
@@ -136,7 +136,7 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
|
||||
}
|
||||
|
||||
/* Parses 's_' as a set of arguments to the "multipath" action and initializes
|
||||
* 'mp' accordingly. ovs-ofctl(8) describes the format parsed.
|
||||
* 'mp' accordingly. ovs-actions(7) describes the format parsed.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
* error. The caller is responsible for freeing the returned string.*/
|
||||
@@ -214,7 +214,7 @@ multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s)
|
||||
}
|
||||
|
||||
/* Parses 's_' as a set of arguments to the "multipath" action and initializes
|
||||
* 'mp' accordingly. ovs-ofctl(8) describes the format parsed.
|
||||
* 'mp' accordingly. ovs-actions(7) describes the format parsed.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
* error. The caller is responsible for freeing the returned string. */
|
||||
@@ -227,7 +227,7 @@ multipath_parse(struct ofpact_multipath *mp, const char *s_)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
|
||||
/* Appends a description of 'mp' to 's', in the format that ovs-actions(7)
|
||||
* describes. */
|
||||
void
|
||||
multipath_format(const struct ofpact_multipath *mp, struct ds *s)
|
||||
|
@@ -1745,7 +1745,7 @@ oxm_match_from_string(const char *s, struct ofpbuf *b)
|
||||
return match_len;
|
||||
}
|
||||
|
||||
/* Parses 's' as a "move" action, in the form described in ovs-ofctl(8), into
|
||||
/* Parses 's' as a "move" action, in the form described in ovs-actions(7), into
|
||||
* '*move'.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
@@ -1825,7 +1825,7 @@ nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
|
||||
/* nxm_parse_stack_action, works for both push() and pop(). */
|
||||
|
||||
/* Parses 's' as a "push" or "pop" action, in the form described in
|
||||
* ovs-ofctl(8), into '*stack_action'.
|
||||
* ovs-actions(7), into '*stack_action'.
|
||||
*
|
||||
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
||||
* error. The caller is responsible for freeing the returned string. */
|
||||
|
2843
lib/ovs-actions.xml
Normal file
2843
lib/ovs-actions.xml
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5526,7 +5526,7 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
|
||||
<p>
|
||||
With <em>flow-based sampling</em>, <code>sample</code> actions in the
|
||||
OpenFlow flow table drive IPFIX sampling. See
|
||||
<code>ovs-ofctl</code>(8) for a description of the
|
||||
<code>ovs-actions</code>(7) for a description of the
|
||||
<code>sample</code> action.
|
||||
</p>
|
||||
|
||||
|
Reference in New Issue
Block a user