mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 23:05:29 +00:00
ovs-fields: New manpage to document Open vSwitch and OpenFlow fields.
There is still plenty of opportunity for improvement, but this new ovs-fields(7) manpage is much more comprehensive than ovs-ofctl(8) could be. Signed-off-by: Ben Pfaff <blp@ovn.org> Acked-by: Justin Pettit <jpettit@ovn.org>
This commit is contained in:
@@ -109,6 +109,10 @@ Man Pages
|
||||
- `(pdf) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.pdf>`__
|
||||
- `(html) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.html>`__
|
||||
- `(plain text) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.txt>`__
|
||||
* - ovs-fields(7)
|
||||
- `(pdf) <http://openvswitch.org/support/dist-docs/ovs-fields.7.pdf>`__
|
||||
- `(html) <http://openvswitch.org/support/dist-docs/ovs-fields.7.html>`__
|
||||
- `(plain text) <http://openvswitch.org/support/dist-docs/ovs-fields.7.txt>`__
|
||||
* - ovs-l3ping(8)
|
||||
- `(pdf) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.pdf>`__
|
||||
- `(html) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.html>`__
|
||||
|
@@ -1,8 +1,11 @@
|
||||
#! /usr/bin/python
|
||||
|
||||
import getopt
|
||||
import sys
|
||||
import os.path
|
||||
import re
|
||||
import xml.dom.minidom
|
||||
import build.nroff
|
||||
|
||||
line = ""
|
||||
|
||||
@@ -13,6 +16,7 @@ VERSION = {"1.0": 0x01,
|
||||
"1.3": 0x04,
|
||||
"1.4": 0x05,
|
||||
"1.5": 0x06}
|
||||
VERSION_REVERSE = dict((v,k) for k, v in VERSION.iteritems())
|
||||
|
||||
TYPES = {"u8": (1, False),
|
||||
"be16": (2, False),
|
||||
@@ -174,8 +178,7 @@ def parse_oxm(s, prefix, n_bytes):
|
||||
if oxm_class == 0xffff:
|
||||
oxm_length += 4
|
||||
|
||||
header = ("NXM_HEADER(0x%x,0x%x,%s,0,%d)"
|
||||
% (oxm_vendor, oxm_class, oxm_type, oxm_length))
|
||||
header = (oxm_vendor, oxm_class, int(oxm_type), oxm_length)
|
||||
|
||||
if of_version:
|
||||
if of_version not in VERSION:
|
||||
@@ -183,6 +186,8 @@ def parse_oxm(s, prefix, n_bytes):
|
||||
of_version_nr = VERSION[of_version]
|
||||
if of_version_nr < VERSION['1.2']:
|
||||
fatal("%s: claimed version %s predates OXM" % (name, of_version))
|
||||
elif prefix == 'OXM':
|
||||
fatal("%s: missing OpenFlow version number" % name)
|
||||
else:
|
||||
of_version_nr = 0
|
||||
|
||||
@@ -257,13 +262,14 @@ def parse_field(mff, comment):
|
||||
fmt = FORMATTING.get(d['Formatting'])
|
||||
if not fmt:
|
||||
fatal("%s: unknown format %s" % (mff, d['Formatting']))
|
||||
f['formatting'] = d['Formatting']
|
||||
if f['n_bytes'] < fmt[1] or f['n_bytes'] > fmt[2]:
|
||||
fatal("%s: %d-byte field can't be formatted as %s"
|
||||
% (mff, f['n_bytes'], d['Formatting']))
|
||||
f['string'] = fmt[0]
|
||||
|
||||
f['prereqs'] = PREREQS.get(d['Prerequisites'])
|
||||
if not f['prereqs']:
|
||||
f['prereqs'] = d['Prerequisites']
|
||||
if f['prereqs'] not in PREREQS:
|
||||
fatal("%s: unknown prerequisites %s" % (mff, d['Prerequisites']))
|
||||
|
||||
if d['Access'] == 'read-only':
|
||||
@@ -302,8 +308,14 @@ def protocols_to_c(protocols):
|
||||
assert False
|
||||
|
||||
|
||||
def make_meta_flow(fields):
|
||||
output = []
|
||||
def autogen_c_comment():
|
||||
return [
|
||||
"/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */",
|
||||
""]
|
||||
|
||||
def make_meta_flow(meta_flow_h):
|
||||
fields = extract_ofp_fields(meta_flow_h)
|
||||
output = autogen_c_comment()
|
||||
for f in fields:
|
||||
output += ["{"]
|
||||
output += [" %s," % f['mff']]
|
||||
@@ -323,7 +335,7 @@ def make_meta_flow(fields):
|
||||
else:
|
||||
rw = 'false'
|
||||
output += [" %s, %s, %s, %s,"
|
||||
% (f['mask'], f['string'], f['prereqs'], rw)]
|
||||
% (f['mask'], f['string'], PREREQS[f['prereqs']], rw)]
|
||||
|
||||
oxm = f['OXM']
|
||||
of10 = f['OF1.0']
|
||||
@@ -375,24 +387,35 @@ def make_meta_flow(fields):
|
||||
output += [" -1, /* not usable for prefix lookup */"]
|
||||
|
||||
output += ["},"]
|
||||
return output
|
||||
for oline in output:
|
||||
print(oline)
|
||||
|
||||
|
||||
def make_nx_match(fields):
|
||||
output = []
|
||||
def make_nx_match(meta_flow_h):
|
||||
fields = extract_ofp_fields(meta_flow_h)
|
||||
output = autogen_c_comment()
|
||||
print("static struct nxm_field_index all_nxm_fields[] = {")
|
||||
for f in fields:
|
||||
# Sort by OpenFlow version number (nx-match.c depends on this).
|
||||
for oxm in sorted(f['OXM'], key=lambda x: x[2]):
|
||||
header = ("NXM_HEADER(0x%x,0x%x,%s,0,%d)" % oxm[0])
|
||||
print("""{ .nf = { %s, %d, "%s", %s } },""" % (
|
||||
oxm[0], oxm[2], oxm[1], f['mff']))
|
||||
header, oxm[2], oxm[1], f['mff']))
|
||||
print("};")
|
||||
return output
|
||||
for oline in output:
|
||||
print(oline)
|
||||
|
||||
|
||||
def extract_ofp_fields(mode):
|
||||
def extract_ofp_fields(fn):
|
||||
global file_name
|
||||
global input_file
|
||||
global line_number
|
||||
global line
|
||||
|
||||
file_name = fn
|
||||
input_file = open(file_name)
|
||||
line_number = 0
|
||||
|
||||
fields = []
|
||||
|
||||
while True:
|
||||
@@ -495,37 +518,297 @@ def extract_ofp_fields(mode):
|
||||
if n_errors:
|
||||
sys.exit(1)
|
||||
|
||||
print("""\
|
||||
/* Generated automatically; do not modify! "-*- buffer-read-only: t -*- */
|
||||
""")
|
||||
return fields
|
||||
|
||||
## ------------------------ ##
|
||||
## Documentation Generation ##
|
||||
## ------------------------ ##
|
||||
|
||||
if mode == '--meta-flow':
|
||||
output = make_meta_flow(fields)
|
||||
elif mode == '--nx-match':
|
||||
output = make_nx_match(fields)
|
||||
def field_to_xml(field_node, f, body, summary):
|
||||
f["used"] = True
|
||||
|
||||
# Summary.
|
||||
if field_node.hasAttribute('internal'):
|
||||
return
|
||||
|
||||
min_of_version = None
|
||||
min_ovs_version = None
|
||||
for header, name, of_version_nr, ovs_version_s in f['OXM']:
|
||||
if (not name.startswith('NXM')
|
||||
and (min_ovs_version is None or of_version_nr < min_of_version)):
|
||||
min_of_version = of_version_nr
|
||||
ovs_version = [int(x) for x in ovs_version_s.split('.')]
|
||||
if min_ovs_version is None or ovs_version < min_ovs_version:
|
||||
min_ovs_version = ovs_version
|
||||
summary += ["\\fB%s\\fR" % f["name"]]
|
||||
if f["extra_name"]:
|
||||
summary += [" aka \\fB%s\\fR" % f["extra_name"]]
|
||||
summary += [";%d" % f["n_bytes"]]
|
||||
if f["n_bits"] != 8 * f["n_bytes"]:
|
||||
summary += [" (low %d bits)" % f["n_bits"]]
|
||||
summary += [";%s;" % {"MFM_NONE": "no", "MFM_FULLY": "yes"}[f["mask"]]]
|
||||
summary += ["%s;" % {True: "yes", False: "no"}[f["writable"]]]
|
||||
summary += ["%s;" % f["prereqs"]]
|
||||
support = []
|
||||
if min_of_version is not None:
|
||||
support += ["OF %s+" % VERSION_REVERSE[min_of_version]]
|
||||
if min_ovs_version is not None:
|
||||
support += ["OVS %s+" % '.'.join([str(x) for x in min_ovs_version])]
|
||||
summary += ' and '.join(support)
|
||||
summary += ["\n"]
|
||||
|
||||
# Full description.
|
||||
if field_node.hasAttribute('hidden'):
|
||||
return
|
||||
|
||||
title = field_node.attributes['title'].nodeValue
|
||||
|
||||
body += [""".PP
|
||||
\\fB%s Field\\fR
|
||||
.TS
|
||||
tab(;);
|
||||
l lx.
|
||||
""" % title]
|
||||
|
||||
body += ["Name:;\\fB%s\\fR" % f["name"]]
|
||||
if f["extra_name"]:
|
||||
body += [" (aka \\fB%s\\fR)" % f["extra_name"]]
|
||||
body += ['\n']
|
||||
|
||||
body += ["Width:;"]
|
||||
if f["n_bits"] != 8 * f["n_bytes"]:
|
||||
body += ["%d bits (only the least-significant %d bits "
|
||||
"may be nonzero)" % (f["n_bytes"] * 8, f["n_bits"])]
|
||||
elif f["n_bits"] <= 128:
|
||||
body += ["%d bits" % f["n_bits"]]
|
||||
else:
|
||||
assert False
|
||||
body += ["%d bits (%d bytes)" % (f["n_bits"], f["n_bits"] / 8)]
|
||||
body += ['\n']
|
||||
|
||||
return output
|
||||
body += ["Format:;%s\n" % f["formatting"]]
|
||||
|
||||
masks = {"MFM_NONE": "not maskable",
|
||||
"MFM_FULLY": "arbitrary bitwise masks"}
|
||||
body += ["Masking:;%s\n" % masks[f["mask"]]]
|
||||
body += ["Prerequisites:;%s\n" % f["prereqs"]]
|
||||
|
||||
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")
|
||||
access = {True: "read/write",
|
||||
False: "read-only"}[f["writable"]]
|
||||
body += ["Access:;%s\n" % access]
|
||||
|
||||
of10 = {None: "not supported",
|
||||
"exact match": "yes (exact match only)",
|
||||
"CIDR mask": "yes (CIDR match only)"}
|
||||
body += ["OpenFlow 1.0:;%s\n" % of10[f["OF1.0"]]]
|
||||
|
||||
of11 = {None: "not supported",
|
||||
"exact match": "yes (exact match only)",
|
||||
"bitwise mask": "yes"}
|
||||
body += ["OpenFlow 1.1:;%s\n" % of11[f["OF1.1"]]]
|
||||
|
||||
oxms = []
|
||||
for header, name, of_version_nr, ovs_version in [x for x in sorted(f['OXM'], key=lambda x: x[2]) if not x[1].startswith('NXM')]:
|
||||
of_version = VERSION_REVERSE[of_version_nr]
|
||||
oxms += [r"\fB%s\fR (%d) since OpenFlow %s and Open vSwitch %s" % (name, header[2], of_version, ovs_version)]
|
||||
if not oxms:
|
||||
oxms = ['none']
|
||||
body += ['OXM:;T{\n%s\nT}\n' % r'\[char59] '.join(oxms)]
|
||||
|
||||
nxms = []
|
||||
for header, name, of_version_nr, ovs_version in [x for x in sorted(f['OXM'], key=lambda x: x[2]) if x[1].startswith('NXM')]:
|
||||
nxms += [r"\fB%s\fR (%d) since Open vSwitch %s" % (name, header[2], ovs_version)]
|
||||
if not nxms:
|
||||
nxms = ['none']
|
||||
body += ['NXM:;T{\n%s\nT}\n' % r'\[char59] '.join(nxms)]
|
||||
|
||||
body += [".TE\n"]
|
||||
|
||||
body += ['.PP\n']
|
||||
body += [build.nroff.block_xml_to_nroff(field_node.childNodes)]
|
||||
|
||||
def group_xml_to_nroff(group_node, fields):
|
||||
title = group_node.attributes['title'].nodeValue
|
||||
|
||||
summary = []
|
||||
body = []
|
||||
for node in group_node.childNodes:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == 'field':
|
||||
id_ = node.attributes['id'].nodeValue
|
||||
field_to_xml(node, fields[id_], body, summary)
|
||||
else:
|
||||
body += [build.nroff.block_xml_to_nroff([node])]
|
||||
|
||||
content = [
|
||||
'.bp\n',
|
||||
'.SH \"%s\"\n' % build.nroff.text_to_nroff(title.upper() + " FIELDS"),
|
||||
'.SS "Summary:"\n',
|
||||
'.TS\n',
|
||||
'tab(;);\n',
|
||||
'l l l l l l l.\n',
|
||||
'Name;Bytes;Mask;RW?;Prereqs;NXM/OXM Support\n',
|
||||
'\_;\_;\_;\_;\_;\_\n']
|
||||
content += summary
|
||||
content += ['.TE\n']
|
||||
content += body
|
||||
return ''.join(content)
|
||||
|
||||
def make_oxm_classes_xml(document):
|
||||
s = '''tab(;);
|
||||
l l l.
|
||||
Prefix;Vendor;Class
|
||||
\_;\_;\_
|
||||
'''
|
||||
for key in sorted(OXM_CLASSES, key=OXM_CLASSES.get):
|
||||
vendor, class_ = OXM_CLASSES.get(key)
|
||||
s += r"\fB%s\fR;" % key.rstrip('_')
|
||||
if vendor:
|
||||
s += r"\fL0x%08x\fR;" % vendor
|
||||
else:
|
||||
s += "(none);"
|
||||
s += r"\fL0x%04x\fR;" % class_
|
||||
s += "\n"
|
||||
e = document.createElement('tbl')
|
||||
e.appendChild(document.createTextNode(s))
|
||||
return e
|
||||
|
||||
def recursively_replace(node, name, replacement):
|
||||
for child in node.childNodes:
|
||||
if child.nodeType == node.ELEMENT_NODE:
|
||||
if child.tagName == name:
|
||||
node.replaceChild(replacement, child)
|
||||
else:
|
||||
recursively_replace(child, name, replacement)
|
||||
|
||||
def make_ovs_fields(meta_flow_h, meta_flow_xml):
|
||||
fields = extract_ofp_fields(meta_flow_h)
|
||||
fields_map = {}
|
||||
for f in fields:
|
||||
fields_map[f['mff']] = f
|
||||
|
||||
document = xml.dom.minidom.parse(meta_flow_xml)
|
||||
doc = document.documentElement
|
||||
|
||||
global version
|
||||
if version == None:
|
||||
version = "UNKNOWN"
|
||||
|
||||
print('''\
|
||||
'\\" tp
|
||||
.\\" -*- mode: troff; coding: utf-8 -*-
|
||||
.TH "ovs\-fields" 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 URL
|
||||
\\\\$2 \\(laURL: \\\\$1 \\(ra\\\\$3
|
||||
..
|
||||
.if \\n[.g] .mso www.tmac
|
||||
.SH NAME
|
||||
ovs\-fields \- protocol header fields in OpenFlow and Open vSwitch
|
||||
.
|
||||
.PP
|
||||
''') % version
|
||||
|
||||
recursively_replace(doc, 'oxm_classes', make_oxm_classes_xml(document))
|
||||
|
||||
s = ''
|
||||
for node in doc.childNodes:
|
||||
if node.nodeType == node.ELEMENT_NODE and node.tagName == "group":
|
||||
s += group_xml_to_nroff(node, fields_map)
|
||||
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])
|
||||
|
||||
for f in fields:
|
||||
if "used" not in f:
|
||||
fatal("%s: field not documented" % f["mff"])
|
||||
if n_errors:
|
||||
sys.exit(1)
|
||||
elif sys.argv[2] in ('--meta-flow', '--nx-match'):
|
||||
global file_name
|
||||
global input_file
|
||||
global line_number
|
||||
file_name = sys.argv[1]
|
||||
input_file = open(file_name)
|
||||
line_number = 0
|
||||
|
||||
for oline in extract_ofp_fields(sys.argv[2]):
|
||||
print(oline)
|
||||
else:
|
||||
sys.stderr.write("invalid arguments; use --help for help\n")
|
||||
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'\[<=]')
|
||||
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__":
|
||||
argv0 = sys.argv[0]
|
||||
try:
|
||||
options, args = getopt.gnu_getopt(sys.argv[1:], 'h',
|
||||
['help', 'ovs-version='])
|
||||
except getopt.GetoptError, 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 = {"meta-flow": (make_meta_flow, 1),
|
||||
"nx-match": (make_nx_match, 1),
|
||||
"ovs-fields": (make_ovs_fields, 2)}
|
||||
|
||||
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:])
|
||||
|
@@ -34,64 +34,7 @@ struct match;
|
||||
/* Open vSwitch fields
|
||||
* ===================
|
||||
*
|
||||
* A "field" is a property of a packet. Most familiarly, "data fields" are
|
||||
* fields that can be extracted from a packet.
|
||||
*
|
||||
* Some data fields are always present as a consequence of the basic networking
|
||||
* technology in use. Ethernet is the assumed base technology for current
|
||||
* versions of OpenFlow and Open vSwitch, so Ethernet header fields are always
|
||||
* available.
|
||||
*
|
||||
* Other data fields are not always present. A packet contains ARP fields, for
|
||||
* example, only when its Ethernet header indicates the Ethertype for ARP,
|
||||
* 0x0806. We say that a field is "applicable" when it is it present in a
|
||||
* packet, and "inapplicable" when it is not, and refer to the conditions that
|
||||
* determine whether a field is applicable as "prerequisites". Some
|
||||
* VLAN-related fields are a special case: these fields are always applicable,
|
||||
* but have a designated value or bit that indicates whether a VLAN header is
|
||||
* present, with the remaining values or bits indicating the VLAN header's
|
||||
* content (if it is present). See MFF_VLAN_TCI for an example.
|
||||
*
|
||||
* Conceptually, an inapplicable field does not have a value, not even a
|
||||
* nominal ``value'' such as all-zero-bits. In many circumstances, OpenFlow
|
||||
* and Open vSwitch allow references only to applicable fields. For example,
|
||||
* one may match a given field only if the match includes the field's
|
||||
* prerequisite, e.g. matching an ARP field is only allowed if one also matches
|
||||
* on Ethertype 0x0806.
|
||||
*
|
||||
* (Practically, however, OVS represents a field's value as some fixed member
|
||||
* in its "struct flow", so accessing that member will obtain some value. Some
|
||||
* members are used for more than one purpose, e.g. the "tp_src" member
|
||||
* represents the TCP, UDP, and SCTP source port, so the value read may not
|
||||
* even make sense. For this reason, it is important to know whether a field's
|
||||
* prerequisites are satisfied before attempting to read it.)
|
||||
*
|
||||
* Sometimes a packet may contain multiple instances of a header. For example,
|
||||
* a packet may contain multiple VLAN or MPLS headers, and tunnels can cause
|
||||
* any data field to recur. OpenFlow and Open vSwitch do not address these
|
||||
* cases uniformly. For VLAN and MPLS headers, only the outermost header is
|
||||
* accessible, so that inner headers may be accessed only by ``popping''
|
||||
* (removing) the outer header. (Open vSwitch supports only a single VLAN
|
||||
* header in any case.) For tunnels, e.g. GRE or VXLAN, the outer header and
|
||||
* inner headers are treated as different data fields.
|
||||
*
|
||||
* OpenFlow and Open vSwitch support some fields other than data fields.
|
||||
* "Metadata fields" relate to the origin or treatment of a packet, but they
|
||||
* are not extracted from the packet data itself. One example is the physical
|
||||
* port on which a packet arrived at the switch. "Register fields" act like
|
||||
* variables: they give an OpenFlow switch space for temporary storage while
|
||||
* processing a packet. Existing metadata and register fields have no
|
||||
* prerequisites.
|
||||
*
|
||||
* A field's value consists of an integral number of bytes. Most data fields
|
||||
* are copied directly from protocol headers, e.g. at layer 2, MFF_ETH_SRC is
|
||||
* copied from the Ethernet source address and MFF_ETH_DST from the destination
|
||||
* address. Other data fields are copied from a packet with padding, usually
|
||||
* with zeros and in the most significant positions (see e.g. MFF_MPLS_LABEL)
|
||||
* but not always (see e.g. MFF_IP_DSCP). A final category of data fields is
|
||||
* transformed in other ways as they are copied from the packets, to make them
|
||||
* more useful for matching, e.g. MFF_IP_FRAG describes whether a packet is a
|
||||
* fragment but it is not copied directly from the IP header.
|
||||
* Refer to ovs-fields(7) for a detailed introduction to Open vSwitch fields.
|
||||
*
|
||||
*
|
||||
* Field specifications
|
||||
@@ -115,8 +58,8 @@ struct match;
|
||||
*
|
||||
* New fields should have only one name.
|
||||
*
|
||||
* - Any number of paragraphs of free text that describe the field. This
|
||||
* is meant for human readers, so extract-ofp-fields ignores it.
|
||||
* - Any number of paragraphs of free text that describe the field. These
|
||||
* are kept brief because the main description is in meta-flow.xml.
|
||||
*
|
||||
* - A final paragraph that consists of a series of key-value pairs, one
|
||||
* per line, in the form "key: value." where the period at the end of the
|
||||
@@ -409,18 +352,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
*
|
||||
* Flags representing aspects of tunnel behavior.
|
||||
*
|
||||
* This field currently only has a single flag defined:
|
||||
*
|
||||
* - NX_TUN_FLAG_OAM: The tunnel protocol indicated that this is an
|
||||
* OAM control packet.
|
||||
*
|
||||
* The switch may reject matches against values that it is not aware of.
|
||||
*
|
||||
* Note that it is possible for newer version of Open vSwitch to
|
||||
* introduce additional flags with varying meaning. It is therefore not
|
||||
* recommended to use an exact match on this field since the behavior of
|
||||
* these new flags is unknown and should be ignored.
|
||||
*
|
||||
* For non-tunneled packets, the value is 0.
|
||||
*
|
||||
* Type: be16 (low 1 bits).
|
||||
@@ -743,42 +674,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
/* "ct_state".
|
||||
*
|
||||
* Connection tracking state. The field is populated by the NXAST_CT
|
||||
* action. The following bit values describe the state of the connection:
|
||||
*
|
||||
* - New (0x01): This is the beginning of a new connection.
|
||||
* - Established (0x02): This is part of an already existing connection.
|
||||
* - Related (0x04): This is a separate connection that is related to an
|
||||
* existing connection.
|
||||
* - Reply (0x08): This flow is in the reply direction, ie it did not
|
||||
* initiate the connection.
|
||||
* - Invalid (0x10): This flow could not be associated with a connection.
|
||||
* This could be set for a variety of reasons,
|
||||
* including (but not limited to):
|
||||
* - L3/L4 protocol handler is not loaded/unavailable.
|
||||
* - L3/L4 protocol handler determines that the packet
|
||||
* is malformed or invalid for the current FSM stage.
|
||||
* - Packets are unexpected length for protocol.
|
||||
* - Tracked (0x20): Connection tracking has occurred.
|
||||
*
|
||||
* The "Tracked" bit corresponds to the packet_state as described in the
|
||||
* description of NXAST_CT action. The remaining bits correspond to
|
||||
* connection state. The "New" bit implies that the connection state
|
||||
* is uncommitted, while "Established" implies that it has previously been
|
||||
* committed.
|
||||
*
|
||||
* There are additional constraints on the ct_state bits, listed in order
|
||||
* of precedence below:
|
||||
*
|
||||
* - If "Tracked" is unset, no other bits may be set.
|
||||
* - If "Tracked" is set, one or more other bits may be set.
|
||||
* - If "Invalid" is set, only the "Tracked" bit is also set.
|
||||
* - The "New" and "Established" bits are mutually exclusive.
|
||||
* - The "New" and "Reply" bits are mutually exclusive.
|
||||
* - The "Related" bit may be set in conjunction with any other bits.
|
||||
* Connections that are identified as "Related" are separate
|
||||
* connections from the originating connection, so must be committed
|
||||
* separately. All packets for a related connection will have the
|
||||
* "Related" bit set (not just the initial packet).
|
||||
* action.
|
||||
*
|
||||
* Type: be32.
|
||||
* Maskable: bitwise.
|
||||
@@ -894,15 +790,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
#if FLOW_N_XREGS == 8
|
||||
/* "xreg<N>".
|
||||
*
|
||||
* OpenFlow 1.5 ``extended register". Each extended register
|
||||
* overlays two of the Open vSwitch extension 32-bit registers:
|
||||
* xreg0 overlays reg0 and reg1, with reg0 supplying the
|
||||
* most-significant bits of xreg0 and reg1 the least-significant.
|
||||
* xreg1 similarly overlays reg2 and reg3, and so on.
|
||||
*
|
||||
* These registers were introduced in OpenFlow 1.5, but EXT-244 in the ONF
|
||||
* JIRA also publishes them as a (draft) OpenFlow extension to OpenFlow
|
||||
* 1.3.
|
||||
* OpenFlow 1.5 ``extended register".
|
||||
*
|
||||
* Type: be64.
|
||||
* Maskable: bitwise.
|
||||
@@ -927,11 +815,7 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
#if FLOW_N_XXREGS == 4
|
||||
/* "xxreg<N>".
|
||||
*
|
||||
* ``extended-extended register". Each of these extended registers
|
||||
* overlays four of the Open vSwitch extension 32-bit registers:
|
||||
* xxreg0 overlays reg0 through reg3, with reg0 supplying the
|
||||
* most-significant bits of xxreg0 and reg3 the least-significant.
|
||||
* xxreg1 similarly overlays reg4 and reg7.
|
||||
* ``extended-extended register".
|
||||
*
|
||||
* Type: be128.
|
||||
* Maskable: bitwise.
|
||||
@@ -964,8 +848,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
*
|
||||
* Source address in Ethernet header.
|
||||
*
|
||||
* This field was not maskable before Open vSwitch 1.8.
|
||||
*
|
||||
* Type: MAC.
|
||||
* Maskable: bitwise.
|
||||
* Formatting: Ethernet.
|
||||
@@ -982,10 +864,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
*
|
||||
* Destination address in Ethernet header.
|
||||
*
|
||||
* Before Open vSwitch 1.8, the allowed masks were restricted to
|
||||
* 00:00:00:00:00:00, fe:ff:ff:ff:ff:ff, 01:00:00:00:00:00,
|
||||
* ff:ff:ff:ff:ff:ff.
|
||||
*
|
||||
* Type: MAC.
|
||||
* Maskable: bitwise.
|
||||
* Formatting: Ethernet.
|
||||
@@ -1002,11 +880,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
*
|
||||
* Packet's Ethernet type.
|
||||
*
|
||||
* For an Ethernet II packet this is taken from the Ethernet header. For
|
||||
* an 802.2 LLC+SNAP header with OUI 00-00-00 this is taken from the SNAP
|
||||
* header. A packet that has neither format has value 0x05ff
|
||||
* (OFP_DL_TYPE_NOT_ETH_TYPE).
|
||||
*
|
||||
* For a packet with an 802.1Q header, this is the type of the encapsulated
|
||||
* frame.
|
||||
*
|
||||
@@ -1040,36 +913,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
* (TCI) field, with the CFI bit forced to 1. For a packet with no 802.1Q
|
||||
* header, this has value 0.
|
||||
*
|
||||
* This field can be used in various ways:
|
||||
*
|
||||
* - If it is not constrained at all, the nx_match matches packets
|
||||
* without an 802.1Q header or with an 802.1Q header that has any TCI
|
||||
* value.
|
||||
*
|
||||
* - Testing for an exact match with 0 matches only packets without an
|
||||
* 802.1Q header.
|
||||
*
|
||||
* - Testing for an exact match with a TCI value with CFI=1 matches
|
||||
* packets that have an 802.1Q header with a specified VID and PCP.
|
||||
*
|
||||
* - Testing for an exact match with a nonzero TCI value with CFI=0 does
|
||||
* not make sense. The switch may reject this combination.
|
||||
*
|
||||
* - Testing with a specific VID and CFI=1, with nxm_mask=0x1fff, matches
|
||||
* packets that have an 802.1Q header with that VID (and any PCP).
|
||||
*
|
||||
* - Testing with a specific PCP and CFI=1, with nxm_mask=0xf000, matches
|
||||
* packets that have an 802.1Q header with that PCP (and any VID).
|
||||
*
|
||||
* - Testing with nxm_value=0, nxm_mask=0x0fff matches packets with no
|
||||
* 802.1Q header or with an 802.1Q header with a VID of 0.
|
||||
*
|
||||
* - Testing with nxm_value=0, nxm_mask=0xe000 matches packets with no
|
||||
* 802.1Q header or with an 802.1Q header with a PCP of 0.
|
||||
*
|
||||
* - Testing with nxm_value=0, nxm_mask=0xefff matches packets with no
|
||||
* 802.1Q header or with an 802.1Q header with both VID and PCP of 0.
|
||||
*
|
||||
* Type: be16.
|
||||
* Maskable: bitwise.
|
||||
* Formatting: hexadecimal.
|
||||
@@ -1399,42 +1242,6 @@ enum OVS_PACKED_ENUM mf_field_id {
|
||||
*
|
||||
* IP fragment information.
|
||||
*
|
||||
* This field has three possible values:
|
||||
*
|
||||
* - A packet that is not an IP fragment has value 0.
|
||||
*
|
||||
* - A packet that is an IP fragment with offset 0 (the first fragment)
|
||||
* has bit 0 set and thus value 1.
|
||||
*
|
||||
* - A packet that is an IP fragment with nonzero offset has bits 0 and 1
|
||||
* set and thus value 3.
|
||||
*
|
||||
* NX_IP_FRAG_ANY and NX_IP_FRAG_LATER are declared to symbolically
|
||||
* represent the meanings of bits 0 and 1.
|
||||
*
|
||||
* The switch may reject matches against values that can never appear.
|
||||
*
|
||||
* It is important to understand how this field interacts with the OpenFlow
|
||||
* IP fragment handling mode:
|
||||
*
|
||||
* - In OFPC_FRAG_DROP mode, the OpenFlow switch drops all IP fragments
|
||||
* before they reach the flow table, so every packet that is available
|
||||
* for matching will have value 0 in this field.
|
||||
*
|
||||
* - Open vSwitch does not implement OFPC_FRAG_REASM mode, but if it did
|
||||
* then IP fragments would be reassembled before they reached the flow
|
||||
* table and again every packet available for matching would always
|
||||
* have value 0.
|
||||
*
|
||||
* - In OFPC_FRAG_NORMAL mode, all three values are possible, but
|
||||
* OpenFlow 1.0 says that fragments' transport ports are always 0, even
|
||||
* for the first fragment, so this does not provide much extra
|
||||
* information.
|
||||
*
|
||||
* - In OFPC_FRAG_NX_MATCH mode, all three values are possible. For
|
||||
* fragments with offset 0, Open vSwitch makes L4 header information
|
||||
* available.
|
||||
*
|
||||
* Type: u8 (low 2 bits).
|
||||
* Maskable: bitwise.
|
||||
* Formatting: frag.
|
||||
|
@@ -495,10 +495,12 @@ lib/dirs.c: lib/dirs.c.in Makefile
|
||||
mv lib/dirs.c.tmp lib/dirs.c
|
||||
|
||||
lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h
|
||||
$(AM_V_GEN)$(run_python) $^ --meta-flow > $@.tmp && mv $@.tmp $@
|
||||
$(AM_V_GEN)$(run_python) $< meta-flow $(srcdir)/include/openvswitch/meta-flow.h > $@.tmp
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
lib/meta-flow.lo: lib/meta-flow.inc
|
||||
lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h
|
||||
$(AM_V_GEN)$(run_python) $^ --nx-match > $@.tmp && mv $@.tmp $@
|
||||
lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h
|
||||
$(AM_V_GEN)$(run_python) $< nx-match $(srcdir)/include/openvswitch/meta-flow.h > $@.tmp
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
lib/nx-match.lo: lib/nx-match.inc
|
||||
CLEANFILES += lib/meta-flow.inc lib/nx-match.inc
|
||||
EXTRA_DIST += build-aux/extract-ofp-fields
|
||||
@@ -535,3 +537,12 @@ lib-install-data-local:
|
||||
$(MKDIR_P) $(DESTDIR)$(LOGDIR)
|
||||
$(MKDIR_P) $(DESTDIR)$(DBDIR)
|
||||
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/openvswitch
|
||||
|
||||
man_MANS += lib/ovs-fields.7
|
||||
lib/ovs-fields.7: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h lib/meta-flow.xml
|
||||
$(AM_V_GEN)PYTHONIOENCODING=utf8 $(run_python) $< \
|
||||
--ovs-version=$(VERSION) ovs-fields \
|
||||
$(srcdir)/include/openvswitch/meta-flow.h \
|
||||
$(srcdir)/lib/meta-flow.xml > $@.tmp
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
EXTRA_DIST += lib/meta-flow.xml
|
||||
|
4360
lib/meta-flow.xml
Normal file
4360
lib/meta-flow.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -887,7 +887,7 @@
|
||||
|
||||
<p>
|
||||
Some logical flows can map to the Open vSwitch ``conjunctive match''
|
||||
extension (see <code>ovs-ofctl</code>(8)). Flows with a
|
||||
extension (see <code>ovs-fields</code>(7)). Flows with a
|
||||
<code>conjunction</code> action use an OpenFlow cookie of 0, because
|
||||
they can correspond to multiple logical flows. The OpenFlow flow for a
|
||||
conjunctive match includes a match on <code>conj_id</code>.
|
||||
|
@@ -284,7 +284,7 @@
|
||||
|
||||
<li>
|
||||
Some logical flows can map to the Open vSwitch ``conjunctive match''
|
||||
extension (see <code>ovs-ofctl</code>(8)). Currently
|
||||
extension (see <code>ovs-fields</code>(7)). Currently
|
||||
<code>ovn-trace</code> cannot display the flows with
|
||||
<code>conjunction</code> actions that effectively produce the
|
||||
<code>conj_id</code> match.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2010, 2011, 2012, 2015 Nicira, Inc.
|
||||
# Copyright (c) 2010, 2011, 2012, 2015, 2016, 2017 Nicira, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@ import sys
|
||||
from ovs.db import error
|
||||
|
||||
|
||||
def text_to_nroff(s, font=r'\fR'):
|
||||
def text_to_nroff(s, font=r'\fR', escape_dot=True):
|
||||
def escape(match):
|
||||
c = match.group(0)
|
||||
|
||||
@@ -47,9 +47,13 @@ def text_to_nroff(s, font=r'\fR'):
|
||||
elif c == "'":
|
||||
return r'\(cq'
|
||||
elif c == ".":
|
||||
# groff(7) says that . can be escaped by \. but in practice groff
|
||||
# still gives an error with \. at the beginning of a line.
|
||||
return r'\[char46]'
|
||||
if escape_dot:
|
||||
# groff(7) says that . can be escaped by \. but in practice
|
||||
# groff still gives an error with \. at the beginning of a
|
||||
# line.
|
||||
return r'\[char46]'
|
||||
else:
|
||||
return '.'
|
||||
else:
|
||||
raise error.Error("bad escape")
|
||||
|
||||
@@ -87,15 +91,27 @@ def inline_xml_to_nroff(node, font, to_upper=False, newline='\n'):
|
||||
s += node.attributes['group'].nodeValue
|
||||
elif node.hasAttribute('db'):
|
||||
s += node.attributes['db'].nodeValue
|
||||
elif node.hasAttribute('field'):
|
||||
s += node.attributes['field'].nodeValue
|
||||
else:
|
||||
raise error.Error("'ref' lacks required attributes: %s"
|
||||
% list(node.attributes.keys()))
|
||||
return s + font
|
||||
elif node.tagName in ['var', 'dfn', 'i']:
|
||||
elif node.tagName in ['var', 'dfn', 'i', 'cite']:
|
||||
s = r'\fI'
|
||||
for child in node.childNodes:
|
||||
s += inline_xml_to_nroff(child, r'\fI', to_upper, newline)
|
||||
return s + font
|
||||
elif node.tagName in ['literal']:
|
||||
s = r'\fL'
|
||||
for child in node.childNodes:
|
||||
s += inline_xml_to_nroff(child, r'\fL')
|
||||
return s + font
|
||||
elif node.tagName == 'url':
|
||||
return ('\n.URL "'
|
||||
+ text_to_nroff(node.attributes['href'].nodeValue,
|
||||
escape_dot=False)
|
||||
+ '"\n')
|
||||
else:
|
||||
raise error.Error("element <%s> unknown or invalid here"
|
||||
% node.tagName)
|
||||
@@ -111,11 +127,21 @@ def pre_to_nroff(nodes, para, font):
|
||||
# from preformatted text.
|
||||
s = para + '\n.nf\n' + font
|
||||
for node in nodes:
|
||||
s += inline_xml_to_nroff(node, font, False, '\n.br\n' + font)
|
||||
s += inline_xml_to_nroff(node, font, False, '\n.br\n' + font) + '\\fR'
|
||||
s += '\n.fi\n'
|
||||
return s
|
||||
|
||||
|
||||
def tbl_to_nroff(nodes, para):
|
||||
s = para + '\n.TS\n'
|
||||
for node in nodes:
|
||||
if node.nodeType != node.TEXT_NODE:
|
||||
fatal("<tbl> element may only have text children")
|
||||
s += node.data + '\n'
|
||||
s += '.TE\n'
|
||||
return s
|
||||
|
||||
|
||||
def fatal(msg):
|
||||
sys.stderr.write('%s\n' % msg)
|
||||
sys.exit(1)
|
||||
@@ -262,12 +288,17 @@ fillval = .2
|
||||
|
||||
|
||||
def block_xml_to_nroff(nodes, para='.PP'):
|
||||
HEADER_TAGS = ('h1', 'h2', 'h3')
|
||||
HEADER_TAGS = ('h1', 'h2', 'h3', 'h4')
|
||||
s = ''
|
||||
prev = ''
|
||||
for node in nodes:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
s += text_to_nroff(node.data)
|
||||
if s == '' and para != '.IP':
|
||||
s = para + '\n'
|
||||
text = re.sub(r'\s+', ' ', node.data)
|
||||
if s.endswith(' '):
|
||||
text = text.lstrip()
|
||||
s += text_to_nroff(text)
|
||||
s = s.lstrip()
|
||||
elif node.nodeType == node.ELEMENT_NODE:
|
||||
if node.tagName in ['ul', 'ol']:
|
||||
@@ -332,12 +363,15 @@ def block_xml_to_nroff(nodes, para='.PP'):
|
||||
if s != "":
|
||||
if not s.endswith("\n"):
|
||||
s += "\n"
|
||||
nroffTag = {'h1': 'SH', 'h2': 'SS', 'h3': 'ST'}[node.tagName]
|
||||
s += '.%s "' % nroffTag
|
||||
nroffTag = {'h1': 'SH',
|
||||
'h2': 'SS',
|
||||
'h3': 'ST',
|
||||
'h4': 'SU'}[node.tagName]
|
||||
to_upper = node.tagName == 'h1'
|
||||
s += ".%s \"" % nroffTag
|
||||
for child_node in node.childNodes:
|
||||
s += inline_xml_to_nroff(child_node, r'\fR',
|
||||
to_upper=(nroffTag == 'SH'))
|
||||
s += '"\n'
|
||||
s += inline_xml_to_nroff(child_node, r'\fR', to_upper)
|
||||
s += "\"\n"
|
||||
elif node.tagName == 'pre':
|
||||
fixed = node.getAttribute('fixed')
|
||||
if fixed == 'yes':
|
||||
@@ -345,6 +379,8 @@ def block_xml_to_nroff(nodes, para='.PP'):
|
||||
else:
|
||||
font = r'\fB'
|
||||
s += pre_to_nroff(node.childNodes, para, font)
|
||||
elif node.tagName == 'tbl':
|
||||
s += tbl_to_nroff(node.childNodes, para)
|
||||
elif node.tagName == 'diagram':
|
||||
s += diagram_to_nroff(node.childNodes, para)
|
||||
else:
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -2236,9 +2236,9 @@
|
||||
<li>
|
||||
<code>gbp</code>: VXLAN-GBP allows to transport the group policy
|
||||
context of a packet across the VXLAN tunnel to other network
|
||||
peers. See the field description of <code>tun_gbp_id</code> and
|
||||
<code>tun_gbp_flags</code> in ovs-ofctl(8) for additional
|
||||
information.
|
||||
peers. See the description of <code>tun_gbp_id</code> and
|
||||
<code>tun_gbp_flags</code> in <code>ovs-fields</code>(7) for
|
||||
additional information.
|
||||
(<code>https://tools.ietf.org/html/draft-smith-vxlan-group-policy</code>)
|
||||
</li>
|
||||
</ul>
|
||||
|
Reference in New Issue
Block a user