2
0
mirror of https://github.com/openvswitch/ovs synced 2025-09-02 15:25:22 +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:
Ben Pfaff
2017-01-25 13:58:03 -08:00
parent 902323d32e
commit 96fee5e0a2
10 changed files with 4775 additions and 1242 deletions

View File

@@ -109,6 +109,10 @@ Man Pages
- `(pdf) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.pdf>`__ - `(pdf) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.pdf>`__
- `(html) <http://openvswitch.org/support/dist-docs/ovs-dpctl-top.8.html>`__ - `(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>`__ - `(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) * - ovs-l3ping(8)
- `(pdf) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.pdf>`__ - `(pdf) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.pdf>`__
- `(html) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.html>`__ - `(html) <http://openvswitch.org/support/dist-docs/ovs-l3ping.8.html>`__

View File

@@ -1,8 +1,11 @@
#! /usr/bin/python #! /usr/bin/python
import getopt
import sys import sys
import os.path import os.path
import re import re
import xml.dom.minidom
import build.nroff
line = "" line = ""
@@ -13,6 +16,7 @@ VERSION = {"1.0": 0x01,
"1.3": 0x04, "1.3": 0x04,
"1.4": 0x05, "1.4": 0x05,
"1.5": 0x06} "1.5": 0x06}
VERSION_REVERSE = dict((v,k) for k, v in VERSION.iteritems())
TYPES = {"u8": (1, False), TYPES = {"u8": (1, False),
"be16": (2, False), "be16": (2, False),
@@ -174,8 +178,7 @@ def parse_oxm(s, prefix, n_bytes):
if oxm_class == 0xffff: if oxm_class == 0xffff:
oxm_length += 4 oxm_length += 4
header = ("NXM_HEADER(0x%x,0x%x,%s,0,%d)" header = (oxm_vendor, oxm_class, int(oxm_type), oxm_length)
% (oxm_vendor, oxm_class, oxm_type, oxm_length))
if of_version: if of_version:
if of_version not in VERSION: if of_version not in VERSION:
@@ -183,6 +186,8 @@ def parse_oxm(s, prefix, n_bytes):
of_version_nr = VERSION[of_version] of_version_nr = VERSION[of_version]
if of_version_nr < VERSION['1.2']: if of_version_nr < VERSION['1.2']:
fatal("%s: claimed version %s predates OXM" % (name, of_version)) fatal("%s: claimed version %s predates OXM" % (name, of_version))
elif prefix == 'OXM':
fatal("%s: missing OpenFlow version number" % name)
else: else:
of_version_nr = 0 of_version_nr = 0
@@ -257,13 +262,14 @@ def parse_field(mff, comment):
fmt = FORMATTING.get(d['Formatting']) fmt = FORMATTING.get(d['Formatting'])
if not fmt: if not fmt:
fatal("%s: unknown format %s" % (mff, d['Formatting'])) fatal("%s: unknown format %s" % (mff, d['Formatting']))
f['formatting'] = d['Formatting']
if f['n_bytes'] < fmt[1] or f['n_bytes'] > fmt[2]: if f['n_bytes'] < fmt[1] or f['n_bytes'] > fmt[2]:
fatal("%s: %d-byte field can't be formatted as %s" fatal("%s: %d-byte field can't be formatted as %s"
% (mff, f['n_bytes'], d['Formatting'])) % (mff, f['n_bytes'], d['Formatting']))
f['string'] = fmt[0] f['string'] = fmt[0]
f['prereqs'] = PREREQS.get(d['Prerequisites']) f['prereqs'] = d['Prerequisites']
if not f['prereqs']: if f['prereqs'] not in PREREQS:
fatal("%s: unknown prerequisites %s" % (mff, d['Prerequisites'])) fatal("%s: unknown prerequisites %s" % (mff, d['Prerequisites']))
if d['Access'] == 'read-only': if d['Access'] == 'read-only':
@@ -302,8 +308,14 @@ def protocols_to_c(protocols):
assert False assert False
def make_meta_flow(fields): def autogen_c_comment():
output = [] 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: for f in fields:
output += ["{"] output += ["{"]
output += [" %s," % f['mff']] output += [" %s," % f['mff']]
@@ -323,7 +335,7 @@ def make_meta_flow(fields):
else: else:
rw = 'false' rw = 'false'
output += [" %s, %s, %s, %s," output += [" %s, %s, %s, %s,"
% (f['mask'], f['string'], f['prereqs'], rw)] % (f['mask'], f['string'], PREREQS[f['prereqs']], rw)]
oxm = f['OXM'] oxm = f['OXM']
of10 = f['OF1.0'] of10 = f['OF1.0']
@@ -375,24 +387,35 @@ def make_meta_flow(fields):
output += [" -1, /* not usable for prefix lookup */"] output += [" -1, /* not usable for prefix lookup */"]
output += ["},"] output += ["},"]
return output for oline in output:
print(oline)
def make_nx_match(fields): def make_nx_match(meta_flow_h):
output = [] fields = extract_ofp_fields(meta_flow_h)
output = autogen_c_comment()
print("static struct nxm_field_index all_nxm_fields[] = {") print("static struct nxm_field_index all_nxm_fields[] = {")
for f in fields: for f in fields:
# Sort by OpenFlow version number (nx-match.c depends on this). # Sort by OpenFlow version number (nx-match.c depends on this).
for oxm in sorted(f['OXM'], key=lambda x: x[2]): 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 } },""" % ( print("""{ .nf = { %s, %d, "%s", %s } },""" % (
oxm[0], oxm[2], oxm[1], f['mff'])) header, oxm[2], oxm[1], f['mff']))
print("};") 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 global line
file_name = fn
input_file = open(file_name)
line_number = 0
fields = [] fields = []
while True: while True:
@@ -495,37 +518,297 @@ def extract_ofp_fields(mode):
if n_errors: if n_errors:
sys.exit(1) sys.exit(1)
print("""\ return fields
/* Generated automatically; do not modify! "-*- buffer-read-only: t -*- */
""") ## ------------------------ ##
## Documentation Generation ##
## ------------------------ ##
if mode == '--meta-flow': def field_to_xml(field_node, f, body, summary):
output = make_meta_flow(fields) f["used"] = True
elif mode == '--nx-match':
output = make_nx_match(fields) # 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: 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__': access = {True: "read/write",
if '--help' in sys.argv: False: "read-only"}[f["writable"]]
usage() body += ["Access:;%s\n" % access]
elif len(sys.argv) != 3:
sys.stderr.write("exactly two arguments required; " of10 = {None: "not supported",
"use --help for help\n") "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) 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]): output = []
print(oline) for oline in s.split("\n"):
else: oline = oline.strip()
sys.stderr.write("invalid arguments; use --help for help\n")
# 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) 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:])

View File

@@ -34,64 +34,7 @@ struct match;
/* Open vSwitch fields /* Open vSwitch fields
* =================== * ===================
* *
* A "field" is a property of a packet. Most familiarly, "data fields" are * Refer to ovs-fields(7) for a detailed introduction to Open vSwitch fields.
* 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.
* *
* *
* Field specifications * Field specifications
@@ -115,8 +58,8 @@ struct match;
* *
* New fields should have only one name. * New fields should have only one name.
* *
* - Any number of paragraphs of free text that describe the field. This * - Any number of paragraphs of free text that describe the field. These
* is meant for human readers, so extract-ofp-fields ignores it. * 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 * - 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 * 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. * 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. * For non-tunneled packets, the value is 0.
* *
* Type: be16 (low 1 bits). * Type: be16 (low 1 bits).
@@ -743,42 +674,7 @@ enum OVS_PACKED_ENUM mf_field_id {
/* "ct_state". /* "ct_state".
* *
* Connection tracking state. The field is populated by the NXAST_CT * Connection tracking state. The field is populated by the NXAST_CT
* action. The following bit values describe the state of the connection: * action.
*
* - 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).
* *
* Type: be32. * Type: be32.
* Maskable: bitwise. * Maskable: bitwise.
@@ -894,15 +790,7 @@ enum OVS_PACKED_ENUM mf_field_id {
#if FLOW_N_XREGS == 8 #if FLOW_N_XREGS == 8
/* "xreg<N>". /* "xreg<N>".
* *
* OpenFlow 1.5 ``extended register". Each extended register * OpenFlow 1.5 ``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.
* *
* Type: be64. * Type: be64.
* Maskable: bitwise. * Maskable: bitwise.
@@ -927,11 +815,7 @@ enum OVS_PACKED_ENUM mf_field_id {
#if FLOW_N_XXREGS == 4 #if FLOW_N_XXREGS == 4
/* "xxreg<N>". /* "xxreg<N>".
* *
* ``extended-extended register". Each of these extended registers * ``extended-extended register".
* 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.
* *
* Type: be128. * Type: be128.
* Maskable: bitwise. * Maskable: bitwise.
@@ -964,8 +848,6 @@ enum OVS_PACKED_ENUM mf_field_id {
* *
* Source address in Ethernet header. * Source address in Ethernet header.
* *
* This field was not maskable before Open vSwitch 1.8.
*
* Type: MAC. * Type: MAC.
* Maskable: bitwise. * Maskable: bitwise.
* Formatting: Ethernet. * Formatting: Ethernet.
@@ -982,10 +864,6 @@ enum OVS_PACKED_ENUM mf_field_id {
* *
* Destination address in Ethernet header. * 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. * Type: MAC.
* Maskable: bitwise. * Maskable: bitwise.
* Formatting: Ethernet. * Formatting: Ethernet.
@@ -1002,11 +880,6 @@ enum OVS_PACKED_ENUM mf_field_id {
* *
* Packet's Ethernet type. * 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 * For a packet with an 802.1Q header, this is the type of the encapsulated
* frame. * 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 * (TCI) field, with the CFI bit forced to 1. For a packet with no 802.1Q
* header, this has value 0. * 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. * Type: be16.
* Maskable: bitwise. * Maskable: bitwise.
* Formatting: hexadecimal. * Formatting: hexadecimal.
@@ -1399,42 +1242,6 @@ enum OVS_PACKED_ENUM mf_field_id {
* *
* IP fragment information. * 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). * Type: u8 (low 2 bits).
* Maskable: bitwise. * Maskable: bitwise.
* Formatting: frag. * Formatting: frag.

View File

@@ -495,10 +495,12 @@ lib/dirs.c: lib/dirs.c.in Makefile
mv lib/dirs.c.tmp lib/dirs.c mv lib/dirs.c.tmp lib/dirs.c
lib/meta-flow.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h 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/meta-flow.lo: lib/meta-flow.inc
lib/nx-match.inc: $(srcdir)/build-aux/extract-ofp-fields include/openvswitch/meta-flow.h 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 $@ $(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 lib/nx-match.lo: lib/nx-match.inc
CLEANFILES += lib/meta-flow.inc lib/nx-match.inc CLEANFILES += lib/meta-flow.inc lib/nx-match.inc
EXTRA_DIST += build-aux/extract-ofp-fields EXTRA_DIST += build-aux/extract-ofp-fields
@@ -535,3 +537,12 @@ lib-install-data-local:
$(MKDIR_P) $(DESTDIR)$(LOGDIR) $(MKDIR_P) $(DESTDIR)$(LOGDIR)
$(MKDIR_P) $(DESTDIR)$(DBDIR) $(MKDIR_P) $(DESTDIR)$(DBDIR)
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/openvswitch $(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

File diff suppressed because it is too large Load Diff

View File

@@ -887,7 +887,7 @@
<p> <p>
Some logical flows can map to the Open vSwitch ``conjunctive match'' 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 <code>conjunction</code> action use an OpenFlow cookie of 0, because
they can correspond to multiple logical flows. The OpenFlow flow for a they can correspond to multiple logical flows. The OpenFlow flow for a
conjunctive match includes a match on <code>conj_id</code>. conjunctive match includes a match on <code>conj_id</code>.

View File

@@ -284,7 +284,7 @@
<li> <li>
Some logical flows can map to the Open vSwitch ``conjunctive match'' 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>ovn-trace</code> cannot display the flows with
<code>conjunction</code> actions that effectively produce the <code>conjunction</code> actions that effectively produce the
<code>conj_id</code> match. <code>conj_id</code> match.

View File

@@ -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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with 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 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): def escape(match):
c = match.group(0) c = match.group(0)
@@ -47,9 +47,13 @@ def text_to_nroff(s, font=r'\fR'):
elif c == "'": elif c == "'":
return r'\(cq' return r'\(cq'
elif c == ".": elif c == ".":
# groff(7) says that . can be escaped by \. but in practice groff if escape_dot:
# still gives an error with \. at the beginning of a line. # groff(7) says that . can be escaped by \. but in practice
return r'\[char46]' # groff still gives an error with \. at the beginning of a
# line.
return r'\[char46]'
else:
return '.'
else: else:
raise error.Error("bad escape") 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 s += node.attributes['group'].nodeValue
elif node.hasAttribute('db'): elif node.hasAttribute('db'):
s += node.attributes['db'].nodeValue s += node.attributes['db'].nodeValue
elif node.hasAttribute('field'):
s += node.attributes['field'].nodeValue
else: else:
raise error.Error("'ref' lacks required attributes: %s" raise error.Error("'ref' lacks required attributes: %s"
% list(node.attributes.keys())) % list(node.attributes.keys()))
return s + font return s + font
elif node.tagName in ['var', 'dfn', 'i']: elif node.tagName in ['var', 'dfn', 'i', 'cite']:
s = r'\fI' s = r'\fI'
for child in node.childNodes: for child in node.childNodes:
s += inline_xml_to_nroff(child, r'\fI', to_upper, newline) s += inline_xml_to_nroff(child, r'\fI', to_upper, newline)
return s + font 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: else:
raise error.Error("element <%s> unknown or invalid here" raise error.Error("element <%s> unknown or invalid here"
% node.tagName) % node.tagName)
@@ -111,11 +127,21 @@ def pre_to_nroff(nodes, para, font):
# from preformatted text. # from preformatted text.
s = para + '\n.nf\n' + font s = para + '\n.nf\n' + font
for node in nodes: 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' s += '\n.fi\n'
return s 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): def fatal(msg):
sys.stderr.write('%s\n' % msg) sys.stderr.write('%s\n' % msg)
sys.exit(1) sys.exit(1)
@@ -262,12 +288,17 @@ fillval = .2
def block_xml_to_nroff(nodes, para='.PP'): def block_xml_to_nroff(nodes, para='.PP'):
HEADER_TAGS = ('h1', 'h2', 'h3') HEADER_TAGS = ('h1', 'h2', 'h3', 'h4')
s = '' s = ''
prev = '' prev = ''
for node in nodes: for node in nodes:
if node.nodeType == node.TEXT_NODE: 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() s = s.lstrip()
elif node.nodeType == node.ELEMENT_NODE: elif node.nodeType == node.ELEMENT_NODE:
if node.tagName in ['ul', 'ol']: if node.tagName in ['ul', 'ol']:
@@ -332,12 +363,15 @@ def block_xml_to_nroff(nodes, para='.PP'):
if s != "": if s != "":
if not s.endswith("\n"): if not s.endswith("\n"):
s += "\n" s += "\n"
nroffTag = {'h1': 'SH', 'h2': 'SS', 'h3': 'ST'}[node.tagName] nroffTag = {'h1': 'SH',
s += '.%s "' % nroffTag 'h2': 'SS',
'h3': 'ST',
'h4': 'SU'}[node.tagName]
to_upper = node.tagName == 'h1'
s += ".%s \"" % nroffTag
for child_node in node.childNodes: for child_node in node.childNodes:
s += inline_xml_to_nroff(child_node, r'\fR', s += inline_xml_to_nroff(child_node, r'\fR', to_upper)
to_upper=(nroffTag == 'SH')) s += "\"\n"
s += '"\n'
elif node.tagName == 'pre': elif node.tagName == 'pre':
fixed = node.getAttribute('fixed') fixed = node.getAttribute('fixed')
if fixed == 'yes': if fixed == 'yes':
@@ -345,6 +379,8 @@ def block_xml_to_nroff(nodes, para='.PP'):
else: else:
font = r'\fB' font = r'\fB'
s += pre_to_nroff(node.childNodes, para, font) s += pre_to_nroff(node.childNodes, para, font)
elif node.tagName == 'tbl':
s += tbl_to_nroff(node.childNodes, para)
elif node.tagName == 'diagram': elif node.tagName == 'diagram':
s += diagram_to_nroff(node.childNodes, para) s += diagram_to_nroff(node.childNodes, para)
else: else:

File diff suppressed because it is too large Load Diff

View File

@@ -2236,9 +2236,9 @@
<li> <li>
<code>gbp</code>: VXLAN-GBP allows to transport the group policy <code>gbp</code>: VXLAN-GBP allows to transport the group policy
context of a packet across the VXLAN tunnel to other network context of a packet across the VXLAN tunnel to other network
peers. See the field description of <code>tun_gbp_id</code> and peers. See the description of <code>tun_gbp_id</code> and
<code>tun_gbp_flags</code> in ovs-ofctl(8) for additional <code>tun_gbp_flags</code> in <code>ovs-fields</code>(7) for
information. additional information.
(<code>https://tools.ietf.org/html/draft-smith-vxlan-group-policy</code>) (<code>https://tools.ietf.org/html/draft-smith-vxlan-group-policy</code>)
</li> </li>
</ul> </ul>