Andrew Evans aevans@nicira.com
Andrew Lambeth wal@nicira.com
Andy Southgate andy.southgate@citrix.com
+Andy Zhou azhou@nicira.com
Anupam Chanda achanda@nicira.com
Arun Sharma arun.sharma@calsoftinc.com
Ben Pfaff blp@nicira.com
Casey Barker crbarker@google.com
Chris Wright chrisw@sous-sol.org
Chuck Short zulcss@ubuntu.com
+Damien Millescamps damien.millescamps@6wind.com
Dan Carpenter dan.carpenter@oracle.com
Dan Wendlandt dan@nicira.com
Daniel Roman droman@nicira.com
Shan Wei davidshan@tencent.com
Shih-Hao Li shli@nicira.com
Simon Horman horms@verge.net.au
+Stephane A. Sezer sas@cd80.net
SUGYO Kazushi sugyo.org@gmail.com
Tadaaki Nagao nagao@stratosphere.co.jp
Tetsuo NAKAGAWA nakagawa@mxc.nes.nec.co.jp
Janis Hamme janis.hamme@student.kit.edu
Jari Sundell sundell.software@gmail.com
Jed Daniels openvswitch@jeddaniels.com
+Jeff Merrick jmerrick@vmware.com
Jeongkeun Lee jklee@hp.com
Jing Ai ai_jing2000@hotmail.com
Joan Cirer joan@ev0.net
common case, so this can be a real form of optimization as well.)
+RETURN VALUES
+
+ For functions that return a success or failure indication, prefer
+one of the following return value conventions:
+
+ * An "int" where 0 indicates success and a positive errno value
+ indicates a reason for failure.
+
+ * A "bool" where true indicates success and false indicates
+ failure.
+
+
MACROS
Don't define an object-like macro if an enum can be used instead.
http://openvswitch.org/mlists/
-
Releases
--------
for OpenFlow 1.2 and 1.3. On these versions of Open vSwitch, the
following command enables OpenFlow 1.0, 1.2, and 1.3 on bridge br0:
- ovs-vsctl set bridge br0 protocols=openflow10,openflow12,openflow13
+ ovs-vsctl set bridge br0 protocols=OpenFlow10,OpenFlow12,OpenFlow13
Support for OpenFlow 1.1 is incomplete enough that it cannot yet be
enabled, even experimentally.
For improved warnings if you installed "sparse" (see
"Prerequisites"), add C=1 to the "make" command line.
-4. Become root by running "su" or another program.
+4. Consider running the testsuite. Refer to "Running the Testsuite"
+ below, for instructions.
-5. Run "make install" to install the executables and manpages into the
+5. Become root by running "su" or another program.
+
+6. Run "make install" to install the executables and manpages into the
running system, by default under /usr/local.
-6. If you built kernel modules, you may load them with "insmod", e.g.:
+7. If you built kernel modules, you may load them with "insmod", e.g.:
% insmod datapath/linux/openvswitch.ko
% make modules_install
-7. Initialize the configuration database using ovsdb-tool, e.g.:
+8. Initialize the configuration database using ovsdb-tool, e.g.:
% mkdir -p /usr/local/etc/openvswitch
% ovsdb-tool create /usr/local/etc/openvswitch/conf.db vswitchd/vswitch.ovsschema
4. Start the Open vSwitch daemons as described under "Building and
Installing Open vSwitch for Linux or FreeBSD" above.
+Running the Testsuite
+=====================
+
+Open vSwitch includes a testsuite. Before you submit patches
+upstream, we advise that you run the tests and ensure that they pass.
+If you add new features to Open vSwitch, then adding tests for those
+features will ensure your features don't break as developers modify
+other areas of Open vSwitch.
+
+You must configure and build Open vSwitch (steps 1 through 3 in
+"Building and Installing Open vSwitch for Linux or FreeBSD" above)
+before you run the testsuite. You do not need to install Open vSwitch
+or to build or load the kernel module to run the testsuite. You do
+not need supervisor privilege to run the testsuite.
+
+To run all the unit tests in Open vSwitch, one at a time:
+ make check
+This takes under 5 minutes on a modern desktop system.
+
+To run all the unit tests in Open vSwitch, up to 8 in parallel:
+ make check TESTSUITEFLAGS=-j8
+This takes under a minute on a modern 4-core desktop system.
+
+To see a list of all the available tests, run:
+ make check TESTSUITEFLAGS=--list
+
+To run only a subset of tests, e.g. test 123 and tests 477 through 484:
+ make check TESTSUITEFLAGS='123 477-484'
+(Tests do not have inter-dependencies, so you may run any subset.)
+
+To run tests matching a keyword, e.g. "ovsdb":
+ make check TESTSUITEFLAGS='-k ovsdb'
+
+To see a complete list of test options:
+ make check TESTSUITEFLAGS=--help
+
+The results of a testing run are reported in tests/testsuite.log.
+Please report test failures as bugs and include the testsuite.log in
+your report.
+
+If you have "valgrind" installed, then you can also run the testsuite
+under valgrind by using "make check-valgrind" in place of "make
+check". All the same options are available via TESTSUITEFLAGS. When
+you do this, the "valgrind" results for test <N> are reported in files
+named tests/testsuite.dir/<N>/valgrind.*. You may find that the
+valgrind results are easier to interpret if you put "-q" in
+~/.valgrindrc, since that reduces the amount of
+
+Sometimes a few tests may fail on some runs but not others. This is
+usually a bug in the testsuite, not a bug in Open vSwitch itself. If
+you find that a test fails intermittently, please report it, since the
+developers may not have noticed.
+
Bug Reporting
-------------
which is usually: "kmod-openvswitch", "kmod-openvswitch-xen", and
"kmod-openvswitch-PAE".
-7b. On RHEL 6, to build the Open vSwitch kernel module, run:
+7b. On RHEL 6, to build the Open vSwitch kernel module, copy
+ rhel/openvswitch-kmod.files into the RPM source directory and run:
rpmbuild -bb rhel/openvswitch-kmod-rhel6.spec
---------------------
- Stable bond mode has been removed.
- The autopath action has been removed.
- - CAPWAP tunneling support removed.
- New support for the data encapsulation format of the LISP tunnel
protocol (RFC 6830). An external control plane or manual flow
setup is required for EID-to-RLOC mapping.
+ - OpenFlow:
+ * The "dec_mpls_ttl" and "set_mpls_ttl" actions from OpenFlow
+ 1.1 and later are now implemented.
+ * New "stack" extension for use in actions, to push and pop from
+ NXM fields.
+ - ovs-dpctl:
+ * New debugging commands "add-flow", "mod-flow", "del-flow".
v1.10.0 - xx xxx xxxx
separately on a per-port basis, so it should no longer be
possible for a large number of new flows arriving on one port to
prevent new flows from being processed on other ports.
- - Many "ovs-vsctl" database commands now accept an --if-exists option.
- Please refer to the ovs-vsctl manpage for details.
+ - ovs-vsctl:
+ * Previously ovs-vsctl would retry connecting to the database forever,
+ causing it to hang if ovsdb-server was not running. Now, ovs-vsctl
+ only tries once by default (use --retry to try forever). This change
+ means that you may want to remove uses of --timeout to avoid hangs
+ in ovs-vsctl calls.
+ * Many "ovs-vsctl" database commands now accept an --if-exists option.
+ Please refer to the ovs-vsctl manpage for details.
- OpenFlow:
- Experimental support for newer versions of OpenFlow. See
the "What versions of OpenFlow does Open vSwitch support?"
value of other-config:dp-desc in the Bridge table.
- It is possible to request the OpenFlow port number with the
"ofport_request" column in the Interface table.
+ - The NXM flow_removed message now reports the OpenFlow table ID
+ from which the flow was removed.
- Tunneling:
- New support for the VXLAN tunnel protocol (see the IETF draft here:
http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-03).
- Inheritance of the Don't Fragment bit in IP tunnels (df_inherit)
is no longer supported.
- Path MTU discovery is no longer supported.
+ - CAPWAP tunneling support removed.
+ - Tunnels with multicast destination ports are no longer supported.
- ovs-dpctl:
- The "dump-flows" and "del-flows" no longer require an argument
if only one datapath exists.
with FreeBSD and the kernel module built into Linux 3.3 and later.
-v1.9.0 - xx xxx xxxx
---------------------
+v1.9.0 - 26 Feb 2013
+------------------------
- Datapath:
- Support for ipv6 set action.
- SKB mark matching and setting.
data in ARP packets.
-v1.8.0 - xx xxx xxxx
+v1.8.0 - 26 Feb 2013
------------------------
+ *** Internal only release ***
- New FAQ. Please send updates and additions!
- Authors of controllers, please read the new section titled "Action
Reproduction" in DESIGN, which describes an Open vSwitch change in
# -*- autoconf -*-
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
OVS_GREP_IFELSE([$KSRC/include/linux/err.h], [ERR_CAST])
+ OVS_GREP_IFELSE([$KSRC/include/linux/etherdevice.h], [eth_hw_addr_random])
+
+ OVS_GREP_IFELSE([$KSRC/include/linux/if_vlan.h], [vlan_set_encap_proto])
+
OVS_GREP_IFELSE([$KSRC/include/linux/in.h], [ipv4_is_multicast])
OVS_GREP_IFELSE([$KSRC/include/linux/netdevice.h], [dev_disable_lro])
[OVS_DEFINE([HAVE_SKB_WARN_LRO])])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [consume_skb])
OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_frag_page])
+ OVS_GREP_IFELSE([$KSRC/include/linux/skbuff.h], [skb_reset_mac_len])
OVS_GREP_IFELSE([$KSRC/include/linux/string.h], [kmemdup], [],
[OVS_GREP_IFELSE([$KSRC/include/linux/slab.h], [kmemdup])])
fi])
dnl Checks for net/if_dl.h.
+dnl
+dnl (We use this as a proxy for checking whether we're building on FreeBSD.)
AC_DEFUN([OVS_CHECK_IF_DL],
[AC_CHECK_HEADER([net/if_dl.h],
[HAVE_IF_DL=yes],
if test "$HAVE_IF_DL" = yes; then
AC_DEFINE([HAVE_IF_DL], [1],
[Define to 1 if net/if_dl.h is available.])
+
+ # On FreeBSD we use libpcap to access network devices.
+ AC_SEARCH_LIBS([pcap_open_live], [pcap])
fi])
dnl Checks for buggy strtok_r.
return name
def print_enum(tag, constants, storage_class):
- print """
+ print ("""
%(storage_class)sconst char *
%(tag)s_to_string(uint16_t value)
{
switch (value) {\
""" % {"tag": tag,
"bufferlen": len(tag) + 32,
- "storage_class": storage_class}
+ "storage_class": storage_class})
for constant in constants:
- print " case %s: return \"%s\";" % (constant, constant)
- print """\
+ print (" case %s: return \"%s\";" % (constant, constant))
+ print ("""\
}
return NULL;
}\
-""" % {"tag": tag}
+""" % {"tag": tag})
def usage():
argv0 = os.path.basename(sys.argv[0])
- print '''\
+ print ('''\
%(argv0)s, for extracting OpenFlow error codes from header files
usage: %(argv0)s FILE [FILE...]
This program is specialized for reading lib/ofp-errors.h. It will not
work on arbitrary header files without extensions.\
-''' % {"argv0": argv0}
+''' % {"argv0": argv0})
sys.exit(0)
def extract_ofp_errors(filenames):
inputFile.close()
- for fn, ln in expected_errors.itervalues():
+ for fn, ln in expected_errors.values():
sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
n_errors += 1
if n_errors:
sys.exit(1)
- print """\
+ print ("""\
/* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
#define OFPERR_N_ERRORS %d
""" % (len(names),
'\n'.join(' "%s",' % name for name in names),
'\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
- for comment in comments))
+ for comment in comments)))
def output_domain(map, name, description, version):
- print """
+ print ("""
static enum ofperr
%s_decode(uint16_t type, uint16_t code)
{
- switch ((type << 16) | code) {""" % name
+ switch ((type << 16) | code) {""" % name)
found = set()
for enum in names:
if enum not in map:
if value in found:
continue
found.add(value)
- print " case (%d << 16) | %d:" % (type_, code)
- print " return OFPERR_%s;" % enum
- print """\
+ print (" case (%d << 16) | %d:" % (type_, code))
+ print (" return OFPERR_%s;" % enum)
+ print ("""\
}
return 0;
-}"""
+}""")
- print """
+ print ("""
static const struct ofperr_domain %s = {
"%s",
%d,
%s_decode,
- {""" % (name, description, version, name)
+ {""" % (name, description, version, name))
for enum in names:
if enum in map:
type_, code = map[enum]
code = -1
else:
type_ = code = -1
- print " { %2d, %3d }, /* %s */" % (type_, code, enum)
- print """\
+ print (" { %2d, %3d }, /* %s */" % (type_, code, enum))
+ print ("""\
},
-};"""
+};""")
output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
-# Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
AC_SEARCH_LIBS([pow], [m])
AC_SEARCH_LIBS([clock_gettime], [rt])
AC_SEARCH_LIBS([timer_create], [rt])
-AC_SEARCH_LIBS([pcap_open_live], [pcap])
OVS_CHECK_ESX
OVS_CHECK_COVERAGE
AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec, struct stat.st_mtimensec],
[], [], [[#include <sys/stat.h>]])
AC_CHECK_FUNCS([mlockall strnlen strsignal getloadavg statvfs setmntent])
-AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h execinfo.h])
+AC_CHECK_HEADERS([mntent.h sys/statvfs.h linux/types.h linux/if_ether.h execinfo.h])
OVS_CHECK_PKIDIR
OVS_CHECK_RUNDIR
#include "vport.h"
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct nlattr *attr, int len,
- struct ovs_key_ipv4_tunnel *tun_key, bool keep_skb);
+ const struct nlattr *attr, int len, bool keep_skb);
static int make_writable(struct sk_buff *skb, int write_len)
{
}
static int sample(struct datapath *dp, struct sk_buff *skb,
- const struct nlattr *attr,
- struct ovs_key_ipv4_tunnel *tun_key)
+ const struct nlattr *attr)
{
const struct nlattr *acts_list = NULL;
const struct nlattr *a;
}
return do_execute_actions(dp, skb, nla_data(acts_list),
- nla_len(acts_list), tun_key, true);
+ nla_len(acts_list), true);
}
static int execute_set_action(struct sk_buff *skb,
- const struct nlattr *nested_attr,
- struct ovs_key_ipv4_tunnel *tun_key)
+ const struct nlattr *nested_attr)
{
int err = 0;
skb_set_mark(skb, nla_get_u32(nested_attr));
break;
- case OVS_KEY_ATTR_TUN_ID:
- /* If we're only using the TUN_ID action, store the value in a
- * temporary instance of struct ovs_key_ipv4_tunnel on the stack.
- * If both IPV4_TUNNEL and TUN_ID are being used together we
- * can't write into the IPV4_TUNNEL action, so make a copy and
- * write into that version.
- */
- if (!OVS_CB(skb)->tun_key)
- memset(tun_key, 0, sizeof(*tun_key));
- else if (OVS_CB(skb)->tun_key != tun_key)
- memcpy(tun_key, OVS_CB(skb)->tun_key, sizeof(*tun_key));
- OVS_CB(skb)->tun_key = tun_key;
-
- OVS_CB(skb)->tun_key->tun_id = nla_get_be64(nested_attr);
- break;
-
case OVS_KEY_ATTR_IPV4_TUNNEL:
OVS_CB(skb)->tun_key = nla_data(nested_attr);
break;
/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
- const struct nlattr *attr, int len,
- struct ovs_key_ipv4_tunnel *tun_key, bool keep_skb)
+ const struct nlattr *attr, int len, bool keep_skb)
{
/* Every output action needs a separate clone of 'skb', but the common
* case is just a single output action, so that doing a clone and
break;
case OVS_ACTION_ATTR_SET:
- err = execute_set_action(skb, nla_data(a), tun_key);
+ err = execute_set_action(skb, nla_data(a));
break;
case OVS_ACTION_ATTR_SAMPLE:
- err = sample(dp, skb, a, tun_key);
+ err = sample(dp, skb, a);
break;
}
/* We limit the number of times that we pass into execute_actions()
* to avoid blowing out the stack in the event that we have a loop. */
-#define MAX_LOOPS 5
+#define MAX_LOOPS 4
struct loop_counter {
u8 count; /* Count. */
struct sw_flow_actions *acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
struct loop_counter *loop;
int error;
- struct ovs_key_ipv4_tunnel tun_key;
/* Check whether we've looped too much. */
loop = &__get_cpu_var(loop_counters);
OVS_CB(skb)->tun_key = NULL;
error = do_execute_actions(dp, skb, acts->actions,
- acts->actions_len, &tun_key, false);
+ acts->actions_len, false);
/* Check whether sub-actions looped too much. */
if (unlikely(loop->looping))
int ovs_net_id __read_mostly;
+static void ovs_notify(struct sk_buff *skb, struct genl_info *info,
+ struct genl_multicast_group *grp)
+{
+ genl_notify(skb, genl_info_net(info), info->snd_portid,
+ grp->id, info->nlhdr, GFP_KERNEL);
+}
+
/**
* DOC: Locking:
*
struct vport *ovs_lookup_vport(const struct datapath *dp, u16 port_no)
{
struct vport *vport;
- struct hlist_node *n;
struct hlist_head *head;
head = vport_hash_bucket(dp, port_no);
- hlist_for_each_entry_rcu(vport, n, head, dp_hash_node) {
+ hlist_for_each_entry_rcu(vport, head, dp_hash_node) {
if (vport->port_no == port_no)
return vport;
}
struct datapath *dp = p->dp;
struct sw_flow *flow;
struct dp_stats_percpu *stats;
+ struct sw_flow_key key;
u64 *stats_counter;
int error;
+ int key_len;
stats = this_cpu_ptr(dp->stats_percpu);
- if (!OVS_CB(skb)->flow) {
- struct sw_flow_key key;
- int key_len;
-
- /* Extract flow from 'skb' into 'key'. */
- error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
- if (unlikely(error)) {
- kfree_skb(skb);
- return;
- }
-
- /* Look up flow. */
- flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table),
- &key, key_len);
- if (unlikely(!flow)) {
- struct dp_upcall_info upcall;
-
- upcall.cmd = OVS_PACKET_CMD_MISS;
- upcall.key = &key;
- upcall.userdata = NULL;
- upcall.portid = p->upcall_portid;
- ovs_dp_upcall(dp, skb, &upcall);
- consume_skb(skb);
- stats_counter = &stats->n_missed;
- goto out;
- }
+ /* Extract flow from 'skb' into 'key'. */
+ error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
+ if (unlikely(error)) {
+ kfree_skb(skb);
+ return;
+ }
- OVS_CB(skb)->flow = flow;
+ /* Look up flow. */
+ flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table), &key, key_len);
+ if (unlikely(!flow)) {
+ struct dp_upcall_info upcall;
+
+ upcall.cmd = OVS_PACKET_CMD_MISS;
+ upcall.key = &key;
+ upcall.userdata = NULL;
+ upcall.portid = p->upcall_portid;
+ ovs_dp_upcall(dp, skb, &upcall);
+ consume_skb(skb);
+ stats_counter = &stats->n_missed;
+ goto out;
}
+ OVS_CB(skb)->flow = flow;
+
stats_counter = &stats->n_hit;
ovs_flow_used(OVS_CB(skb)->flow, skb);
ovs_execute_actions(dp, skb);
return err;
}
+static size_t key_attr_size(void)
+{
+ return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
+ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
+ + nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
+ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_8021Q */
+ + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
+ + nla_total_size(28); /* OVS_KEY_ATTR_ND */
+}
+
+static size_t upcall_msg_size(const struct sk_buff *skb,
+ const struct nlattr *userdata)
+{
+ size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
+ + nla_total_size(skb->len) /* OVS_PACKET_ATTR_PACKET */
+ + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
+
+ /* OVS_PACKET_ATTR_USERDATA */
+ if (userdata)
+ size += NLA_ALIGN(userdata->nla_len);
+
+ return size;
+}
+
static int queue_userspace_packet(struct net *net, int dp_ifindex,
struct sk_buff *skb,
const struct dp_upcall_info *upcall_info)
struct sk_buff *nskb = NULL;
struct sk_buff *user_skb; /* to be queued to userspace */
struct nlattr *nla;
- unsigned int len;
int err;
if (vlan_tx_tag_present(skb)) {
goto out;
}
- len = sizeof(struct ovs_header);
- len += nla_total_size(skb->len);
- len += nla_total_size(FLOW_BUFSIZE);
- if (upcall_info->userdata)
- len += NLA_ALIGN(upcall_info->userdata->nla_len);
-
- user_skb = genlmsg_new(len, GFP_ATOMIC);
+ user_skb = genlmsg_new(upcall_msg_size(skb, upcall_info->userdata), GFP_ATOMIC);
if (!user_skb) {
err = -ENOMEM;
goto out;
int next_offset = offsetof(struct sw_flow_actions, actions) +
(*sfa)->actions_len;
- if (req_size <= ((*sfa)->buf_size - next_offset))
+ if (req_size <= (ksize(*sfa) - next_offset))
goto out;
- new_acts_size = (*sfa)->buf_size * 2;
+ new_acts_size = ksize(*sfa) * 2;
if (new_acts_size > MAX_ACTIONS_BUFSIZE) {
if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size)
memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len);
acts->actions_len = (*sfa)->actions_len;
- ovs_flow_actions_free(*sfa);
+ kfree(*sfa);
*sfa = acts;
out:
int err;
case OVS_KEY_ATTR_PRIORITY:
- case OVS_KEY_ATTR_TUN_ID:
case OVS_KEY_ATTR_ETHERNET:
break;
err = -EINVAL;
if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] ||
- !a[OVS_PACKET_ATTR_ACTIONS] ||
- nla_len(a[OVS_PACKET_ATTR_PACKET]) < ETH_HLEN)
+ !a[OVS_PACKET_ATTR_ACTIONS])
goto err;
len = nla_len(a[OVS_PACKET_ATTR_PACKET]);
goto err;
skb_reserve(packet, NET_IP_ALIGN);
- memcpy(__skb_put(packet, len), nla_data(a[OVS_PACKET_ATTR_PACKET]), len);
+ nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len);
skb_reset_mac_header(packet);
eth = eth_hdr(packet);
/* Normally, setting the skb 'protocol' field would be handled by a
* call to eth_type_trans(), but it assumes there's a sending
* device, which we may not have. */
- if (ntohs(eth->h_proto) >= 1536)
+ if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN)
packet->protocol = eth->h_proto;
else
packet->protocol = htons(ETH_P_802_2);
}
static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
- [OVS_PACKET_ATTR_PACKET] = { .type = NLA_UNSPEC },
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
+ [OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN },
+#else
+ [OVS_PACKET_ATTR_PACKET] = { .minlen = ETH_HLEN },
+#endif
[OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
[OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
};
return 0;
}
+static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
+{
+ return NLMSG_ALIGN(sizeof(struct ovs_header))
+ + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
+ + nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
+ + nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
+ + nla_total_size(8) /* OVS_FLOW_ATTR_USED */
+ + nla_total_size(acts->actions_len); /* OVS_FLOW_ATTR_ACTIONS */
+}
+
/* Called with genl_lock. */
static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
struct sk_buff *skb, u32 portid,
static struct sk_buff *ovs_flow_cmd_alloc_info(struct sw_flow *flow)
{
const struct sw_flow_actions *sf_acts;
- int len;
sf_acts = rcu_dereference_protected(flow->sf_acts,
lockdep_genl_is_held());
- /* OVS_FLOW_ATTR_KEY */
- len = nla_total_size(FLOW_BUFSIZE);
- /* OVS_FLOW_ATTR_ACTIONS */
- len += nla_total_size(sf_acts->actions_len);
- /* OVS_FLOW_ATTR_STATS */
- len += nla_total_size(sizeof(struct ovs_flow_stats));
- /* OVS_FLOW_ATTR_TCP_FLAGS */
- len += nla_total_size(1);
- /* OVS_FLOW_ATTR_USED */
- len += nla_total_size(8);
-
- len += NLMSG_ALIGN(sizeof(struct ovs_header));
-
- return genlmsg_new(len, GFP_KERNEL);
+ return genlmsg_new(ovs_flow_cmd_msg_size(sf_acts), GFP_KERNEL);
}
static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow,
}
if (!IS_ERR(reply))
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_flow_multicast_group.id, info->nlhdr,
- GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
else
netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
return 0;
err_kfree:
- ovs_flow_actions_free(acts);
+ kfree(acts);
error:
return error;
}
ovs_flow_deferred_free(flow);
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_flow_multicast_group.id, info->nlhdr, GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
return 0;
}
.name = OVS_DATAPATH_MCGROUP
};
+static size_t ovs_dp_cmd_msg_size(void)
+{
+ size_t msgsize = NLMSG_ALIGN(sizeof(struct ovs_header));
+
+ msgsize += nla_total_size(IFNAMSIZ);
+ msgsize += nla_total_size(sizeof(struct ovs_dp_stats));
+
+ return msgsize;
+}
+
static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
u32 portid, u32 seq, u32 flags, u8 cmd)
{
struct sk_buff *skb;
int retval;
- skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ skb = genlmsg_new(ovs_dp_cmd_msg_size(), GFP_KERNEL);
if (!skb)
return ERR_PTR(-ENOMEM);
rtnl_unlock();
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_datapath_multicast_group.id, info->nlhdr,
- GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0;
err_destroy_local_port:
for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++) {
struct vport *vport;
- struct hlist_node *node, *n;
+ struct hlist_node *n;
- hlist_for_each_entry_safe(vport, node, n, &dp->ports[i], dp_hash_node)
+ hlist_for_each_entry_safe(vport, n, &dp->ports[i], dp_hash_node)
if (vport->port_no != OVSP_LOCAL)
ovs_dp_detach_port(vport);
}
__dp_destroy(dp);
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_datapath_multicast_group.id, info->nlhdr,
- GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0;
}
return 0;
}
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_datapath_multicast_group.id, info->nlhdr,
- GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_datapath_multicast_group);
return 0;
}
return ERR_PTR(-ENOMEM);
retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd);
- if (retval < 0) {
- kfree_skb(skb);
- return ERR_PTR(retval);
- }
+ BUG_ON(retval < 0);
+
return skb;
}
ovs_dp_detach_port(vport);
goto exit_unlock;
}
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+
+ ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
exit_unlock:
rtnl_unlock();
nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type)
err = -EINVAL;
+ reply = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!reply) {
+ err = -ENOMEM;
+ goto exit_unlock;
+ }
+
if (!err && a[OVS_VPORT_ATTR_OPTIONS])
err = ovs_vport_set_options(vport, a[OVS_VPORT_ATTR_OPTIONS]);
if (err)
- goto exit_unlock;
+ goto exit_free;
if (a[OVS_VPORT_ATTR_STATS])
ovs_vport_set_stats(vport, nla_data(a[OVS_VPORT_ATTR_STATS]));
if (a[OVS_VPORT_ATTR_UPCALL_PID])
vport->upcall_portid = nla_get_u32(a[OVS_VPORT_ATTR_UPCALL_PID]);
- reply = ovs_vport_cmd_build_info(vport, info->snd_portid,
- info->snd_seq, OVS_VPORT_CMD_NEW);
- if (IS_ERR(reply)) {
- netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
- ovs_dp_vport_multicast_group.id, PTR_ERR(reply));
- goto exit_unlock;
- }
+ err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
+ info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+ BUG_ON(err < 0);
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
+ rtnl_unlock();
+ return 0;
+
+exit_free:
+ kfree_skb(reply);
exit_unlock:
rtnl_unlock();
exit:
err = 0;
ovs_dp_detach_port(vport);
- genl_notify(reply, genl_info_net(info), info->snd_portid,
- ovs_dp_vport_multicast_group.id, info->nlhdr, GFP_KERNEL);
+ ovs_notify(reply, info, &ovs_dp_vport_multicast_group);
exit_unlock:
rtnl_unlock();
rcu_read_lock();
for (i = bucket; i < DP_VPORT_HASH_BUCKETS; i++) {
struct vport *vport;
- struct hlist_node *n;
j = 0;
- hlist_for_each_entry_rcu(vport, n, &dp->ports[i], dp_hash_node) {
+ hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) {
if (j >= skip &&
ovs_vport_cmd_fill_info(vport, skb,
NETLINK_CB(cb->skb).portid,
if (err)
goto error_genl_exec;
- err = ovs_tnl_init();
- if (err)
- goto error_wq;
-
err = ovs_flow_init();
if (err)
- goto error_tnl_exit;
+ goto error_wq;
err = ovs_vport_init();
if (err)
ovs_vport_exit();
error_flow_exit:
ovs_flow_exit();
-error_tnl_exit:
- ovs_tnl_exit();
error_wq:
ovs_workqueues_exit();
error_genl_exec:
rcu_barrier();
ovs_vport_exit();
ovs_flow_exit();
- ovs_tnl_exit();
ovs_workqueues_exit();
genl_exec_exit();
}
*/
struct ovs_net {
struct list_head dps;
+ struct vport_net vport_net;
};
extern int ovs_net_id;
if (size > MAX_ACTIONS_BUFSIZE)
return ERR_PTR(-EINVAL);
- size += sizeof(*sfa);
- if (size <= MAX_ACTIONS_BUFSIZE_KMALLOC)
- sfa = kmalloc(size, GFP_KERNEL);
- else
- sfa = vmalloc(size);
-
+ sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
if (!sfa)
return ERR_PTR(-ENOMEM);
sfa->actions_len = 0;
- sfa->buf_size = size;
-
return sfa;
}
-void ovs_flow_actions_free(struct sw_flow_actions *sfa)
-{
- if (sfa->buf_size <= MAX_ACTIONS_BUFSIZE_KMALLOC)
- kfree(sfa);
- else
- vfree(sfa);
-}
-
struct sw_flow *ovs_flow_alloc(void)
{
struct sw_flow *flow;
for (i = 0; i < table->n_buckets; i++) {
struct sw_flow *flow;
struct hlist_head *head = flex_array_get(table->buckets, i);
- struct hlist_node *node, *n;
+ struct hlist_node *n;
int ver = table->node_ver;
- hlist_for_each_entry_safe(flow, node, n, head, hash_node[ver]) {
+ hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) {
hlist_del_rcu(&flow->hash_node[ver]);
ovs_flow_free(flow);
}
{
struct sw_flow *flow;
struct hlist_head *head;
- struct hlist_node *n;
int ver;
int i;
while (*bucket < table->n_buckets) {
i = 0;
head = flex_array_get(table->buckets, *bucket);
- hlist_for_each_entry_rcu(flow, n, head, hash_node[ver]) {
+ hlist_for_each_entry_rcu(flow, head, hash_node[ver]) {
if (i < *last) {
i++;
continue;
for (i = 0; i < old->n_buckets; i++) {
struct sw_flow *flow;
struct hlist_head *head;
- struct hlist_node *n;
head = flex_array_get(old->buckets, i);
- hlist_for_each_entry(flow, n, head, hash_node[old_ver])
+ hlist_for_each_entry(flow, head, hash_node[old_ver])
__flow_tbl_insert(new, flow);
}
old->keep_flows = true;
{
struct sw_flow_actions *sf_acts = container_of(rcu,
struct sw_flow_actions, rcu);
- ovs_flow_actions_free(sf_acts);
+ kfree(sf_acts);
}
/* Schedules 'sf_acts' to be freed after the next RCU grace period.
proto = *(__be16 *) skb->data;
__skb_pull(skb, sizeof(__be16));
- if (ntohs(proto) >= 1536)
+ if (ntohs(proto) >= ETH_P_802_3_MIN)
return proto;
if (skb->len < sizeof(struct llc_snap_hdr))
__skb_pull(skb, sizeof(struct llc_snap_hdr));
- if (ntohs(llc->ethertype) >= 1536)
+ if (ntohs(llc->ethertype) >= ETH_P_802_3_MIN)
return llc->ethertype;
return htons(ETH_P_802_2);
struct sw_flow_key *key, int key_len)
{
struct sw_flow *flow;
- struct hlist_node *n;
struct hlist_head *head;
u8 *_key;
int key_start;
_key = (u8 *) key + key_start;
head = find_bucket(table, hash);
- hlist_for_each_entry_rcu(flow, n, head, hash_node[table->node_ver]) {
+ hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) {
if (flow->hash == hash &&
!memcmp((u8 *)&flow->key + key_start, _key, key_len - key_start)) {
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow)
{
+ BUG_ON(table->count == 0);
hlist_del_rcu(&flow->hash_node[table->node_ver]);
table->count--;
- BUG_ON(table->count < 0);
}
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
[OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp),
[OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd),
[OVS_KEY_ATTR_TUNNEL] = -1,
-
- /* Not upstream. */
- [OVS_KEY_ATTR_TUN_ID] = sizeof(__be64),
};
static int ipv4_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_len,
attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
}
- if (attrs & (1ULL << OVS_KEY_ATTR_TUN_ID) &&
- attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
- __be64 tun_id;
-
- err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
- if (err)
- return err;
-
- if (!(swkey->tun_key.tun_flags & OVS_TNL_F_KEY))
- return -EINVAL;
-
- tun_id = nla_get_be64(a[OVS_KEY_ATTR_TUN_ID]);
- if (tun_id != swkey->tun_key.tun_id)
- return -EINVAL;
-
- attrs &= ~(1ULL << OVS_KEY_ATTR_TUN_ID);
- attrs &= ~(1ULL << OVS_KEY_ATTR_TUNNEL);
- } else if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
-
+ if (attrs & (1ULL << OVS_KEY_ATTR_TUNNEL)) {
err = ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], &swkey->tun_key);
if (err)
return err;
if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
swkey->eth.type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
- if (ntohs(swkey->eth.type) < 1536)
+ if (ntohs(swkey->eth.type) < ETH_P_802_3_MIN)
return -EINVAL;
attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
} else {
struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key;
const struct nlattr *nla;
int rem;
- __be64 tun_id = 0;
flow->key.phy.in_port = DP_MAX_PORTS;
flow->key.phy.priority = 0;
flow->key.phy.priority = nla_get_u32(nla);
break;
- case OVS_KEY_ATTR_TUN_ID:
- tun_id = nla_get_be64(nla);
-
- if (tun_key->ipv4_dst) {
- if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
- return -EINVAL;
- if (tun_key->tun_id != tun_id)
- return -EINVAL;
- break;
- }
- tun_key->tun_id = tun_id;
- tun_key->tun_flags |= OVS_TNL_F_KEY;
-
- break;
-
case OVS_KEY_ATTR_TUNNEL:
- if (tun_key->tun_flags & OVS_TNL_F_KEY) {
- tun_id = tun_key->tun_id;
- err = ipv4_tun_from_nlattr(nla, tun_key);
- if (err)
- return err;
-
- if (!(tun_key->tun_flags & OVS_TNL_F_KEY))
- return -EINVAL;
-
- if (tun_key->tun_id != tun_id)
- return -EINVAL;
- } else {
- err = ipv4_tun_from_nlattr(nla, tun_key);
- if (err)
- return err;
- }
+ err = ipv4_tun_from_nlattr(nla, tun_key);
+ if (err)
+ return err;
break;
case OVS_KEY_ATTR_IN_PORT:
ipv4_tun_to_nlattr(skb, &swkey->tun_key))
goto nla_put_failure;
- if ((swkey->tun_key.tun_flags & OVS_TNL_F_KEY) &&
- nla_put_be64(skb, OVS_KEY_ATTR_TUN_ID, swkey->tun_key.tun_id))
- goto nla_put_failure;
-
if (swkey->phy.in_port != DP_MAX_PORTS &&
nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port))
goto nla_put_failure;
struct sw_flow_actions {
struct rcu_head rcu;
u32 actions_len;
- int buf_size;
struct nlattr actions[];
};
void ovs_flow_free(struct sw_flow *);
struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len);
-void ovs_flow_actions_free(struct sw_flow_actions *sfa);
void ovs_flow_deferred_free_acts(struct sw_flow_actions *);
int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *,
void ovs_flow_used(struct sw_flow *, struct sk_buff *);
u64 ovs_flow_used_time(unsigned long flow_jiffies);
-/* Upper bound on the length of a nlattr-formatted flow key. The longest
- * nlattr-formatted flow key would be:
- *
- * struct pad nl hdr total
- * ------ --- ------ -----
- * OVS_KEY_ATTR_PRIORITY 4 -- 4 8
- * OVS_KEY_ATTR_TUN_ID 8 -- 4 12
- * OVS_KEY_ATTR_TUNNEL 0 -- 4 4
- * - OVS_TUNNEL_KEY_ATTR_ID 8 -- 4 12
- * - OVS_TUNNEL_KEY_ATTR_IPV4_SRC 4 -- 4 8
- * - OVS_TUNNEL_KEY_ATTR_IPV4_DST 4 -- 4 8
- * - OVS_TUNNEL_KEY_ATTR_TOS 1 3 4 8
- * - OVS_TUNNEL_KEY_ATTR_TTL 1 3 4 8
- * - OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT 0 -- 4 4
- * - OVS_TUNNEL_KEY_ATTR_CSUM 0 -- 4 4
- * OVS_KEY_ATTR_IN_PORT 4 -- 4 8
- * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8
- * OVS_KEY_ATTR_ETHERNET 12 -- 4 16
- * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype)
- * OVS_KEY_ATTR_8021Q 4 -- 4 8
- * OVS_KEY_ATTR_ENCAP 0 -- 4 4 (VLAN encapsulation)
- * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (inner VLAN ethertype)
- * OVS_KEY_ATTR_IPV6 40 -- 4 44
- * OVS_KEY_ATTR_ICMPV6 2 2 4 8
- * OVS_KEY_ATTR_ND 28 -- 4 32
- * ----------------------------------------------------------
- * total 220
- */
-#define FLOW_BUFSIZE 220
-
int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
const struct nlattr *);
int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, int key_len,
const struct nlattr *attr);
-#define MAX_ACTIONS_BUFSIZE (32 * 1024)
-#define MAX_ACTIONS_BUFSIZE_KMALLOC PAGE_SIZE
+#define MAX_ACTIONS_BUFSIZE (32 * 1024)
#define TBL_MIN_BUCKETS 1024
struct flow_table {
linux/compat/workqueue.c
openvswitch_headers += \
linux/compat/include/asm/percpu.h \
- linux/compat/include/linux/bug.h \
linux/compat/include/linux/compiler.h \
linux/compat/include/linux/compiler-gcc.h \
linux/compat/include/linux/cpumask.h \
linux/compat/include/linux/jiffies.h \
linux/compat/include/linux/kernel.h \
linux/compat/include/linux/kobject.h \
+ linux/compat/include/linux/list.h \
linux/compat/include/linux/lockdep.h \
linux/compat/include/linux/log2.h \
linux/compat/include/linux/mutex.h \
+++ /dev/null
-#ifndef __BUG_H_WRAPPER
-#define __BUG_H_WRAPPER 1
-
-#include_next <linux/bug.h>
-
-#ifndef BUILD_BUG_ON_NOT_POWER_OF_2
-/* Force a compilation error if a constant expression is not a power of 2 */
-#define BUILD_BUG_ON_NOT_POWER_OF_2(n) \
- BUILD_BUG_ON((n) == 0 || (((n) & ((n) - 1)) != 0))
-#endif
-
-#endif
#include <linux/version.h>
#include_next <linux/etherdevice.h>
+#ifndef HAVE_ETH_HW_ADDR_RANDOM
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
static inline void eth_hw_addr_random(struct net_device *dev)
{
dev_hw_addr_random(dev, dev->dev_addr);
}
#endif
+#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
#define eth_mac_addr rpl_eth_mac_addr
#endif /* linux kernel < 2.6.30 */
+#ifndef ETH_P_802_3_MIN
+#define ETH_P_802_3_MIN 0x0600
+#endif
+
#endif
#define VLAN_TAG_PRESENT VLAN_CFI_MASK
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
+#ifndef HAVE_VLAN_SET_ENCAP_PROTO
static inline void vlan_set_encap_proto(struct sk_buff *skb, struct vlan_hdr *vhdr)
{
__be16 proto;
#endif
#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
-/* BUILD_BUG_ON_NOT_POWER_OF_2 definition */
-#include <linux/bug.h>
-#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
#undef pr_emerg
--- /dev/null
+#ifndef __LINUX_LIST_WRAPPER_H
+#define __LINUX_LIST_WRAPPER_H 1
+
+#include_next <linux/list.h>
+
+#ifndef hlist_entry_safe
+#define hlist_entry_safe(ptr, type, member) \
+ (ptr) ? hlist_entry(ptr, type, member) : NULL
+
+#undef hlist_for_each_entry
+#define hlist_for_each_entry(pos, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
+ pos; \
+ pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
+
+#undef hlist_for_each_entry_safe
+#define hlist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
+ pos && ({ n = pos->member.next; 1; }); \
+ pos = hlist_entry_safe(n, typeof(*pos), member))
+
+#endif
+
+#endif
#else
/* Prior to 2.6.26, the contents of rculist.h were part of list.h. */
#include <linux/list.h>
+#include <linux/rcupdate.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
}
#endif
+#ifndef hlist_first_rcu
+#define hlist_first_rcu(head) (*((struct hlist_node __rcu **)(&(head)->first)))
+#define hlist_next_rcu(node) (*((struct hlist_node __rcu **)(&(node)->next)))
+#define hlist_pprev_rcu(node) (*((struct hlist_node __rcu **)((node)->pprev)))
+#endif
+
+#undef hlist_for_each_entry_rcu
+#define hlist_for_each_entry_rcu(pos, head, member) \
+ for (pos = hlist_entry_safe (rcu_dereference_raw(hlist_first_rcu(head)),\
+ typeof(*(pos)), member); \
+ pos; \
+ pos = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(\
+ &(pos)->member)), typeof(*(pos)), member))
+
#endif
#define rcu_dereference_protected(p, c) (p)
#endif
+#ifndef rcu_dereference_raw
+#define rcu_dereference_raw(p) rcu_dereference_check(p, 1)
+#endif
+
#ifndef HAVE_RCU_READ_LOCK_HELD
static inline int rcu_read_lock_held(void)
{
}
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,40)
+#ifndef HAVE_SKB_RESET_MAC_LEN
static inline void skb_reset_mac_len(struct sk_buff *skb)
{
skb->mac_len = skb->network_header - skb->mac_header;
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-#include <linux/if_vlan.h>
-#include <linux/igmp.h>
#include <linux/in.h>
#include <linux/in_route.h>
#include <linux/inetdevice.h>
#include <linux/version.h>
#include <linux/workqueue.h>
#include <linux/rculist.h>
-
-#include <net/dsfield.h>
-#include <net/dst.h>
-#include <net/icmp.h>
-#include <net/inet_ecn.h>
-#include <net/ip.h>
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-#include <net/ipv6.h>
-#endif
#include <net/route.h>
#include <net/xfrm.h>
#include "tunnel.h"
#include "vlan.h"
#include "vport.h"
-#include "vport-internal_dev.h"
-
-#define PORT_TABLE_SIZE 1024
-
-static struct hlist_head *port_table __read_mostly;
-
-/*
- * These are just used as an optimization: they don't require any kind of
- * synchronization because we could have just as easily read the value before
- * the port change happened.
- */
-static unsigned int key_local_remote_ports __read_mostly;
-static unsigned int key_remote_ports __read_mostly;
-static unsigned int key_multicast_ports __read_mostly;
-static unsigned int local_remote_ports __read_mostly;
-static unsigned int remote_ports __read_mostly;
-static unsigned int null_ports __read_mostly;
-static unsigned int multicast_ports __read_mostly;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
#define rt_dst(rt) (rt->dst)
#define rt_dst(rt) (rt->u.dst)
#endif
-static struct vport *tnl_vport_to_vport(const struct tnl_vport *tnl_vport)
-{
- return vport_from_priv(tnl_vport);
-}
-
-static void free_config_rcu(struct rcu_head *rcu)
-{
- struct tnl_mutable_config *c = container_of(rcu, struct tnl_mutable_config, rcu);
- kfree(c);
-}
-
-/* Frees the portion of 'mutable' that requires RTNL and thus can't happen
- * within an RCU callback. Fortunately this part doesn't require waiting for
- * an RCU grace period.
- */
-static void free_mutable_rtnl(struct tnl_mutable_config *mutable)
-{
- ASSERT_RTNL();
- if (ipv4_is_multicast(mutable->key.daddr) && mutable->mlink) {
- struct in_device *in_dev;
- in_dev = inetdev_by_index(port_key_get_net(&mutable->key), mutable->mlink);
- if (in_dev)
- ip_mc_dec_group(in_dev, mutable->key.daddr);
- }
-}
-
-static void assign_config_rcu(struct vport *vport,
- struct tnl_mutable_config *new_config)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *old_config;
-
- old_config = rtnl_dereference(tnl_vport->mutable);
- rcu_assign_pointer(tnl_vport->mutable, new_config);
-
- free_mutable_rtnl(old_config);
- call_rcu(&old_config->rcu, free_config_rcu);
-}
-
-static unsigned int *find_port_pool(const struct tnl_mutable_config *mutable)
-{
- bool is_multicast = ipv4_is_multicast(mutable->key.daddr);
-
- if (mutable->flags & TNL_F_IN_KEY_MATCH) {
- if (mutable->key.saddr)
- return &local_remote_ports;
- else if (is_multicast)
- return &multicast_ports;
- else
- return &remote_ports;
- } else {
- if (mutable->key.saddr)
- return &key_local_remote_ports;
- else if (is_multicast)
- return &key_multicast_ports;
- else if (mutable->key.daddr)
- return &key_remote_ports;
- else
- return &null_ports;
- }
-}
-
-static u32 port_hash(const struct port_lookup_key *key)
-{
- return jhash2((u32 *)key, (PORT_KEY_LEN / sizeof(u32)), 0);
-}
-
-static struct hlist_head *find_bucket(u32 hash)
-{
- return &port_table[(hash & (PORT_TABLE_SIZE - 1))];
-}
-
-static void port_table_add_port(struct vport *vport)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- const struct tnl_mutable_config *mutable;
- u32 hash;
-
- mutable = rtnl_dereference(tnl_vport->mutable);
- hash = port_hash(&mutable->key);
- hlist_add_head_rcu(&tnl_vport->hash_node, find_bucket(hash));
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))++;
-}
-
-static void port_table_move_port(struct vport *vport,
- struct tnl_mutable_config *new_mutable)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- u32 hash;
-
- hash = port_hash(&new_mutable->key);
- hlist_del_init_rcu(&tnl_vport->hash_node);
- hlist_add_head_rcu(&tnl_vport->hash_node, find_bucket(hash));
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))--;
- assign_config_rcu(vport, new_mutable);
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))++;
-}
-
-static void port_table_remove_port(struct vport *vport)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
-
- hlist_del_init_rcu(&tnl_vport->hash_node);
-
- (*find_port_pool(rtnl_dereference(tnl_vport->mutable)))--;
-}
-
-static struct vport *port_table_lookup(struct port_lookup_key *key,
- const struct tnl_mutable_config **pmutable)
-{
- struct hlist_node *n;
- struct hlist_head *bucket;
- u32 hash = port_hash(key);
- struct tnl_vport *tnl_vport;
-
- bucket = find_bucket(hash);
-
- hlist_for_each_entry_rcu(tnl_vport, n, bucket, hash_node) {
- struct tnl_mutable_config *mutable;
-
- mutable = rcu_dereference_rtnl(tnl_vport->mutable);
- if (!memcmp(&mutable->key, key, PORT_KEY_LEN)) {
- *pmutable = mutable;
- return tnl_vport_to_vport(tnl_vport);
- }
- }
-
- return NULL;
-}
-
-struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
- __be64 key, int tunnel_type,
- const struct tnl_mutable_config **mutable)
-{
- struct port_lookup_key lookup;
- struct vport *vport;
- bool is_multicast = ipv4_is_multicast(saddr);
-
- port_key_set_net(&lookup, net);
- lookup.saddr = saddr;
- lookup.daddr = daddr;
-
- /* First try for exact match on in_key. */
- lookup.in_key = key;
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_EXACT;
- if (!is_multicast && key_local_remote_ports) {
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (key_remote_ports) {
- lookup.saddr = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
-
- lookup.saddr = saddr;
- }
-
- /* Then try matches that wildcard in_key. */
- lookup.in_key = 0;
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_MATCH;
- if (!is_multicast && local_remote_ports) {
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (remote_ports) {
- lookup.saddr = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
-
- if (is_multicast) {
- lookup.saddr = 0;
- lookup.daddr = saddr;
- if (key_multicast_ports) {
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_EXACT;
- lookup.in_key = key;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- if (multicast_ports) {
- lookup.tunnel_type = tunnel_type | TNL_T_KEY_MATCH;
- lookup.in_key = 0;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- }
-
- if (null_ports) {
- lookup.daddr = 0;
- lookup.saddr = 0;
- lookup.in_key = 0;
- lookup.tunnel_type = tunnel_type;
- vport = port_table_lookup(&lookup, mutable);
- if (vport)
- return vport;
- }
- return NULL;
-}
-
-static void ecn_decapsulate(struct sk_buff *skb)
-{
- if (unlikely(INET_ECN_is_ce(OVS_CB(skb)->tun_key->ipv4_tos))) {
- __be16 protocol = skb->protocol;
-
- skb_set_network_header(skb, ETH_HLEN);
-
- if (protocol == htons(ETH_P_8021Q)) {
- if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
- return;
-
- protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- skb_set_network_header(skb, VLAN_ETH_HLEN);
- }
-
- if (protocol == htons(ETH_P_IP)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct iphdr))))
- return;
-
- IP_ECN_set_ce(ip_hdr(skb));
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (protocol == htons(ETH_P_IPV6)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct ipv6hdr))))
- return;
-
- IP6_ECN_set_ce(ipv6_hdr(skb));
- }
-#endif
- }
-}
-
/**
* ovs_tnl_rcv - ingress point for generic tunnel code
*
skb_reset_mac_header(skb);
eh = eth_hdr(skb);
- if (likely(ntohs(eh->h_proto) >= 1536))
+ if (likely(ntohs(eh->h_proto) >= ETH_P_802_3_MIN))
skb->protocol = eh->h_proto;
else
skb->protocol = htons(ETH_P_802_2);
nf_reset(skb);
skb_clear_rxhash(skb);
secpath_reset(skb);
-
- ecn_decapsulate(skb);
vlan_set_tci(skb, 0);
if (unlikely(compute_ip_summed(skb, false))) {
}
static struct sk_buff *handle_offloads(struct sk_buff *skb,
- const struct tnl_mutable_config *mutable,
const struct rtable *rt,
int tunnel_hlen)
{
return ERR_PTR(err);
}
-static int send_frags(struct sk_buff *skb,
- int tunnel_hlen)
-{
- int sent_len;
-
- sent_len = 0;
- while (skb) {
- struct sk_buff *next = skb->next;
- int frag_len = skb->len - tunnel_hlen;
- int err;
-
- skb->next = NULL;
- memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
-
- err = ip_local_out(skb);
- skb = next;
- if (unlikely(net_xmit_eval(err)))
- goto free_frags;
- sent_len += frag_len;
- }
-
- return sent_len;
-
-free_frags:
- /*
- * There's no point in continuing to send fragments once one has been
- * dropped so just free the rest. This may help improve the congestion
- * that caused the first packet to be dropped.
- */
- ovs_tnl_free_linked_skbs(skb);
- return sent_len;
-}
-
/* Compute source UDP port for outgoing packet.
* Currently we use the flow hash.
*/
int ovs_tnl_send(struct vport *vport, struct sk_buff *skb)
{
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- const struct tnl_mutable_config *mutable = rcu_dereference(tnl_vport->mutable);
- enum vport_err_type err = VPORT_E_TX_ERROR;
struct rtable *rt;
- struct ovs_key_ipv4_tunnel tun_key;
+ __be32 saddr;
int sent_len = 0;
int tunnel_hlen;
- __be16 frag_off;
- __be32 daddr;
- __be32 saddr;
- u32 skb_mark;
- u8 ttl;
- u8 tos;
-
- /* Validate the protocol headers before we try to use them. */
- if (skb->protocol == htons(ETH_P_8021Q) &&
- !vlan_tx_tag_present(skb)) {
- if (unlikely(!pskb_may_pull(skb, VLAN_ETH_HLEN)))
- goto error_free;
-
- skb->protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
- skb_set_network_header(skb, VLAN_ETH_HLEN);
- }
-
- if (skb->protocol == htons(ETH_P_IP)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct iphdr))))
- skb->protocol = 0;
- }
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6)) {
- if (unlikely(!pskb_may_pull(skb, skb_network_offset(skb)
- + sizeof(struct ipv6hdr))))
- skb->protocol = 0;
- }
-#endif
- /* If OVS_CB(skb)->tun_key is NULL, point it at the local tun_key here,
- * and zero it out.
- */
- if (!OVS_CB(skb)->tun_key) {
- memset(&tun_key, 0, sizeof(tun_key));
- OVS_CB(skb)->tun_key = &tun_key;
- }
-
- tunnel_hlen = tnl_vport->tnl_ops->hdr_len(mutable, OVS_CB(skb)->tun_key);
- if (unlikely(tunnel_hlen < 0)) {
- err = VPORT_E_TX_DROPPED;
+ if (unlikely(!OVS_CB(skb)->tun_key))
goto error_free;
- }
- tunnel_hlen += sizeof(struct iphdr);
-
- if (OVS_CB(skb)->tun_key->ipv4_dst) {
- daddr = OVS_CB(skb)->tun_key->ipv4_dst;
- saddr = OVS_CB(skb)->tun_key->ipv4_src;
- tos = OVS_CB(skb)->tun_key->ipv4_tos;
- ttl = OVS_CB(skb)->tun_key->ipv4_ttl;
- frag_off = OVS_CB(skb)->tun_key->tun_flags &
- OVS_TNL_F_DONT_FRAGMENT ? htons(IP_DF) : 0;
- } else {
- u8 inner_tos;
- daddr = mutable->key.daddr;
- saddr = mutable->key.saddr;
-
- if (unlikely(!daddr)) {
- /* Trying to sent packet from Null-port without
- * tunnel info? Drop this packet. */
- err = VPORT_E_TX_DROPPED;
- goto error_free;
- }
-
- /* ToS */
- if (skb->protocol == htons(ETH_P_IP))
- inner_tos = ip_hdr(skb)->tos;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- inner_tos = ipv6_get_dsfield(ipv6_hdr(skb));
-#endif
- else
- inner_tos = 0;
-
- if (mutable->flags & TNL_F_TOS_INHERIT)
- tos = inner_tos;
- else
- tos = mutable->tos;
-
- tos = INET_ECN_encapsulate(tos, inner_tos);
-
- /* TTL */
- ttl = mutable->ttl;
- if (mutable->flags & TNL_F_TTL_INHERIT) {
- if (skb->protocol == htons(ETH_P_IP))
- ttl = ip_hdr(skb)->ttl;
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
- else if (skb->protocol == htons(ETH_P_IPV6))
- ttl = ipv6_hdr(skb)->hop_limit;
-#endif
- }
-
- frag_off = mutable->flags & TNL_F_DF_DEFAULT ? htons(IP_DF) : 0;
- }
/* Route lookup */
- skb_mark = skb_get_mark(skb);
- rt = find_route(port_key_get_net(&mutable->key), &saddr, daddr,
- tnl_vport->tnl_ops->ipproto, tos, skb_mark);
+ saddr = OVS_CB(skb)->tun_key->ipv4_src;
+ rt = find_route(ovs_dp_get_net(vport->dp),
+ &saddr,
+ OVS_CB(skb)->tun_key->ipv4_dst,
+ tnl_vport->tnl_ops->ipproto,
+ OVS_CB(skb)->tun_key->ipv4_tos,
+ skb_get_mark(skb));
if (IS_ERR(rt))
goto error_free;
- /* Reset SKB */
- nf_reset(skb);
- secpath_reset(skb);
- skb_dst_drop(skb);
- skb_clear_rxhash(skb);
-
/* Offloading */
- skb = handle_offloads(skb, mutable, rt, tunnel_hlen);
+ tunnel_hlen = tnl_vport->tnl_ops->hdr_len(OVS_CB(skb)->tun_key);
+ tunnel_hlen += sizeof(struct iphdr);
+
+ skb = handle_offloads(skb, rt, tunnel_hlen);
if (IS_ERR(skb)) {
skb = NULL;
goto err_free_rt;
}
- /* TTL Fixup. */
- if (!OVS_CB(skb)->tun_key->ipv4_dst) {
- if (!(mutable->flags & TNL_F_TTL_INHERIT)) {
- if (!ttl)
- ttl = ip4_dst_hoplimit(&rt_dst(rt));
- }
- }
+ /* Reset SKB */
+ nf_reset(skb);
+ secpath_reset(skb);
+ skb_dst_drop(skb);
+ skb_clear_rxhash(skb);
while (skb) {
- struct iphdr *iph;
struct sk_buff *next_skb = skb->next;
+ struct iphdr *iph;
+ int frag_len;
+ int err;
+
skb->next = NULL;
if (unlikely(vlan_deaccel_tag(skb)))
goto next;
+ frag_len = skb->len;
skb_push(skb, tunnel_hlen);
skb_reset_network_header(skb);
skb_set_transport_header(skb, sizeof(struct iphdr));
else
skb_dst_set(skb, &rt_dst(rt));
+ /* Push Tunnel header. */
+ tnl_vport->tnl_ops->build_header(vport, skb, tunnel_hlen);
+
/* Push IP header. */
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = sizeof(struct iphdr) >> 2;
iph->protocol = tnl_vport->tnl_ops->ipproto;
- iph->daddr = daddr;
+ iph->daddr = OVS_CB(skb)->tun_key->ipv4_dst;
iph->saddr = saddr;
- iph->tos = tos;
- iph->ttl = ttl;
- iph->frag_off = frag_off;
- ip_select_ident(iph, &rt_dst(rt), NULL);
+ iph->tos = OVS_CB(skb)->tun_key->ipv4_tos;
+ iph->ttl = OVS_CB(skb)->tun_key->ipv4_ttl;
+ iph->frag_off = OVS_CB(skb)->tun_key->tun_flags &
+ OVS_TNL_F_DONT_FRAGMENT ? htons(IP_DF) : 0;
+ /*
+ * Allow our local IP stack to fragment the outer packet even
+ * if the DF bit is set as a last resort. We also need to
+ * force selection of an IP ID here with __ip_select_ident(),
+ * as ip_select_ident() assumes a proper ID is not needed when
+ * when the DF bit is set.
+ */
+ skb->local_df = 1;
+ __ip_select_ident(iph, skb_dst(skb), 0);
- /* Push Tunnel header. */
- skb = tnl_vport->tnl_ops->build_header(vport, mutable,
- &rt_dst(rt), skb, tunnel_hlen);
- if (unlikely(!skb))
+ memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+
+ err = ip_local_out(skb);
+ if (unlikely(net_xmit_eval(err)))
goto next;
- sent_len += send_frags(skb, tunnel_hlen);
+ sent_len += frag_len;
next:
skb = next_skb;
err_free_rt:
ip_rt_put(rt);
error_free:
- ovs_tnl_free_linked_skbs(skb);
- ovs_vport_record_error(vport, err);
+ kfree_skb(skb);
+ ovs_vport_record_error(vport, VPORT_E_TX_ERROR);
return sent_len;
}
-static const struct nla_policy tnl_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
- [OVS_TUNNEL_ATTR_FLAGS] = { .type = NLA_U32 },
- [OVS_TUNNEL_ATTR_DST_IPV4] = { .type = NLA_U32 },
- [OVS_TUNNEL_ATTR_SRC_IPV4] = { .type = NLA_U32 },
- [OVS_TUNNEL_ATTR_OUT_KEY] = { .type = NLA_U64 },
- [OVS_TUNNEL_ATTR_IN_KEY] = { .type = NLA_U64 },
- [OVS_TUNNEL_ATTR_TOS] = { .type = NLA_U8 },
- [OVS_TUNNEL_ATTR_TTL] = { .type = NLA_U8 },
- [OVS_TUNNEL_ATTR_DST_PORT] = { .type = NLA_U16 },
-};
-
-/* Sets OVS_TUNNEL_ATTR_* fields in 'mutable', which must initially be
- * zeroed. */
-static int tnl_set_config(struct net *net, struct nlattr *options,
- const struct tnl_ops *tnl_ops,
- const struct vport *cur_vport,
- struct tnl_mutable_config *mutable)
-{
- const struct vport *old_vport;
- const struct tnl_mutable_config *old_mutable;
- struct nlattr *a[OVS_TUNNEL_ATTR_MAX + 1];
- int err;
-
- port_key_set_net(&mutable->key, net);
- mutable->key.tunnel_type = tnl_ops->tunnel_type;
- if (!options)
- goto out;
-
- err = nla_parse_nested(a, OVS_TUNNEL_ATTR_MAX, options, tnl_policy);
- if (err)
- return err;
-
- /* Process attributes possibly useful for null_ports first */
- if (a[OVS_TUNNEL_ATTR_DST_PORT])
- mutable->dst_port =
- htons(nla_get_u16(a[OVS_TUNNEL_ATTR_DST_PORT]));
-
- if (a[OVS_TUNNEL_ATTR_DST_IPV4])
- mutable->key.daddr = nla_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
-
- /* Skip the rest if configuring a null_port */
- if (!mutable->key.daddr)
- goto out;
-
- if (a[OVS_TUNNEL_ATTR_FLAGS])
- mutable->flags = nla_get_u32(a[OVS_TUNNEL_ATTR_FLAGS])
- & TNL_F_PUBLIC;
-
- if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
- if (ipv4_is_multicast(mutable->key.daddr))
- return -EINVAL;
- mutable->key.saddr = nla_get_be32(a[OVS_TUNNEL_ATTR_SRC_IPV4]);
- }
-
- if (a[OVS_TUNNEL_ATTR_TOS]) {
- mutable->tos = nla_get_u8(a[OVS_TUNNEL_ATTR_TOS]);
- /* Reject ToS config with ECN bits set. */
- if (mutable->tos & INET_ECN_MASK)
- return -EINVAL;
- }
-
- if (a[OVS_TUNNEL_ATTR_TTL])
- mutable->ttl = nla_get_u8(a[OVS_TUNNEL_ATTR_TTL]);
-
- if (!a[OVS_TUNNEL_ATTR_IN_KEY]) {
- mutable->key.tunnel_type |= TNL_T_KEY_MATCH;
- mutable->flags |= TNL_F_IN_KEY_MATCH;
- } else {
- mutable->key.tunnel_type |= TNL_T_KEY_EXACT;
- mutable->key.in_key = nla_get_be64(a[OVS_TUNNEL_ATTR_IN_KEY]);
- }
-
- if (!a[OVS_TUNNEL_ATTR_OUT_KEY])
- mutable->flags |= TNL_F_OUT_KEY_ACTION;
- else
- mutable->out_key = nla_get_be64(a[OVS_TUNNEL_ATTR_OUT_KEY]);
-
- mutable->mlink = 0;
- if (ipv4_is_multicast(mutable->key.daddr)) {
- struct net_device *dev;
- struct rtable *rt;
- __be32 saddr = mutable->key.saddr;
-
- rt = find_route(port_key_get_net(&mutable->key),
- &saddr, mutable->key.daddr,
- tnl_ops->ipproto, mutable->tos, 0);
- if (IS_ERR(rt))
- return -EADDRNOTAVAIL;
- dev = rt_dst(rt).dev;
- ip_rt_put(rt);
- if (__in_dev_get_rtnl(dev) == NULL)
- return -EADDRNOTAVAIL;
- mutable->mlink = dev->ifindex;
- ip_mc_inc_group(__in_dev_get_rtnl(dev), mutable->key.daddr);
- }
-
-out:
- old_vport = port_table_lookup(&mutable->key, &old_mutable);
- if (old_vport && old_vport != cur_vport)
- return -EEXIST;
-
- return 0;
-}
-
struct vport *ovs_tnl_create(const struct vport_parms *parms,
const struct vport_ops *vport_ops,
const struct tnl_ops *tnl_ops)
{
struct vport *vport;
struct tnl_vport *tnl_vport;
- struct tnl_mutable_config *mutable;
int err;
vport = ovs_vport_alloc(sizeof(struct tnl_vport), vport_ops, parms);
strcpy(tnl_vport->name, parms->name);
tnl_vport->tnl_ops = tnl_ops;
- mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
- if (!mutable) {
- err = -ENOMEM;
- goto error_free_vport;
- }
-
- err = tnl_set_config(ovs_dp_get_net(parms->dp), parms->options, tnl_ops,
- NULL, mutable);
- if (err)
- goto error_free_mutable;
-
- rcu_assign_pointer(tnl_vport->mutable, mutable);
-
- port_table_add_port(vport);
return vport;
-error_free_mutable:
- free_mutable_rtnl(mutable);
- kfree(mutable);
-error_free_vport:
- ovs_vport_free(vport);
error:
return ERR_PTR(err);
}
-int ovs_tnl_set_options(struct vport *vport, struct nlattr *options)
-{
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- const struct tnl_mutable_config *old_mutable;
- struct tnl_mutable_config *mutable;
- int err;
-
- old_mutable = rtnl_dereference(tnl_vport->mutable);
- if (!old_mutable->key.daddr)
- return -EINVAL;
-
- mutable = kzalloc(sizeof(struct tnl_mutable_config), GFP_KERNEL);
- if (!mutable) {
- err = -ENOMEM;
- goto error;
- }
-
- /* Parse the others configured by userspace. */
- err = tnl_set_config(ovs_dp_get_net(vport->dp), options, tnl_vport->tnl_ops,
- vport, mutable);
- if (err)
- goto error_free;
-
- if (port_hash(&mutable->key) != port_hash(&old_mutable->key))
- port_table_move_port(vport, mutable);
- else
- assign_config_rcu(vport, mutable);
-
- return 0;
-
-error_free:
- free_mutable_rtnl(mutable);
- kfree(mutable);
-error:
- return err;
-}
-
-int ovs_tnl_get_options(const struct vport *vport, struct sk_buff *skb)
-{
- const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- const struct tnl_mutable_config *mutable = rcu_dereference_rtnl(tnl_vport->mutable);
-
- if (mutable->dst_port && nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT,
- ntohs(mutable->dst_port)))
- goto nla_put_failure;
-
- /* Skip the rest for null_ports */
- if (!mutable->key.daddr)
- return 0;
-
- if (nla_put_be32(skb, OVS_TUNNEL_ATTR_DST_IPV4, mutable->key.daddr))
- goto nla_put_failure;
- if (nla_put_u32(skb, OVS_TUNNEL_ATTR_FLAGS,
- mutable->flags & TNL_F_PUBLIC))
- goto nla_put_failure;
- if (!(mutable->flags & TNL_F_IN_KEY_MATCH) &&
- nla_put_be64(skb, OVS_TUNNEL_ATTR_IN_KEY, mutable->key.in_key))
- goto nla_put_failure;
- if (!(mutable->flags & TNL_F_OUT_KEY_ACTION) &&
- nla_put_be64(skb, OVS_TUNNEL_ATTR_OUT_KEY, mutable->out_key))
- goto nla_put_failure;
- if (mutable->key.saddr &&
- nla_put_be32(skb, OVS_TUNNEL_ATTR_SRC_IPV4, mutable->key.saddr))
- goto nla_put_failure;
- if (mutable->tos && nla_put_u8(skb, OVS_TUNNEL_ATTR_TOS, mutable->tos))
- goto nla_put_failure;
- if (mutable->ttl && nla_put_u8(skb, OVS_TUNNEL_ATTR_TTL, mutable->ttl))
- goto nla_put_failure;
-
- return 0;
-
-nla_put_failure:
- return -EMSGSIZE;
-}
-
static void free_port_rcu(struct rcu_head *rcu)
{
struct tnl_vport *tnl_vport = container_of(rcu,
struct tnl_vport, rcu);
- kfree((struct tnl_mutable __force *)tnl_vport->mutable);
- ovs_vport_free(tnl_vport_to_vport(tnl_vport));
+ ovs_vport_free(vport_from_priv(tnl_vport));
}
void ovs_tnl_destroy(struct vport *vport)
{
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *mutable;
- mutable = rtnl_dereference(tnl_vport->mutable);
- port_table_remove_port(vport);
- free_mutable_rtnl(mutable);
call_rcu(&tnl_vport->rcu, free_port_rcu);
}
const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
return tnl_vport->name;
}
-
-void ovs_tnl_free_linked_skbs(struct sk_buff *skb)
-{
- while (skb) {
- struct sk_buff *next = skb->next;
- kfree_skb(skb);
- skb = next;
- }
-}
-
-int ovs_tnl_init(void)
-{
- int i;
-
- port_table = kmalloc(PORT_TABLE_SIZE * sizeof(struct hlist_head *),
- GFP_KERNEL);
- if (!port_table)
- return -ENOMEM;
-
- for (i = 0; i < PORT_TABLE_SIZE; i++)
- INIT_HLIST_HEAD(&port_table[i]);
-
- return 0;
-}
-
-void ovs_tnl_exit(void)
-{
- kfree(port_table);
-}
#include <net/netns/generic.h>
#include "flow.h"
-#include "openvswitch/tunnel.h"
#include "vport.h"
-/*
- * The absolute minimum fragment size. Note that there are many other
- * definitions of the minimum MTU.
- */
-#define IP_MIN_MTU 68
-
-/*
- * One of these goes in struct tnl_ops and in tnl_find_port().
- * These values are in the same namespace as other TNL_T_* values, so
- * only the least significant 10 bits are available to define protocol
- * identifiers.
- */
-#define TNL_T_PROTO_GRE 0
-#define TNL_T_PROTO_GRE64 1
-#define TNL_T_PROTO_VXLAN 3
-#define TNL_T_PROTO_LISP 4
-
-/* These flags are only needed when calling tnl_find_port(). */
-#define TNL_T_KEY_EXACT (1 << 10)
-#define TNL_T_KEY_MATCH (1 << 11)
-
-/* Private flags not exposed to userspace in this form. */
-#define TNL_F_IN_KEY_MATCH (1 << 16) /* Store the key in tun_id to
- * match in flow table. */
-#define TNL_F_OUT_KEY_ACTION (1 << 17) /* Get the key from a SET_TUNNEL
- * action. */
-
-/* All public tunnel flags. */
-#define TNL_F_PUBLIC (TNL_F_CSUM | TNL_F_TOS_INHERIT | TNL_F_TTL_INHERIT | \
- TNL_F_DF_DEFAULT)
-
-/**
- * struct port_lookup_key - Tunnel port key, used as hash table key.
- * @in_key: Key to match on input, 0 for wildcard.
- * @net: Network namespace of the port.
- * @saddr: IPv4 source address to match, 0 to accept any source address.
- * @daddr: IPv4 destination of tunnel.
- * @tunnel_type: Set of TNL_T_* flags that define lookup.
- */
-struct port_lookup_key {
- __be64 in_key;
-#ifdef CONFIG_NET_NS
- struct net *net;
-#endif
- __be32 saddr;
- __be32 daddr;
- u32 tunnel_type;
-};
-
-#define PORT_KEY_LEN (offsetof(struct port_lookup_key, tunnel_type) + \
- FIELD_SIZEOF(struct port_lookup_key, tunnel_type))
-
-static inline struct net *port_key_get_net(const struct port_lookup_key *key)
-{
- return read_pnet(&key->net);
-}
-
-static inline void port_key_set_net(struct port_lookup_key *key, struct net *net)
-{
- write_pnet(&key->net, net);
-}
-
-/**
- * struct tnl_mutable_config - modifiable configuration for a tunnel.
- * @key: Used as key for tunnel port. Configured via OVS_TUNNEL_ATTR_*
- * attributes.
- * @rcu: RCU callback head for deferred destruction.
- * @tunnel_hlen: Tunnel header length.
- * @out_key: Key to use on output, 0 if this tunnel has no fixed output key.
- * @flags: TNL_F_* flags.
- * @tos: IPv4 TOS value to use for tunnel, 0 if no fixed TOS.
- * @ttl: IPv4 TTL value to use for tunnel, 0 if no fixed TTL.
- */
-struct tnl_mutable_config {
- struct port_lookup_key key;
- struct rcu_head rcu;
-
- /* Configured via OVS_TUNNEL_ATTR_* attributes. */
- __be64 out_key;
- u32 flags;
- u8 tos;
- u8 ttl;
- __be16 dst_port;
-
- /* Multicast configuration. */
- int mlink;
-};
-
struct tnl_ops {
- u32 tunnel_type; /* Put the TNL_T_PROTO_* type in here. */
u8 ipproto; /* The IP protocol for the tunnel. */
/*
* Returns the length of the tunnel header that will be added in
- * build_header() (i.e. excludes the IP header). Returns a negative
- * error code if the configuration is invalid.
+ * build_header() (i.e. excludes the IP header).
*/
- int (*hdr_len)(const struct tnl_mutable_config *,
- const struct ovs_key_ipv4_tunnel *);
+ int (*hdr_len)(const struct ovs_key_ipv4_tunnel *);
/*
- * Returns a linked list of SKBs with tunnel headers (multiple
- * packets may be generated in the event of fragmentation). Space
- * will have already been allocated at the start of the packet equal
- * to sizeof(struct iphdr) + value returned by hdr_len(). The IP
- * header will have already been constructed.
- */
- struct sk_buff *(*build_header)(const struct vport *,
- const struct tnl_mutable_config *,
- struct dst_entry *, struct sk_buff *,
- int tunnel_hlen);
+ * Builds header for given SKB. Space will have already been
+ * allocated at the start of the packet equal
+ * to sizeof(struct iphdr) + value returned by hdr_len().
+ */
+ void (*build_header)(const struct vport *, struct sk_buff *,
+ int tunnel_hlen);
};
struct tnl_vport {
struct rcu_head rcu;
- struct hlist_node hash_node;
+ __be16 dst_port;
char name[IFNAMSIZ];
const struct tnl_ops *tnl_ops;
-
- struct tnl_mutable_config __rcu *mutable;
};
struct vport *ovs_tnl_create(const struct vport_parms *, const struct vport_ops *,
const struct tnl_ops *);
void ovs_tnl_destroy(struct vport *);
-int ovs_tnl_set_options(struct vport *, struct nlattr *);
-int ovs_tnl_get_options(const struct vport *, struct sk_buff *);
-
const char *ovs_tnl_get_name(const struct vport *vport);
int ovs_tnl_send(struct vport *vport, struct sk_buff *skb);
void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb);
u16 ovs_tnl_get_src_port(struct sk_buff *skb);
-struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
- __be64 key, int tunnel_type,
- const struct tnl_mutable_config **mutable);
-bool ovs_tnl_frag_needed(struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct sk_buff *skb, unsigned int mtu);
-void ovs_tnl_free_linked_skbs(struct sk_buff *skb);
-
-int ovs_tnl_init(void);
-void ovs_tnl_exit(void);
static inline struct tnl_vport *tnl_vport_priv(const struct vport *vport)
{
return vport_priv(vport);
sizeof(*tun_key) - OVS_TUNNEL_KEY_SIZE);
}
-static inline void tnl_get_param(const struct tnl_mutable_config *mutable,
- const struct ovs_key_ipv4_tunnel *tun_key,
- u32 *flags, __be64 *out_key)
-{
- if (tun_key->ipv4_dst) {
- *flags = 0;
-
- if (tun_key->tun_flags & OVS_TNL_F_KEY)
- *flags = TNL_F_OUT_KEY_ACTION;
- if (tun_key->tun_flags & OVS_TNL_F_CSUM)
- *flags |= TNL_F_CSUM;
- *out_key = tun_key->tun_id;
- } else {
- *flags = mutable->flags;
- if (mutable->flags & TNL_F_OUT_KEY_ACTION)
- *out_key = tun_key->tun_id;
- else
- *out_key = mutable->out_key;
- }
-}
-
#endif /* tunnel.h */
__be16 protocol;
};
-static int gre_hdr_len(const struct tnl_mutable_config *mutable,
- const struct ovs_key_ipv4_tunnel *tun_key)
+static int gre_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
{
- int len;
- u32 flags;
- __be64 out_key;
+ int len = GRE_HEADER_SECTION;
- tnl_get_param(mutable, tun_key, &flags, &out_key);
- len = GRE_HEADER_SECTION;
-
- if (flags & TNL_F_CSUM)
+ if (tun_key->tun_flags & OVS_TNL_F_KEY)
+ len += GRE_HEADER_SECTION;
+ if (tun_key->tun_flags & OVS_TNL_F_CSUM)
len += GRE_HEADER_SECTION;
+ return len;
+}
+static int gre64_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
+{
/* Set key for GRE64 tunnels, even when key if is zero. */
- if (out_key ||
- mutable->key.tunnel_type & TNL_T_PROTO_GRE64 ||
- flags & TNL_F_OUT_KEY_ACTION) {
+ int len = GRE_HEADER_SECTION + /* GRE Hdr */
+ GRE_HEADER_SECTION + /* GRE Key */
+ GRE_HEADER_SECTION; /* GRE SEQ */
+ if (tun_key->tun_flags & OVS_TNL_F_CSUM)
len += GRE_HEADER_SECTION;
- if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64)
- len += GRE_HEADER_SECTION;
- }
+
return len;
}
-
/* Returns the least-significant 32 bits of a __be64. */
static __be32 be64_get_low32(__be64 x)
{
#endif
}
-static struct sk_buff *gre_build_header(const struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct dst_entry *dst,
- struct sk_buff *skb,
- int tunnel_hlen)
+static void __gre_build_header(struct sk_buff *skb,
+ int tunnel_hlen,
+ bool is_gre64)
{
- u32 flags;
- __be64 out_key;
const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
__be32 *options = (__be32 *)(skb_network_header(skb) + tunnel_hlen
- - GRE_HEADER_SECTION);
+ - GRE_HEADER_SECTION);
struct gre_base_hdr *greh = (struct gre_base_hdr *) skb_transport_header(skb);
-
- tnl_get_param(mutable, tun_key, &flags, &out_key);
-
greh->protocol = htons(ETH_P_TEB);
greh->flags = 0;
/* Work backwards over the options so the checksum is last. */
- if (out_key || flags & TNL_F_OUT_KEY_ACTION ||
- mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+ if (tun_key->tun_flags & OVS_TNL_F_KEY || is_gre64) {
greh->flags |= GRE_KEY;
- if (mutable->key.tunnel_type & TNL_T_PROTO_GRE64) {
+ if (is_gre64) {
/* Set higher 32 bits to seq. */
- *options = be64_get_high32(out_key);
+ *options = be64_get_high32(tun_key->tun_id);
options--;
greh->flags |= GRE_SEQ;
}
- *options = be64_get_low32(out_key);
+ *options = be64_get_low32(tun_key->tun_id);
options--;
}
- if (flags & TNL_F_CSUM) {
+ if (tun_key->tun_flags & OVS_TNL_F_CSUM) {
greh->flags |= GRE_CSUM;
*options = 0;
*(__sum16 *)options = csum_fold(skb_checksum(skb,
skb->len - skb_transport_offset(skb),
0));
}
- /*
- * Allow our local IP stack to fragment the outer packet even if the
- * DF bit is set as a last resort. We also need to force selection of
- * an IP ID here because Linux will otherwise leave it at 0 if the
- * packet originally had DF set.
- */
- skb->local_df = 1;
- __ip_select_ident(ip_hdr(skb), dst, 0);
-
- return skb;
+}
+
+static void gre_build_header(const struct vport *vport,
+ struct sk_buff *skb,
+ int tunnel_hlen)
+{
+ __gre_build_header(skb, tunnel_hlen, false);
+}
+
+static void gre64_build_header(const struct vport *vport,
+ struct sk_buff *skb,
+ int tunnel_hlen)
+{
+ __gre_build_header(skb, tunnel_hlen, true);
}
static __be64 key_to_tunnel_id(__be32 key, __be32 seq)
}
static int parse_header(struct iphdr *iph, __be16 *flags, __be64 *tun_id,
- u32 *tunnel_type)
+ bool *is_gre64)
{
/* IP and ICMP protocol handlers check that the IHL is valid. */
struct gre_base_hdr *greh = (struct gre_base_hdr *)((u8 *)iph + (iph->ihl << 2));
if (greh->flags & GRE_SEQ) {
seq = *options;
- *tunnel_type = TNL_T_PROTO_GRE64;
+ *is_gre64 = true;
} else {
seq = 0;
- *tunnel_type = TNL_T_PROTO_GRE;
+ *is_gre64 = false;
}
*tun_id = key_to_tunnel_id(gre_key, seq);
} else {
*tun_id = 0;
/* Ignore GRE seq if there is no key present. */
- *tunnel_type = TNL_T_PROTO_GRE;
+ *is_gre64 = false;
}
if (greh->flags & GRE_SEQ)
return (csum == 0);
}
-static u32 gre_flags_to_tunnel_flags(const struct tnl_mutable_config *mutable,
- __be16 gre_flags, __be64 *key)
+static u32 gre_flags_to_tunnel_flags(__be16 gre_flags, bool is_gre64)
{
u32 tunnel_flags = 0;
- if (gre_flags & GRE_KEY) {
- if (mutable->flags & TNL_F_IN_KEY_MATCH ||
- !mutable->key.daddr)
- tunnel_flags = OVS_TNL_F_KEY;
- else
- *key = 0;
- }
+ if (gre_flags & GRE_KEY || is_gre64)
+ tunnel_flags = OVS_TNL_F_KEY;
if (gre_flags & GRE_CSUM)
tunnel_flags |= OVS_TNL_F_CSUM;
/* Called with rcu_read_lock and BH disabled. */
static int gre_rcv(struct sk_buff *skb)
{
+ struct ovs_net *ovs_net;
struct vport *vport;
- const struct tnl_mutable_config *mutable;
int hdr_len;
struct iphdr *iph;
struct ovs_key_ipv4_tunnel tun_key;
__be16 gre_flags;
u32 tnl_flags;
__be64 key;
- u32 tunnel_type;
+ bool is_gre64;
if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr) + ETH_HLEN)))
goto error;
if (unlikely(!check_checksum(skb)))
goto error;
- hdr_len = parse_header(ip_hdr(skb), &gre_flags, &key, &tunnel_type);
+ hdr_len = parse_header(ip_hdr(skb), &gre_flags, &key, &is_gre64);
if (unlikely(hdr_len < 0))
goto error;
- if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
+ ovs_net = net_generic(dev_net(skb->dev), ovs_net_id);
+ if (is_gre64)
+ vport = rcu_dereference(ovs_net->vport_net.gre64_vport);
+ else
+ vport = rcu_dereference(ovs_net->vport_net.gre_vport);
+ if (unlikely(!vport))
goto error;
- iph = ip_hdr(skb);
- vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr, key,
- tunnel_type, &mutable);
- if (unlikely(!vport))
+ if (unlikely(!pskb_may_pull(skb, hdr_len + ETH_HLEN)))
goto error;
- tnl_flags = gre_flags_to_tunnel_flags(mutable, gre_flags, &key);
+ iph = ip_hdr(skb);
+ tnl_flags = gre_flags_to_tunnel_flags(gre_flags, is_gre64);
tnl_tun_key_init(&tun_key, iph, key, tnl_flags);
OVS_CB(skb)->tun_key = &tun_key;
return 0;
}
-static const struct tnl_ops gre_tnl_ops = {
- .tunnel_type = TNL_T_PROTO_GRE,
- .ipproto = IPPROTO_GRE,
- .hdr_len = gre_hdr_len,
- .build_header = gre_build_header,
-};
-
-static struct vport *gre_create(const struct vport_parms *parms)
-{
- return ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
-}
-
-static const struct tnl_ops gre64_tnl_ops = {
- .tunnel_type = TNL_T_PROTO_GRE64,
- .ipproto = IPPROTO_GRE,
- .hdr_len = gre_hdr_len,
- .build_header = gre_build_header,
-};
-
-static struct vport *gre_create64(const struct vport_parms *parms)
-{
- return ovs_tnl_create(parms, &ovs_gre64_vport_ops, &gre64_tnl_ops);
-}
-
static const struct net_protocol gre_protocol_handlers = {
.handler = gre_rcv,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
inet_del_protocol(&gre_protocol_handlers, IPPROTO_GRE);
}
+/* GRE vport. */
+static const struct tnl_ops gre_tnl_ops = {
+ .ipproto = IPPROTO_GRE,
+ .hdr_len = gre_hdr_len,
+ .build_header = gre_build_header,
+};
+
+static struct vport *gre_create(const struct vport_parms *parms)
+{
+ struct net *net = ovs_dp_get_net(parms->dp);
+ struct ovs_net *ovs_net;
+ struct vport *vport;
+
+ ovs_net = net_generic(net, ovs_net_id);
+ if (rtnl_dereference(ovs_net->vport_net.gre_vport))
+ return ERR_PTR(-EEXIST);
+
+ vport = ovs_tnl_create(parms, &ovs_gre_vport_ops, &gre_tnl_ops);
+
+ rcu_assign_pointer(ovs_net->vport_net.gre_vport, vport);
+ return vport;
+}
+
+static void gre_tnl_destroy(struct vport *vport)
+{
+ struct net *net = ovs_dp_get_net(vport->dp);
+ struct ovs_net *ovs_net;
+
+ ovs_net = net_generic(net, ovs_net_id);
+
+ rcu_assign_pointer(ovs_net->vport_net.gre_vport, NULL);
+ ovs_tnl_destroy(vport);
+}
+
const struct vport_ops ovs_gre_vport_ops = {
.type = OVS_VPORT_TYPE_GRE,
.flags = VPORT_F_TUN_ID,
.init = gre_init,
.exit = gre_exit,
.create = gre_create,
- .destroy = ovs_tnl_destroy,
+ .destroy = gre_tnl_destroy,
.get_name = ovs_tnl_get_name,
- .get_options = ovs_tnl_get_options,
- .set_options = ovs_tnl_set_options,
.send = ovs_tnl_send,
};
+/* GRE64 vport. */
+static const struct tnl_ops gre64_tnl_ops = {
+ .ipproto = IPPROTO_GRE,
+ .hdr_len = gre64_hdr_len,
+ .build_header = gre64_build_header,
+};
+
+static struct vport *gre64_create(const struct vport_parms *parms)
+{
+ struct net *net = ovs_dp_get_net(parms->dp);
+ struct ovs_net *ovs_net;
+ struct vport *vport;
+
+ ovs_net = net_generic(net, ovs_net_id);
+ if (rtnl_dereference(ovs_net->vport_net.gre64_vport))
+ return ERR_PTR(-EEXIST);
+
+ vport = ovs_tnl_create(parms, &ovs_gre64_vport_ops, &gre64_tnl_ops);
+
+ rcu_assign_pointer(ovs_net->vport_net.gre64_vport, vport);
+ return vport;
+}
+
+
+static void gre64_tnl_destroy(struct vport *vport)
+{
+ struct net *net = ovs_dp_get_net(vport->dp);
+ struct ovs_net *ovs_net;
+
+ ovs_net = net_generic(net, ovs_net_id);
+
+ rcu_assign_pointer(ovs_net->vport_net.gre64_vport, NULL);
+ ovs_tnl_destroy(vport);
+}
+
const struct vport_ops ovs_gre64_vport_ops = {
.type = OVS_VPORT_TYPE_GRE64,
.flags = VPORT_F_TUN_ID,
.init = gre_init,
.exit = gre_exit,
- .create = gre_create64,
- .destroy = ovs_tnl_destroy,
+ .create = gre64_create,
+ .destroy = gre64_tnl_destroy,
.get_name = ovs_tnl_get_name,
- .get_options = ovs_tnl_get_options,
- .set_options = ovs_tnl_set_options,
.send = ovs_tnl_send,
};
}
vlan_copy_skb_tci(skb);
- OVS_CB(skb)->flow = NULL;
rcu_read_lock();
ovs_vport_receive(internal_dev_priv(netdev)->vport, skb);
static void internal_dev_getinfo(struct net_device *netdev,
struct ethtool_drvinfo *info)
{
- strcpy(info->driver, "openvswitch");
+ strlcpy(info->driver, "openvswitch", sizeof(info->driver));
}
static const struct ethtool_ops internal_dev_ethtool_ops = {
const struct vport_ops ovs_internal_vport_ops = {
.type = OVS_VPORT_TYPE_INTERNAL,
- .flags = VPORT_F_REQUIRED | VPORT_F_FLOW,
+ .flags = VPORT_F_REQUIRED,
.create = internal_dev_create,
.destroy = internal_dev_destroy,
.get_name = ovs_netdev_get_name,
#include <linux/in.h>
#include <linux/ip.h>
-#include <linux/list.h>
#include <linux/net.h>
+#include <linux/rculist.h>
#include <linux/udp.h>
#include <net/icmp.h>
#define LISP_HLEN (sizeof(struct udphdr) + sizeof(struct lisphdr))
-static inline int lisp_hdr_len(const struct tnl_mutable_config *mutable,
- const struct ovs_key_ipv4_tunnel *tun_key)
+static inline int lisp_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
{
return LISP_HLEN;
}
/**
* struct lisp_port - Keeps track of open UDP ports
* @list: list element.
- * @port: The UDP port number in network byte order.
+ * @vport: vport for the tunnel.
* @socket: The socket created for this port number.
- * @count: How many ports are using this socket/port.
*/
struct lisp_port {
struct list_head list;
- __be16 port;
+ struct vport *vport;
struct socket *lisp_rcv_socket;
- int count;
+ struct rcu_head rcu;
};
static LIST_HEAD(lisp_ports);
-static struct lisp_port *lisp_port_exists(struct net *net, __be16 port)
+static struct lisp_port *lisp_find_port(struct net *net, __be16 port)
{
struct lisp_port *lisp_port;
- list_for_each_entry(lisp_port, &lisp_ports, list) {
- if (lisp_port->port == port &&
+ list_for_each_entry_rcu(lisp_port, &lisp_ports, list) {
+ struct tnl_vport *tnl_vport = tnl_vport_priv(lisp_port->vport);
+
+ if (tnl_vport->dst_port == port &&
net_eq(sock_net(lisp_port->lisp_rcv_socket->sk), net))
return lisp_port;
}
#endif
}
-static struct sk_buff *lisp_build_header(const struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct dst_entry *dst,
- struct sk_buff *skb,
- int tunnel_hlen)
+static void lisp_build_header(const struct vport *vport,
+ struct sk_buff *skb,
+ int tunnel_hlen)
{
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
struct udphdr *udph = udp_hdr(skb);
struct lisphdr *lisph = (struct lisphdr *)(udph + 1);
const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
- __be64 out_key;
- u32 flags;
-
- tnl_get_param(mutable, tun_key, &flags, &out_key);
- udph->dest = mutable->dst_port;
+ udph->dest = tnl_vport->dst_port;
udph->source = htons(ovs_tnl_get_src_port(skb));
udph->check = 0;
udph->len = htons(skb->len - skb_transport_offset(skb));
lisph->u1.nonce[1] = 0;
lisph->u1.nonce[2] = 0;
- tunnel_id_to_instance_id(out_key, &lisph->u2.word2.instance_id[0]);
+ tunnel_id_to_instance_id(tun_key->tun_id, &lisph->u2.word2.instance_id[0]);
lisph->u2.word2.locator_status_bits = 1;
-
- /*
- * Allow our local IP stack to fragment the outer packet even if the
- * DF bit is set as a last resort. We also need to force selection of
- * an IP ID here because Linux will otherwise leave it at 0 if the
- * packet originally had DF set.
- */
- skb->local_df = 1;
- __ip_select_ident(ip_hdr(skb), dst, 0);
-
- return skb;
}
/* Called with rcu_read_lock and BH disabled. */
static int lisp_rcv(struct sock *sk, struct sk_buff *skb)
{
- struct vport *vport;
+ struct lisp_port *lisp_port;
struct lisphdr *lisph;
- const struct tnl_mutable_config *mutable;
struct iphdr *iph, *inner_iph;
struct ovs_key_ipv4_tunnel tun_key;
__be64 key;
- u32 tunnel_flags = 0;
struct ethhdr *ethh;
__be16 protocol;
+ lisp_port = lisp_find_port(dev_net(skb->dev), udp_hdr(skb)->dest);
+ if (unlikely(!lisp_port))
+ goto error;
+
if (unlikely(!pskb_may_pull(skb, LISP_HLEN)))
goto error;
else
key = instance_id_to_tunnel_id(&lisph->u2.word2.instance_id[0]);
- iph = ip_hdr(skb);
- vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
- key, TNL_T_PROTO_LISP, &mutable);
- if (unlikely(!vport))
- goto error;
-
- if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
- tunnel_flags = OVS_TNL_F_KEY;
- else
- key = 0;
-
/* Save outer tunnel values */
- tnl_tun_key_init(&tun_key, iph, key, tunnel_flags);
+ iph = ip_hdr(skb);
+ tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY);
OVS_CB(skb)->tun_key = &tun_key;
/* Drop non-IP inner packets */
ethh->h_source[0] = 0x02;
ethh->h_proto = protocol;
- ovs_tnl_rcv(vport, skb);
+ ovs_tnl_rcv(lisp_port->vport, skb);
goto out;
error:
{
int err;
struct sockaddr_in sin;
+ struct tnl_vport *tnl_vport = tnl_vport_priv(lisp_port->vport);
err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
&lisp_port->lisp_rcv_socket);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = lisp_port->port;
+ sin.sin_port = tnl_vport->dst_port;
err = kernel_bind(lisp_port->lisp_rcv_socket, (struct sockaddr *)&sin,
sizeof(struct sockaddr_in));
return err;
}
-static void lisp_tunnel_release(struct lisp_port *lisp_port)
+
+static void free_port_rcu(struct rcu_head *rcu)
{
- lisp_port->count--;
+ struct lisp_port *lisp_port = container_of(rcu,
+ struct lisp_port, rcu);
- if (lisp_port->count == 0) {
- /* Release old socket */
- sk_release_kernel(lisp_port->lisp_rcv_socket->sk);
- list_del(&lisp_port->list);
- kfree(lisp_port);
- }
+ kfree(lisp_port);
}
-static int lisp_tunnel_setup(struct net *net, struct nlattr *options,
- struct lisp_port **lport)
+static void lisp_tunnel_release(struct lisp_port *lisp_port)
{
+ if (!lisp_port)
+ return;
+ list_del_rcu(&lisp_port->list);
+ /* Release socket */
+ sk_release_kernel(lisp_port->lisp_rcv_socket->sk);
+ call_rcu(&lisp_port->rcu, free_port_rcu);
+}
+
+static int lisp_tunnel_setup(struct net *net, struct vport *vport,
+ struct nlattr *options)
+{
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
+ struct lisp_port *lisp_port;
struct nlattr *a;
int err;
u16 dst_port;
- struct lisp_port *lisp_port = NULL;
-
- *lport = NULL;
if (!options) {
err = -EINVAL;
}
/* Verify if we already have a socket created for this port */
- lisp_port = lisp_port_exists(net, htons(dst_port));
+ lisp_port = lisp_find_port(net, htons(dst_port));
if (lisp_port) {
- lisp_port->count++;
- err = 0;
- *lport = lisp_port;
+ err = -EEXIST;
goto out;
}
goto out;
}
- lisp_port->port = htons(dst_port);
- lisp_port->count = 1;
- list_add_tail(&lisp_port->list, &lisp_ports);
+ tnl_vport->dst_port = htons(dst_port);
+ lisp_port->vport = vport;
+ list_add_tail_rcu(&lisp_port->list, &lisp_ports);
err = lisp_socket_init(lisp_port, net);
if (err)
goto error;
- *lport = lisp_port;
- goto out;
+ return 0;
error:
- list_del(&lisp_port->list);
+ list_del_rcu(&lisp_port->list);
kfree(lisp_port);
out:
return err;
}
-static int lisp_tnl_set_options(struct vport *vport, struct nlattr *options)
+static int lisp_get_options(const struct vport *vport, struct sk_buff *skb)
{
- int err;
- struct net *net = ovs_dp_get_net(vport->dp);
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *config;
- struct lisp_port *old_port = NULL;
- struct lisp_port *lisp_port = NULL;
-
- config = rtnl_dereference(tnl_vport->mutable);
-
- old_port = lisp_port_exists(net, config->dst_port);
+ const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- err = lisp_tunnel_setup(net, options, &lisp_port);
- if (err)
- goto out;
-
- err = ovs_tnl_set_options(vport, options);
-
- if (err)
- lisp_tunnel_release(lisp_port);
- else {
- /* Release old socket */
- lisp_tunnel_release(old_port);
- }
-out:
- return err;
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(tnl_vport->dst_port)))
+ return -EMSGSIZE;
+ return 0;
}
static const struct tnl_ops ovs_lisp_tnl_ops = {
- .tunnel_type = TNL_T_PROTO_LISP,
.ipproto = IPPROTO_UDP,
.hdr_len = lisp_hdr_len,
.build_header = lisp_build_header,
{
struct lisp_port *lisp_port;
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *config;
- config = rtnl_dereference(tnl_vport->mutable);
-
- lisp_port = lisp_port_exists(ovs_dp_get_net(vport->dp),
- config->dst_port);
+ lisp_port = lisp_find_port(ovs_dp_get_net(vport->dp),
+ tnl_vport->dst_port);
lisp_tunnel_release(lisp_port);
-
ovs_tnl_destroy(vport);
}
static struct vport *lisp_tnl_create(const struct vport_parms *parms)
{
- int err;
struct vport *vport;
- struct lisp_port *lisp_port = NULL;
-
- err = lisp_tunnel_setup(ovs_dp_get_net(parms->dp), parms->options,
- &lisp_port);
- if (err)
- return ERR_PTR(err);
+ int err;
vport = ovs_tnl_create(parms, &ovs_lisp_vport_ops, &ovs_lisp_tnl_ops);
-
if (IS_ERR(vport))
- lisp_tunnel_release(lisp_port);
+ return vport;
+
+ err = lisp_tunnel_setup(ovs_dp_get_net(parms->dp), vport,
+ parms->options);
+ if (err) {
+ ovs_tnl_destroy(vport);
+ return ERR_PTR(err);
+ }
return vport;
}
.create = lisp_tnl_create,
.destroy = lisp_tnl_destroy,
.get_name = ovs_tnl_get_name,
- .get_options = ovs_tnl_get_options,
- .set_options = lisp_tnl_set_options,
+ .get_options = lisp_get_options,
.send = lisp_tnl_send,
};
#else
#include <linux/in.h>
#include <linux/ip.h>
-#include <linux/list.h>
#include <linux/net.h>
+#include <linux/rculist.h>
#include <linux/udp.h>
#include <net/icmp.h>
#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr))
-static inline int vxlan_hdr_len(const struct tnl_mutable_config *mutable,
- const struct ovs_key_ipv4_tunnel *tun_key)
+static inline int vxlan_hdr_len(const struct ovs_key_ipv4_tunnel *tun_key)
{
return VXLAN_HLEN;
}
/**
* struct vxlan_port - Keeps track of open UDP ports
* @list: list element.
- * @port: The UDP port number in network byte order.
+ * @vport: vport for the tunnel.
* @socket: The socket created for this port number.
- * @count: How many ports are using this socket/port.
*/
struct vxlan_port {
struct list_head list;
- __be16 port;
+ struct vport *vport;
struct socket *vxlan_rcv_socket;
- int count;
+ struct rcu_head rcu;
};
static LIST_HEAD(vxlan_ports);
-static struct vxlan_port *vxlan_port_exists(struct net *net, __be16 port)
+static struct vxlan_port *vxlan_find_port(struct net *net, __be16 port)
{
struct vxlan_port *vxlan_port;
- list_for_each_entry(vxlan_port, &vxlan_ports, list) {
- if (vxlan_port->port == port &&
+ list_for_each_entry_rcu(vxlan_port, &vxlan_ports, list) {
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vxlan_port->vport);
+
+ if (tnl_vport->dst_port == port &&
net_eq(sock_net(vxlan_port->vxlan_rcv_socket->sk), net))
return vxlan_port;
}
return (struct vxlanhdr *)(udp_hdr(skb) + 1);
}
-static struct sk_buff *vxlan_build_header(const struct vport *vport,
- const struct tnl_mutable_config *mutable,
- struct dst_entry *dst,
- struct sk_buff *skb,
- int tunnel_hlen)
+static void vxlan_build_header(const struct vport *vport,
+ struct sk_buff *skb,
+ int tunnel_hlen)
{
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
struct udphdr *udph = udp_hdr(skb);
struct vxlanhdr *vxh = (struct vxlanhdr *)(udph + 1);
const struct ovs_key_ipv4_tunnel *tun_key = OVS_CB(skb)->tun_key;
- __be64 out_key;
- u32 flags;
- tnl_get_param(mutable, tun_key, &flags, &out_key);
-
- udph->dest = mutable->dst_port;
+ udph->dest = tnl_vport->dst_port;
udph->source = htons(ovs_tnl_get_src_port(skb));
udph->check = 0;
udph->len = htons(skb->len - skb_transport_offset(skb));
vxh->vx_flags = htonl(VXLAN_FLAGS);
- vxh->vx_vni = htonl(be64_to_cpu(out_key) << 8);
-
- /*
- * Allow our local IP stack to fragment the outer packet even if the
- * DF bit is set as a last resort. We also need to force selection of
- * an IP ID here because Linux will otherwise leave it at 0 if the
- * packet originally had DF set.
- */
- skb->local_df = 1;
- __ip_select_ident(ip_hdr(skb), dst, 0);
-
- return skb;
+ vxh->vx_vni = htonl(be64_to_cpu(tun_key->tun_id) << 8);
}
/* Called with rcu_read_lock and BH disabled. */
static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
{
- struct vport *vport;
+ struct vxlan_port *vxlan_vport;
struct vxlanhdr *vxh;
- const struct tnl_mutable_config *mutable;
struct iphdr *iph;
struct ovs_key_ipv4_tunnel tun_key;
__be64 key;
- u32 tunnel_flags = 0;
+
+ vxlan_vport = vxlan_find_port(dev_net(skb->dev), udp_hdr(skb)->dest);
+ if (unlikely(!vxlan_vport))
+ goto error;
if (unlikely(!pskb_may_pull(skb, VXLAN_HLEN + ETH_HLEN)))
goto error;
key = cpu_to_be64(ntohl(vxh->vx_vni) >> 8);
- iph = ip_hdr(skb);
- vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr,
- key, TNL_T_PROTO_VXLAN, &mutable);
- if (unlikely(!vport))
- goto error;
-
- if (mutable->flags & TNL_F_IN_KEY_MATCH || !mutable->key.daddr)
- tunnel_flags = OVS_TNL_F_KEY;
- else
- key = 0;
-
/* Save outer tunnel values */
- tnl_tun_key_init(&tun_key, iph, key, tunnel_flags);
+ iph = ip_hdr(skb);
+ tnl_tun_key_init(&tun_key, iph, key, OVS_TNL_F_KEY);
OVS_CB(skb)->tun_key = &tun_key;
- ovs_tnl_rcv(vport, skb);
+ ovs_tnl_rcv(vxlan_vport->vport, skb);
goto out;
error:
{
int err;
struct sockaddr_in sin;
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vxlan_port->vport);
err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
&vxlan_port->vxlan_rcv_socket);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = vxlan_port->port;
+ sin.sin_port = tnl_vport->dst_port;
err = kernel_bind(vxlan_port->vxlan_rcv_socket, (struct sockaddr *)&sin,
sizeof(struct sockaddr_in));
return err;
}
+static void free_port_rcu(struct rcu_head *rcu)
+{
+ struct vxlan_port *vxlan_port = container_of(rcu,
+ struct vxlan_port, rcu);
+
+ kfree(vxlan_port);
+}
+
static void vxlan_tunnel_release(struct vxlan_port *vxlan_port)
{
- vxlan_port->count--;
+ if (!vxlan_port)
+ return;
- if (vxlan_port->count == 0) {
- /* Release old socket */
- sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk);
- list_del(&vxlan_port->list);
- kfree(vxlan_port);
- }
+ list_del_rcu(&vxlan_port->list);
+ /* Release socket */
+ sk_release_kernel(vxlan_port->vxlan_rcv_socket->sk);
+ call_rcu(&vxlan_port->rcu, free_port_rcu);
}
-static int vxlan_tunnel_setup(struct net *net, struct nlattr *options,
- struct vxlan_port **vxport)
+
+static int vxlan_tunnel_setup(struct net *net, struct vport *vport,
+ struct nlattr *options)
{
+ struct vxlan_port *vxlan_port;
+ struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
struct nlattr *a;
int err;
u16 dst_port;
- struct vxlan_port *vxlan_port = NULL;
-
- *vxport = NULL;
if (!options) {
err = -EINVAL;
}
/* Verify if we already have a socket created for this port */
- vxlan_port = vxlan_port_exists(net, htons(dst_port));
+ vxlan_port = vxlan_find_port(net, htons(dst_port));
if (vxlan_port) {
- vxlan_port->count++;
- err = 0;
- *vxport = vxlan_port;
+ err = -EEXIST;
goto out;
}
goto out;
}
- vxlan_port->port = htons(dst_port);
- vxlan_port->count = 1;
- list_add_tail(&vxlan_port->list, &vxlan_ports);
+ tnl_vport->dst_port = htons(dst_port);
+ vxlan_port->vport = vport;
+ list_add_tail_rcu(&vxlan_port->list, &vxlan_ports);
err = vxlan_socket_init(vxlan_port, net);
if (err)
goto error;
- *vxport = vxlan_port;
- goto out;
+ return 0;
error:
- list_del(&vxlan_port->list);
+ list_del_rcu(&vxlan_port->list);
kfree(vxlan_port);
out:
return err;
}
-static int vxlan_set_options(struct vport *vport, struct nlattr *options)
+static int vxlan_get_options(const struct vport *vport, struct sk_buff *skb)
{
- int err;
- struct net *net = ovs_dp_get_net(vport->dp);
- struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *config;
- struct vxlan_port *old_port = NULL;
- struct vxlan_port *vxlan_port = NULL;
-
- config = rtnl_dereference(tnl_vport->mutable);
-
- old_port = vxlan_port_exists(net, config->dst_port);
-
- err = vxlan_tunnel_setup(net, options, &vxlan_port);
- if (err)
- goto out;
-
- err = ovs_tnl_set_options(vport, options);
+ const struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- if (err)
- vxlan_tunnel_release(vxlan_port);
- else {
- /* Release old socket */
- vxlan_tunnel_release(old_port);
- }
-out:
- return err;
+ if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(tnl_vport->dst_port)))
+ return -EMSGSIZE;
+ return 0;
}
static const struct tnl_ops ovs_vxlan_tnl_ops = {
- .tunnel_type = TNL_T_PROTO_VXLAN,
.ipproto = IPPROTO_UDP,
.hdr_len = vxlan_hdr_len,
.build_header = vxlan_build_header,
{
struct vxlan_port *vxlan_port;
struct tnl_vport *tnl_vport = tnl_vport_priv(vport);
- struct tnl_mutable_config *config;
-
- config = rtnl_dereference(tnl_vport->mutable);
- vxlan_port = vxlan_port_exists(ovs_dp_get_net(vport->dp),
- config->dst_port);
+ vxlan_port = vxlan_find_port(ovs_dp_get_net(vport->dp),
+ tnl_vport->dst_port);
vxlan_tunnel_release(vxlan_port);
-
ovs_tnl_destroy(vport);
}
{
int err;
struct vport *vport;
- struct vxlan_port *vxlan_port = NULL;
-
- err = vxlan_tunnel_setup(ovs_dp_get_net(parms->dp), parms->options,
- &vxlan_port);
- if (err)
- return ERR_PTR(err);
vport = ovs_tnl_create(parms, &ovs_vxlan_vport_ops, &ovs_vxlan_tnl_ops);
-
if (IS_ERR(vport))
- vxlan_tunnel_release(vxlan_port);
+ return vport;
+
+ err = vxlan_tunnel_setup(ovs_dp_get_net(parms->dp), vport,
+ parms->options);
+ if (err) {
+ ovs_tnl_destroy(vport);
+ return ERR_PTR(err);
+ }
return vport;
}
.create = vxlan_tnl_create,
.destroy = vxlan_tnl_destroy,
.get_name = ovs_tnl_get_name,
- .get_options = ovs_tnl_get_options,
- .set_options = vxlan_set_options,
+ .get_options = vxlan_get_options,
.send = ovs_tnl_send,
};
#else
{
struct hlist_head *bucket = hash_bucket(net, name);
struct vport *vport;
- struct hlist_node *node;
- hlist_for_each_entry_rcu(vport, node, bucket, hash_node)
+ hlist_for_each_entry_rcu(vport, bucket, hash_node)
if (!strcmp(name, vport->ops->get_name(vport)) &&
net_eq(ovs_dp_get_net(vport->dp), net))
return vport;
int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
{
struct nlattr *nla;
+ int err;
+
+ if (!vport->ops->get_options)
+ return 0;
nla = nla_nest_start(skb, OVS_VPORT_ATTR_OPTIONS);
if (!nla)
return -EMSGSIZE;
- if (vport->ops->get_options) {
- int err = vport->ops->get_options(vport, skb);
- if (err) {
- nla_nest_cancel(skb, nla);
- return err;
- }
+ err = vport->ops->get_options(vport, skb);
+ if (err) {
+ nla_nest_cancel(skb, nla);
+ return err;
}
nla_nest_end(skb, nla);
stats->rx_bytes += skb->len;
u64_stats_update_end(&stats->sync);
- if (!(vport->ops->flags & VPORT_F_FLOW))
- OVS_CB(skb)->flow = NULL;
-
if (!(vport->ops->flags & VPORT_F_TUN_ID))
OVS_CB(skb)->tun_key = NULL;
struct vport_parms;
/* The following definitions are for users of the vport subsytem: */
+struct vport_net {
+ struct vport __rcu *gre_vport;
+ struct vport __rcu *gre64_vport;
+};
int ovs_vport_init(void);
void ovs_vport_exit(void);
};
#define VPORT_F_REQUIRED (1 << 0) /* If init fails, module loading fails. */
-#define VPORT_F_FLOW (1 << 1) /* Sets OVS_CB(skb)->flow. */
-#define VPORT_F_TUN_ID (1 << 2) /* Sets OVS_CB(skb)->tun_id. */
+#define VPORT_F_TUN_ID (1 << 1) /* Sets OVS_CB(skb)->tun_id. */
/**
* struct vport_parms - parameters for creating a new vport
- The data in the RARP packets can now be matched in the same way as the
data in ARP packets.
- -- Open vSwitch team <dev@openvswitch.org> Wed, 24 Oct 2012 16:10:39 -0700
+ -- Open vSwitch team <dev@openvswitch.org> Tue, 26 Feb 2013 11:23:19 -0700
openvswitch (1.8.0-1) unstable; urgency=low
[ Open vSwitch team ]
* New upstream version
+ *** Internal only release ***
- New FAQ. Please send updates and additions!
- Authors of controllers, please read the new section titled "Action
Reproduction" in DESIGN, which describes an Open vSwitch change in
include/linux/openvswitch.h
include/openvswitch/datapath-compat.h
- include/openvswitch/tunnel.h
On Debian systems, the complete text of the GNU General Public License
version 2 can be found in `/usr/share/common-licenses/GPL-2'.
#! /bin/sh
-# Copyright (c) 2012 Nicira, Inc.
+# Copyright (c) 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
[ -n "${bridges}" ] && $1 --allow=ovs ${bridges}
}
-ovs_ctl () {
- set /usr/share/openvswitch/scripts/ovs-ctl "$@"
- "$@"
-}
-
load_kmod () {
ovs_ctl load-kmod || exit $?
}
noinst_HEADERS += \
+ include/linux/if_ether.h \
include/linux/openvswitch.h \
include/linux/types.h
--- /dev/null
+/*
+ * Copyright (c) 2013 Nicira, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LINUX_IF_ETHER_H
+#define LINUX_IF_ETHER_H 1
+
+/* On Linux, this header file just includes <linux/if_ether.h>.
+ *
+ * On other platforms, this header file implements just enough of
+ * <linux/if_ether.h> to allow <linux/openvswitch.h> to work. */
+
+#if defined(HAVE_LINUX_IF_ETHER_H) || defined(__KERNEL__)
+#include_next <linux/if_ether.h>
+#else /* no <linux/if_ether.h> */
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#endif
+
+#endif /* <linux/if_ether.h> */
#define _LINUX_OPENVSWITCH_H 1
#include <linux/types.h>
+#include <linux/if_ether.h>
/**
* struct ovs_header - header for OVS Generic Netlink messages.
#define OVS_VPORT_ATTR_MAX (__OVS_VPORT_ATTR_MAX - 1)
-/* OVS_VPORT_ATTR_OPTIONS attributes for patch vports. */
+/* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
+ */
enum {
- OVS_PATCH_ATTR_UNSPEC,
- OVS_PATCH_ATTR_PEER, /* name of peer vport, as a string */
- __OVS_PATCH_ATTR_MAX
+ OVS_TUNNEL_ATTR_UNSPEC,
+ OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by L4 tunnels. */
+ __OVS_TUNNEL_ATTR_MAX
};
-#define OVS_PATCH_ATTR_MAX (__OVS_PATCH_ATTR_MAX - 1)
+#define OVS_TUNNEL_ATTR_MAX (__OVS_TUNNEL_ATTR_MAX - 1)
/* Flows. */
#endif
OVS_KEY_ATTR_MPLS = 62, /* struct ovs_key_mpls */
- OVS_KEY_ATTR_TUN_ID = 63, /* be64 tunnel ID */
__OVS_KEY_ATTR_MAX
};
#define OVS_FRAG_TYPE_MAX (__OVS_FRAG_TYPE_MAX - 1)
struct ovs_key_ethernet {
- __u8 eth_src[6];
- __u8 eth_dst[6];
+ __u8 eth_src[ETH_ALEN];
+ __u8 eth_dst[ETH_ALEN];
};
struct ovs_key_mpls {
__be32 arp_sip;
__be32 arp_tip;
__be16 arp_op;
- __u8 arp_sha[6];
- __u8 arp_tha[6];
+ __u8 arp_sha[ETH_ALEN];
+ __u8 arp_tha[ETH_ALEN];
};
struct ovs_key_nd {
__u32 nd_target[4];
- __u8 nd_sll[6];
- __u8 nd_tll[6];
+ __u8 nd_sll[ETH_ALEN];
+ __u8 nd_tll[ETH_ALEN];
};
/**
/* This command enables or disables an Open vSwitch extension that allows a
* controller to specify the OpenFlow table to which a flow should be added,
* instead of having the switch decide which table is most appropriate as
- * required by OpenFlow 1.0. By default, the extension is disabled.
+ * required by OpenFlow 1.0. Because NXM was designed as an extension to
+ * OpenFlow 1.0, the extension applies equally to ofp10_flow_mod and
+ * nx_flow_mod. By default, the extension is disabled.
*
* When this feature is enabled, Open vSwitch treats struct ofp10_flow_mod's
- * 16-bit 'command' member as two separate fields. The upper 8 bits are used
- * as the table ID, the lower 8 bits specify the command as usual. A table ID
- * of 0xff is treated like a wildcarded table ID.
+ * and struct nx_flow_mod's 16-bit 'command' member as two separate fields.
+ * The upper 8 bits are used as the table ID, the lower 8 bits specify the
+ * command as usual. A table ID of 0xff is treated like a wildcarded table ID.
*
* The specific treatment of the table ID depends on the type of flow mod:
*
NXAST_WRITE_METADATA, /* struct nx_action_write_metadata */
NXAST_PUSH_MPLS, /* struct nx_action_push_mpls */
NXAST_POP_MPLS, /* struct nx_action_pop_mpls */
+ NXAST_SET_MPLS_TTL, /* struct nx_action_ttl */
+ NXAST_DEC_MPLS_TTL, /* struct nx_action_header */
+ NXAST_STACK_PUSH, /* struct nx_action_stack */
+ NXAST_STACK_POP, /* struct nx_action_stack */
};
/* Header for Nicira-defined actions. */
};
OFP_ASSERT(sizeof(struct nx_action_reg_load) == 24);
+/* Action structure for NXAST_STACK_PUSH and NXAST_STACK_POP.
+ *
+ * Pushes (or pops) field[offset: offset + n_bits] to (or from)
+ * top of the stack.
+ */
+struct nx_action_stack {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 16. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_STACK_PUSH or NXAST_STACK_POP. */
+ ovs_be16 offset; /* Bit offset into the field. */
+ ovs_be32 field; /* The field used for push or pop. */
+ ovs_be16 n_bits; /* (n_bits + 1) bits of the field. */
+ uint8_t zero[6]; /* Reserved, must be zero. */
+};
+OFP_ASSERT(sizeof(struct nx_action_stack) == 24);
+
/* Action structure for NXAST_NOTE.
*
* This action has no effect. It is variable length. The switch does not
*/
struct nx_flow_mod {
ovs_be64 cookie; /* Opaque controller-issued identifier. */
- ovs_be16 command; /* One of OFPFC_*. */
+ ovs_be16 command; /* OFPFC_* + possibly a table ID (see comment
+ * on struct nx_flow_mod_table_id). */
ovs_be16 idle_timeout; /* Idle time before discarding (seconds). */
ovs_be16 hard_timeout; /* Max time before discarding (seconds). */
ovs_be16 priority; /* Priority level of flow entry. */
};
OFP_ASSERT(sizeof(struct nx_flow_mod) == 32);
-/* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED). */
+/* NXT_FLOW_REMOVED (analogous to OFPT_FLOW_REMOVED).
+ *
+ * 'table_id' is present only in Open vSwitch 1.11 and later. In earlier
+ * versions of Open vSwitch, this is a padding byte that is always zeroed.
+ * Therefore, a 'table_id' value of 0 indicates that the table ID is not known,
+ * and other values may be interpreted as one more than the flow's former table
+ * ID. */
struct nx_flow_removed {
ovs_be64 cookie; /* Opaque controller-issued identifier. */
ovs_be16 priority; /* Priority level of flow entry. */
uint8_t reason; /* One of OFPRR_*. */
- uint8_t pad[1]; /* Align to 32-bits. */
+ uint8_t table_id; /* Flow's former table ID, plus one. */
ovs_be32 duration_sec; /* Time flow was alive in seconds. */
ovs_be32 duration_nsec; /* Time flow was alive in nanoseconds beyond
duration_sec. */
};
OFP_ASSERT(sizeof(struct nx_action_pop_mpls) == 16);
+/* Action structure for NXAST_SET_MPLS_TTL. */
+struct nx_action_mpls_ttl {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* Length is 8. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_SET_MPLS_TTL. */
+ uint8_t ttl; /* TTL */
+ uint8_t pad[5];
+};
+OFP_ASSERT(sizeof(struct nx_action_mpls_ttl) == 16);
+
#endif /* openflow/nicira-ext.h */
enum ofp13_action_type {
OFPAT13_PUSH_PBB = 26, /* Push a new PBB service tag (I-TAG) */
- OFPAT13_PPO_PBB = 27 /* Pop the outer PBB service tag (I-TAG) */
+ OFPAT13_POP_PBB = 27 /* Pop the outer PBB service tag (I-TAG) */
};
/* enum ofp_config_flags value OFPC_INVALID_TTL_TO_CONTROLLER
noinst_HEADERS += \
include/openvswitch/datapath-compat.h \
- include/openvswitch/tunnel.h \
include/openvswitch/types.h
+++ /dev/null
-/*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
- *
- * This file is offered under your choice of two licenses: Apache 2.0 or GNU
- * GPL 2.0 or later. The permission statements for each of these licenses is
- * given below. You may license your modifications to this file under either
- * of these licenses or both. If you wish to license your modifications under
- * only one of these licenses, delete the permission text for the other
- * 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 obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ----------------------------------------------------------------------
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- * ----------------------------------------------------------------------
- */
-
-#ifndef OPENVSWITCH_TUNNEL_H
-#define OPENVSWITCH_TUNNEL_H 1
-
-#include <linux/types.h>
-#include <linux/openvswitch.h>
-
-/* OVS_VPORT_ATTR_OPTIONS attributes for tunnels.
- *
- * OVS_TUNNEL_ATTR_DST_IPV4 is required for kernel tunnel ports, all other
- * attributes are optional.
- * For flow-based tunnels, only the OVS_TUNNEL_ATTR_DST_PORT is useful.
- */
-enum {
- OVS_TUNNEL_ATTR_UNSPEC,
- OVS_TUNNEL_ATTR_FLAGS, /* 32-bit TNL_F_*. */
- OVS_TUNNEL_ATTR_DST_IPV4, /* Remote IPv4 address. */
- OVS_TUNNEL_ATTR_SRC_IPV4, /* Local IPv4 address. */
- OVS_TUNNEL_ATTR_OUT_KEY, /* __be64 key to use on output. */
- OVS_TUNNEL_ATTR_IN_KEY, /* __be64 key to match on input. */
- OVS_TUNNEL_ATTR_TOS, /* 8-bit TOS value. */
- OVS_TUNNEL_ATTR_TTL, /* 8-bit TTL value. */
- OVS_TUNNEL_ATTR_DST_PORT, /* 16-bit UDP port, used by VXLAN. */
- __OVS_TUNNEL_ATTR_MAX
-};
-
-#define OVS_TUNNEL_ATTR_MAX (__OVS_TUNNEL_ATTR_MAX - 1)
-
-#define TNL_F_CSUM (1 << 0) /* Checksum packets. */
-#define TNL_F_TOS_INHERIT (1 << 1) /* Inherit ToS from inner packet. */
-#define TNL_F_TTL_INHERIT (1 << 2) /* Inherit TTL from inner packet. */
-/* Bit 3 was previously used for Don't Fragment inheritance. " */
-#define TNL_F_DF_DEFAULT (1 << 4) /* Set DF bit if inherit off or
- * not IP. */
-/* Bit 5 was previously used for path MTU discovery. " */
-/* Bit 6 is reserved since it was previously used for Tunnel header caching. */
-/* Bit 7 was previously used for IPsec tunnel ports. */
-
-#endif /* openvswitch/tunnel.h */
lib/ovsdb-types.h \
lib/packets.c \
lib/packets.h \
- lib/pcap.c \
- lib/pcap.h \
+ lib/pcap-file.c \
+ lib/pcap-file.h \
lib/poll-loop.c \
lib/poll-loop.h \
lib/process.c \
#include "odp-util.h"
#include "ofpbuf.h"
#include "openvswitch/datapath-compat.h"
-#include "openvswitch/tunnel.h"
#include "packets.h"
#include "poll-loop.h"
#include "random.h"
const struct ovs_key_udp *udp_key;
switch (type) {
- case OVS_KEY_ATTR_TUN_ID:
case OVS_KEY_ATTR_PRIORITY:
case OVS_KEY_ATTR_SKB_MARK:
case OVS_KEY_ATTR_TUNNEL:
parse_mpls(&b, flow);
}
- packet->l3 = b.data;
- flow_extract_l3_onwards(packet, flow, flow->dl_type);
-}
-
-/* Initializes l3 and higher 'flow' members from 'packet'
- *
- * This should be called by or after flow_extract()
- *
- * Initializes 'packet' header pointers as follows:
- *
- * - packet->l4 to just past the IPv4 header, if one is present and has a
- * correct length, and otherwise NULL.
- *
- * - packet->l7 to just past the TCP or UDP or ICMP header, if one is
- * present and has a correct length, and otherwise NULL.
- */
-void
-flow_extract_l3_onwards(struct ofpbuf *packet, struct flow *flow,
- ovs_be16 dl_type)
-{
- struct ofpbuf b;
-
- ofpbuf_use_const(&b, packet->l3, packet->size -
- (size_t)((char *)packet->l3 - (char *)packet->l2));
-
/* Network layer. */
- if (dl_type == htons(ETH_TYPE_IP)) {
+ packet->l3 = b.data;
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
const struct ip_header *nh = pull_ip(&b);
if (nh) {
packet->l4 = b.data;
}
}
}
- } else if (dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
if (parse_ipv6(&b, flow)) {
return;
}
packet->l7 = b.data;
}
}
- } else if (dl_type == htons(ETH_TYPE_ARP) ||
- dl_type == htons(ETH_TYPE_RARP)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
const struct arp_eth_header *arp = pull_arp(&b);
if (arp && arp->ar_hrd == htons(1)
&& arp->ar_pro == htons(ETH_TYPE_IP)
void
flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
fmd->tun_id = flow->tunnel.tun_id;
fmd->metadata = flow->metadata;
set_mpls_lse_label(&flow->mpls_lse, label);
}
+/* Sets the MPLS TTL that 'flow' matches to 'ttl', which should be in the
+ * range 0...255. */
+void
+flow_set_mpls_ttl(struct flow *flow, uint8_t ttl)
+{
+ set_mpls_lse_ttl(&flow->mpls_lse, ttl);
+}
+
/* Sets the MPLS TC that 'flow' matches to 'tc', which should be in the
* range 0...7. */
void
void
flow_compose(struct ofpbuf *b, const struct flow *flow)
{
- ovs_be16 inner_dl_type;
-
- inner_dl_type = flow_innermost_dl_type(flow);
eth_compose(b, flow->dl_dst, flow->dl_src, ntohs(flow->dl_type), 0);
if (flow->dl_type == htons(FLOW_DL_TYPE_NONE)) {
struct eth_header *eth = b->l2;
eth_push_vlan(b, flow->vlan_tci);
}
- if (inner_dl_type == htons(ETH_TYPE_IP)) {
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
struct ip_header *ip;
b->l3 = ip = ofpbuf_put_zeros(b, sizeof *ip);
ip->ip_tot_len = htons((uint8_t *) b->data + b->size
- (uint8_t *) b->l3);
ip->ip_csum = csum(ip, sizeof *ip);
- } else if (inner_dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
/* XXX */
- } else if (inner_dl_type == htons(ETH_TYPE_ARP) ||
- inner_dl_type == htons(ETH_TYPE_RARP)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
struct arp_eth_header *arp;
b->l3 = arp = ofpbuf_put_zeros(b, sizeof *arp);
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 19
+#define FLOW_WC_SEQ 20
#define FLOW_N_REGS 8
BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
uint16_t mpls_depth; /* Depth of MPLS stack. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be16 dl_type; /* Ethernet frame type. */
- ovs_be16 encap_dl_type; /* MPLS encapsulated Ethernet frame type */
ovs_be16 tp_src; /* TCP/UDP source port. */
ovs_be16 tp_dst; /* TCP/UDP destination port. */
uint8_t dl_src[6]; /* Ethernet source address. */
uint8_t arp_tha[6]; /* ARP/ND target hardware address. */
uint8_t nw_ttl; /* IP TTL/Hop Limit. */
uint8_t nw_frag; /* FLOW_FRAG_* flags. */
- uint8_t zeros[4];
+ uint8_t zeros[6];
};
BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(sizeof(struct flow) == sizeof(struct flow_tnl) + 160 &&
- FLOW_WC_SEQ == 19);
+ FLOW_WC_SEQ == 20);
/* Represents the metadata fields of struct flow. */
struct flow_metadata {
void flow_extract(struct ofpbuf *, uint32_t priority, uint32_t mark,
const struct flow_tnl *, uint16_t in_port, struct flow *);
-void flow_extract_l3_onwards(struct ofpbuf *, struct flow *,
- ovs_be16 dl_type);
-
-/* Returns the innermost dl_type.
- * If there's an outer and an inner type then the inner type is returned.
- * Otherwise, if there is only one type then it is returned. */
-static inline ovs_be16
-flow_innermost_dl_type(const struct flow *flow)
-{
- return (flow->encap_dl_type == htons(0)
- ? flow->dl_type
- : flow->encap_dl_type);
-}
void flow_zero_wildcards(struct flow *, const struct flow_wildcards *);
void flow_get_metadata(const struct flow *, struct flow_metadata *);
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
ds_put_char(ds, '"');
}
+\f
+static size_t
+json_string_serialized_length(const char *string)
+{
+ size_t length;
+ uint8_t c;
+
+ length = strlen("\"\"");
+
+ while ((c = *string++) != '\0') {
+ switch (c) {
+ case '"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ length += 2;
+ break;
+
+ default:
+ if (c >= 32) {
+ length++;
+ } else {
+ /* \uXXXX */
+ length += 6;
+ }
+ break;
+ }
+ }
+
+ return length;
+}
+
+static size_t
+json_object_serialized_length(const struct shash *object)
+{
+ size_t length = strlen("{}");
+
+ if (!shash_is_empty(object)) {
+ struct shash_node *node;
+
+ /* Commas and colons. */
+ length += 2 * shash_count(object) - 1;
+
+ SHASH_FOR_EACH (node, object) {
+ const struct json *value = node->data;
+
+ length += json_string_serialized_length(node->name);
+ length += json_serialized_length(value);
+ }
+ }
+
+ return length;
+}
+
+static size_t
+json_array_serialized_length(const struct json_array *array)
+{
+ size_t length = strlen("[]");
+
+ if (array->n) {
+ size_t i;
+
+ /* Commas. */
+ length += array->n - 1;
+
+ for (i = 0; i < array->n; i++) {
+ length += json_serialized_length(array->elems[i]);
+ }
+ }
+
+ return length;
+}
+
+/* Returns strlen(json_to_string(json, 0)), that is, the number of bytes in the
+ * JSON output by json_to_string() for 'json' when JSSF_PRETTY is not
+ * requested. (JSSF_SORT does not affect the length of json_to_string()'s
+ * output.) */
+size_t
+json_serialized_length(const struct json *json)
+{
+ switch (json->type) {
+ case JSON_NULL:
+ return strlen("null");
+
+ case JSON_FALSE:
+ return strlen("false");
+
+ case JSON_TRUE:
+ return strlen("true");
+
+ case JSON_OBJECT:
+ return json_object_serialized_length(json->u.object);
+
+ case JSON_ARRAY:
+ return json_array_serialized_length(&json->u.array);
+
+ case JSON_INTEGER:
+ return snprintf(NULL, 0, "%lld", json->u.integer);
+
+ case JSON_REAL:
+ return snprintf(NULL, 0, "%.*g", DBL_DIG, json->u.real);
+
+ case JSON_STRING:
+ return json_string_serialized_length(json->u.string);
+
+ case JSON_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+}
/*
- * Copyright (c) 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
};
char *json_to_string(const struct json *, int flags);
void json_to_ds(const struct json *, int flags, struct ds *);
+
+size_t json_serialized_length(const struct json *);
\f
/* JSON string formatting operations. */
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct jsonrpc *rpc;
struct stream *stream;
struct pstream *pstream;
+ int last_error;
unsigned int seqno;
uint8_t dscp;
};
* acceptable to stream_open() or pstream_open().
*
* If 'name' is an active connection method, e.g. "tcp:127.1.2.3", the new
- * jsonrpc_session connects and reconnects, with back-off, to 'name'.
+ * jsonrpc_session connects to 'name'. If 'retry' is true, then the new
+ * session connects and reconnects to 'name', with backoff. If 'retry' is
+ * false, the new session will only try to connect once and after a connection
+ * failure or a disconnection jsonrpc_session_is_alive() will return false for
+ * the new session.
*
* If 'name' is a passive connection method, e.g. "ptcp:", the new
* jsonrpc_session listens for connections to 'name'. It maintains at most one
* connection at any given time. Any new connection causes the previous one
* (if any) to be dropped. */
struct jsonrpc_session *
-jsonrpc_session_open(const char *name)
+jsonrpc_session_open(const char *name, bool retry)
{
struct jsonrpc_session *s;
s->pstream = NULL;
s->seqno = 0;
s->dscp = 0;
+ s->last_error = 0;
if (!pstream_verify_name(name)) {
reconnect_set_passive(s->reconnect, true, time_msec());
+ } else if (!retry) {
+ reconnect_set_max_tries(s->reconnect, 1);
+ reconnect_set_backoff(s->reconnect, INT_MAX, INT_MAX);
}
if (!stream_or_pstream_needs_probes(name)) {
error = jsonrpc_stream_open(name, &s->stream, s->dscp);
if (!error) {
reconnect_connecting(s->reconnect, time_msec());
+ } else {
+ s->last_error = error;
}
} else {
error = s->pstream ? 0 : jsonrpc_pstream_open(name, &s->pstream,
if (error) {
reconnect_disconnected(s->reconnect, time_msec(), error);
jsonrpc_session_disconnect(s);
+ s->last_error = error;
}
} else if (s->stream) {
int error;
return s && s->rpc ? jsonrpc_get_status(s->rpc) : 0;
}
+int
+jsonrpc_session_get_last_error(const struct jsonrpc_session *s)
+{
+ return s->last_error;
+}
+
void
jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *s,
struct reconnect_stats *stats)
/*
- * Copyright (c) 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
\f
/* A JSON-RPC session with reconnection. */
-struct jsonrpc_session *jsonrpc_session_open(const char *name);
+struct jsonrpc_session *jsonrpc_session_open(const char *name, bool retry);
struct jsonrpc_session *jsonrpc_session_open_unreliably(struct jsonrpc *,
uint8_t);
void jsonrpc_session_close(struct jsonrpc_session *);
bool jsonrpc_session_is_connected(const struct jsonrpc_session *);
unsigned int jsonrpc_session_get_seqno(const struct jsonrpc_session *);
int jsonrpc_session_get_status(const struct jsonrpc_session *);
+int jsonrpc_session_get_last_error(const struct jsonrpc_session *);
void jsonrpc_session_get_reconnect_stats(const struct jsonrpc_session *,
struct reconnect_stats *);
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "priority=%u,", priority);
unsigned int change_seq;
struct list devs; /* List of child "netdev_dummy"s. */
+ int ifindex;
};
struct netdev_dummy {
netdev_dev->mtu = 1500;
netdev_dev->flags = 0;
netdev_dev->change_seq = 1;
+ netdev_dev->ifindex = -EOPNOTSUPP;
list_init(&netdev_dev->devs);
shash_add(&dummy_netdev_devs, name, netdev_dev);
free(netdev_dev);
}
+static int
+netdev_dummy_get_config(struct netdev_dev *netdev_dev_, struct smap *args)
+{
+ struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
+
+ if (netdev_dev->ifindex >= 0) {
+ smap_add_format(args, "ifindex", "%d", netdev_dev->ifindex);
+ }
+ return 0;
+}
+
+static int
+netdev_dummy_set_config(struct netdev_dev *netdev_dev_,
+ const struct smap *args)
+{
+ struct netdev_dev_dummy *netdev_dev = netdev_dev_dummy_cast(netdev_dev_);
+
+ netdev_dev->ifindex = smap_get_int(args, "ifindex", -EOPNOTSUPP);
+ return 0;
+}
+
static int
netdev_dummy_open(struct netdev_dev *netdev_dev_, struct netdev **netdevp)
{
return 0;
}
+static int
+netdev_dummy_get_ifindex(const struct netdev *netdev)
+{
+ struct netdev_dev_dummy *dev =
+ netdev_dev_dummy_cast(netdev_get_dev(netdev));
+
+ return dev->ifindex;
+}
+
static int
netdev_dummy_update_flags(struct netdev *netdev,
enum netdev_flags off, enum netdev_flags on,
netdev_dummy_create,
netdev_dummy_destroy,
- NULL, /* get_config */
- NULL, /* set_config */
+ netdev_dummy_get_config,
+ netdev_dummy_set_config,
NULL, /* get_tunnel_config */
netdev_dummy_open,
netdev_dummy_get_etheraddr,
netdev_dummy_get_mtu,
netdev_dummy_set_mtu,
- NULL, /* get_ifindex */
+ netdev_dummy_get_ifindex,
NULL, /* get_carrier */
NULL, /* get_carrier_resets */
NULL, /* get_miimon */
sock = af_packet_sock();
if (sock < 0) {
- return sock;
+ return -sock;
}
error = get_ifindex(netdev_, &ifindex);
struct in_addr in_addr;
if (lookup_ip(node->value, &in_addr)) {
VLOG_WARN("%s: bad %s 'remote_ip'", name, type);
+ } else if (ip_is_multicast(in_addr.s_addr)) {
+ VLOG_WARN("%s: multicast remote_ip="IP_FMT" not allowed",
+ name, IP_ARGS(in_addr.s_addr));
+ return EINVAL;
} else {
tnl_cfg.ip_dst = in_addr.s_addr;
}
name, type);
return EINVAL;
}
-
- if (tnl_cfg.ip_src) {
- if (ip_is_multicast(tnl_cfg.ip_dst)) {
- VLOG_WARN("%s: remote_ip is multicast, ignoring local_ip", name);
- tnl_cfg.ip_src = 0;
- }
- }
-
if (!tnl_cfg.ttl) {
tnl_cfg.ttl = DEFAULT_TTL;
}
if (tnl_cfg->dst_port) {
uint16_t dst_port = ntohs(tnl_cfg->dst_port);
- if (dst_port != VXLAN_DST_PORT) {
+ const char *type = netdev_dev_get_type(dev);
+
+ if ((!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
+ (!strcmp("lisp", type) && dst_port != LISP_DST_PORT)) {
smap_add_format(args, "dst_port", "%d", dst_port);
}
}
}
}
-/* Returns true if a network device named 'name' exists and may be opened,
- * otherwise false. */
-bool
-netdev_exists(const char *name)
-{
- struct netdev *netdev;
- int error;
-
- error = netdev_open(name, "system", &netdev);
- if (!error) {
- netdev_close(netdev);
- return true;
- } else {
- if (error != ENODEV) {
- VLOG_WARN("failed to open network device %s: %s",
- name, strerror(error));
- }
- return false;
- }
-}
-
-/* Returns true if a network device named 'name' is currently opened,
- * otherwise false. */
-bool
-netdev_is_open(const char *name)
-{
- return !!shash_find_data(&netdev_dev_shash, name);
-}
-
/* Parses 'netdev_name_', which is of the form [type@]name into its component
* pieces. 'name' and 'type' must be freed by the caller. */
void
extern "C" {
#endif
-/* Generic interface to network devices.
+/* Generic interface to network devices ("netdev"s).
*
- * Currently, there is a single implementation of this interface that supports
- * Linux. The interface should be generic enough to be implementable on other
- * operating systems as well. */
+ * Every port on a switch must have a corresponding netdev that must minimally
+ * support a few operations, such as the ability to read the netdev's MTU.
+ * The PORTING file at the top of the source tree has more information in the
+ * "Writing a netdev Provider" section. */
struct ofpbuf;
struct in_addr;
int netdev_open(const char *name, const char *type, struct netdev **);
void netdev_close(struct netdev *);
-bool netdev_exists(const char *name);
-bool netdev_is_open(const char *name);
-
void netdev_parse_name(const char *netdev_name, char **name, char **type);
/* Options. */
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
/* Metadata. */
if (match->wc.masks.in_port) {
struct ofpbuf *openflow)
{
const struct mf_field *mf = load->dst.field;
+ uint16_t padded_value_len = ROUND_UP(mf->n_bytes, 8);
struct ofp12_action_set_field *oasf;
- uint16_t padded_value_len;
-
- oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
- oasf->dst = htonl(mf->oxm_header);
+ char *value;
/* Set field is the only action of variable length (so far),
* so handling the variable length portion is open-coded here */
- padded_value_len = ROUND_UP(mf->n_bytes, 8);
- ofpbuf_put_uninit(openflow, padded_value_len);
+ oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
+ oasf->dst = htonl(mf->oxm_header);
oasf->len = htons(ntohs(oasf->len) + padded_value_len);
- memset(oasf + 1, 0, padded_value_len);
+ value = ofpbuf_put_zeros(openflow, padded_value_len);
bitwise_copy(&load->subvalue, sizeof load->subvalue, load->dst.ofs,
- oasf + 1, mf->n_bytes, load->dst.ofs, load->dst.n_bits);
- return;
+ value, mf->n_bytes, load->dst.ofs, load->dst.n_bits);
}
void
sizeof src_data_be * 8);
mf_write_subfield_flow(dst, &src_subvalue, flow);
}
+\f
+/* nxm_parse_stack_action, works for both push() and pop(). */
+void
+nxm_parse_stack_action(struct ofpact_stack *stack_action, const char *s)
+{
+ s = mf_parse_subfield(&stack_action->subfield, s);
+ if (*s != '\0') {
+ ovs_fatal(0, "%s: trailing garbage following push or pop", s);
+ }
+}
+
+void
+nxm_format_stack_push(const struct ofpact_stack *push, struct ds *s)
+{
+ ds_put_cstr(s, "push:");
+ mf_format_subfield(&push->subfield, s);
+}
+
+void
+nxm_format_stack_pop(const struct ofpact_stack *pop, struct ds *s)
+{
+ ds_put_cstr(s, "pop:");
+ mf_format_subfield(&pop->subfield, s);
+}
+
+/* Common set for both push and pop actions. */
+static void
+stack_action_from_openflow__(const struct nx_action_stack *nasp,
+ struct ofpact_stack *stack_action)
+{
+ stack_action->subfield.field = mf_from_nxm_header(ntohl(nasp->field));
+ stack_action->subfield.ofs = ntohs(nasp->offset);
+ stack_action->subfield.n_bits = ntohs(nasp->n_bits);
+}
+
+static void
+nxm_stack_to_nxast__(const struct ofpact_stack *stack_action,
+ struct nx_action_stack *nasp)
+{
+ nasp->offset = htons(stack_action->subfield.ofs);
+ nasp->n_bits = htons(stack_action->subfield.n_bits);
+ nasp->field = htonl(stack_action->subfield.field->nxm_header);
+}
+
+enum ofperr
+nxm_stack_push_from_openflow(const struct nx_action_stack *nasp,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_stack *push;
+
+ push = ofpact_put_STACK_PUSH(ofpacts);
+ stack_action_from_openflow__(nasp, push);
+
+ return nxm_stack_push_check(push, NULL);
+}
+
+enum ofperr
+nxm_stack_pop_from_openflow(const struct nx_action_stack *nasp,
+ struct ofpbuf *ofpacts)
+{
+ struct ofpact_stack *pop;
+
+ pop = ofpact_put_STACK_POP(ofpacts);
+ stack_action_from_openflow__(nasp, pop);
+
+ return nxm_stack_pop_check(pop, NULL);
+}
+
+enum ofperr
+nxm_stack_push_check(const struct ofpact_stack *push,
+ const struct flow *flow)
+{
+ return mf_check_src(&push->subfield, flow);
+}
+
+enum ofperr
+nxm_stack_pop_check(const struct ofpact_stack *pop,
+ const struct flow *flow)
+{
+ return mf_check_dst(&pop->subfield, flow);
+}
+
+void
+nxm_stack_push_to_nxast(const struct ofpact_stack *stack,
+ struct ofpbuf *openflow)
+{
+ nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_PUSH(openflow));
+}
+
+void
+nxm_stack_pop_to_nxast(const struct ofpact_stack *stack,
+ struct ofpbuf *openflow)
+{
+ nxm_stack_to_nxast__(stack, ofputil_put_NXAST_STACK_POP(openflow));
+}
+
+/* nxm_execute_stack_push(), nxm_execute_stack_pop(). */
+static void
+nx_stack_push(struct ofpbuf *stack, union mf_subvalue *v)
+{
+ ofpbuf_put(stack, v, sizeof *v);
+}
+
+static union mf_subvalue *
+nx_stack_pop(struct ofpbuf *stack)
+{
+ union mf_subvalue *v = NULL;
+
+ if (stack->size) {
+ stack->size -= sizeof *v;
+ v = (union mf_subvalue *) ofpbuf_tail(stack);
+ }
+
+ return v;
+}
+
+void
+nxm_execute_stack_push(const struct ofpact_stack *push,
+ const struct flow *flow, struct ofpbuf *stack)
+{
+ union mf_subvalue dst_value;
+
+ mf_read_subfield(&push->subfield, flow, &dst_value);
+ nx_stack_push(stack, &dst_value);
+}
+
+void
+nxm_execute_stack_pop(const struct ofpact_stack *pop,
+ struct flow *flow, struct ofpbuf *stack)
+{
+ union mf_subvalue *src_value;
+
+ src_value = nx_stack_pop(stack);
+
+ /* Only pop if stack is not empty. Otherwise, give warning. */
+ if (src_value) {
+ mf_write_subfield_flow(&pop->subfield, src_value, flow);
+ } else {
+ if (!VLOG_DROP_WARN(&rl)) {
+ char *flow_str = flow_to_string(flow);
+ VLOG_WARN_RL(&rl, "Failed to pop from an empty stack. On flow \n"
+ " %s", flow_str);
+ free(flow_str);
+ }
+ }
+}
struct mf_subfield;
struct ofpact_reg_move;
struct ofpact_reg_load;
+struct ofpact_stack;
struct ofpbuf;
struct nx_action_reg_load;
struct nx_action_reg_move;
+
/* Nicira Extended Match (NXM) flexible flow match helper functions.
*
* See include/openflow/nicira-ext.h for NXM specification.
void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
struct flow *);
+void nxm_parse_stack_action(struct ofpact_stack *, const char *);
+
+void nxm_format_stack_push(const struct ofpact_stack *, struct ds *);
+void nxm_format_stack_pop(const struct ofpact_stack *, struct ds *);
+
+enum ofperr nxm_stack_push_from_openflow(const struct nx_action_stack *,
+ struct ofpbuf *ofpacts);
+enum ofperr nxm_stack_pop_from_openflow(const struct nx_action_stack *,
+ struct ofpbuf *ofpacts);
+enum ofperr nxm_stack_push_check(const struct ofpact_stack *,
+ const struct flow *);
+enum ofperr nxm_stack_pop_check(const struct ofpact_stack *,
+ const struct flow *);
+
+void nxm_stack_push_to_nxast(const struct ofpact_stack *,
+ struct ofpbuf *openflow);
+void nxm_stack_pop_to_nxast(const struct ofpact_stack *,
+ struct ofpbuf *openflow);
+
+void nxm_execute_stack_push(const struct ofpact_stack *,
+ const struct flow *, struct ofpbuf *);
+void nxm_execute_stack_pop(const struct ofpact_stack *,
+ struct flow *, struct ofpbuf *);
+
int nxm_field_bytes(uint32_t header);
int nxm_field_bits(uint32_t header);
case OVS_KEY_ATTR_ENCAP: return "encap";
case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
- case OVS_KEY_ATTR_TUN_ID: return "tun_id";
case OVS_KEY_ATTR_TUNNEL: return "tunnel";
case OVS_KEY_ATTR_IN_PORT: return "in_port";
case OVS_KEY_ATTR_ETHERNET: return "eth";
case OVS_KEY_ATTR_ENCAP: return -2;
case OVS_KEY_ATTR_PRIORITY: return 4;
case OVS_KEY_ATTR_SKB_MARK: return 4;
- case OVS_KEY_ATTR_TUN_ID: return 8;
case OVS_KEY_ATTR_TUNNEL: return -2;
case OVS_KEY_ATTR_IN_PORT: return 4;
case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
ds_put_format(ds, "(%#"PRIx32")", nl_attr_get_u32(a));
break;
- case OVS_KEY_ATTR_TUN_ID:
- ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
- break;
-
case OVS_KEY_ATTR_TUNNEL:
memset(&tun_key, 0, sizeof tun_key);
if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
}
}
- {
- char tun_id_s[32];
- int n = -1;
-
- if (sscanf(s, "tun_id(%31[x0123456789abcdefABCDEF])%n",
- tun_id_s, &n) > 0 && n > 0) {
- uint64_t tun_id = strtoull(tun_id_s, NULL, 0);
- nl_msg_put_be64(key, OVS_KEY_ATTR_TUN_ID, htonll(tun_id));
- return n;
- }
- }
-
{
char tun_id_s[32];
int tos, ttl;
if (flow->tunnel.ip_dst) {
tun_key_to_attr(buf, &flow->tunnel);
- } else if (flow->tunnel.tun_id != htonll(0)) {
- nl_msg_put_be64(buf, OVS_KEY_ATTR_TUN_ID, flow->tunnel.tun_id);
}
if (flow->skb_mark) {
uint64_t present_attrs;
size_t left;
+ BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs);
present_attrs = 0;
*out_of_range_attrp = 0;
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
return false;
}
- if (type >= CHAR_BIT * sizeof present_attrs) {
+ if (type > OVS_KEY_ATTR_MAX) {
*out_of_range_attrp = type;
} else {
if (present_attrs & (UINT64_C(1) << type)) {
const struct nlattr *key, size_t key_len)
{
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- ovs_be16 dl_type;
- /* Parse MPLS label stack entry */
if (eth_type_mpls(flow->dl_type)) {
- /* Calculate fitness of outer attributes. */
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
- /* Get the MPLS LSE value. */
if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
return ODP_FIT_TOO_LITTLE;
}
flow->mpls_lse = nl_attr_get_be32(attrs[OVS_KEY_ATTR_MPLS]);
flow->mpls_depth++;
-
- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
- flow->encap_dl_type = htons(ETH_TYPE_IP);
- } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
- flow->encap_dl_type = htons(ETH_TYPE_IPV6);
- } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
- flow->encap_dl_type = htons(ETH_TYPE_ARP);
- }
- }
-
- dl_type = flow_innermost_dl_type(flow);
-
- if (dl_type == htons(ETH_TYPE_IP)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
const struct ovs_key_ipv4 *ipv4_key;
return ODP_FIT_ERROR;
}
}
- } else if (dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
const struct ovs_key_ipv6 *ipv6_key;
return ODP_FIT_ERROR;
}
}
- } else if (dl_type == htons(ETH_TYPE_ARP) ||
- dl_type == htons(ETH_TYPE_RARP)) {
+ } else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
+ flow->dl_type == htons(ETH_TYPE_RARP)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
const struct ovs_key_arp *arp_key;
}
if (flow->nw_proto == IPPROTO_TCP
- && (dl_type == htons(ETH_TYPE_IP) ||
- dl_type == htons(ETH_TYPE_IPV6))
+ && (flow->dl_type == htons(ETH_TYPE_IP) ||
+ flow->dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
flow->tp_dst = tcp_key->tcp_dst;
}
} else if (flow->nw_proto == IPPROTO_UDP
- && (dl_type == htons(ETH_TYPE_IP) ||
- dl_type == htons(ETH_TYPE_IPV6))
+ && (flow->dl_type == htons(ETH_TYPE_IP) ||
+ flow->dl_type == htons(ETH_TYPE_IPV6))
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
flow->tp_dst = udp_key->udp_dst;
}
} else if (flow->nw_proto == IPPROTO_ICMP
- && dl_type == htons(ETH_TYPE_IP)
+ && flow->dl_type == htons(ETH_TYPE_IP)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
flow->tp_dst = htons(icmp_key->icmp_code);
}
} else if (flow->nw_proto == IPPROTO_ICMPV6
- && dl_type == htons(ETH_TYPE_IPV6)
+ && flow->dl_type == htons(ETH_TYPE_IPV6)
&& !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
}
- if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUN_ID)) {
- flow->tunnel.tun_id = nl_attr_get_be64(attrs[OVS_KEY_ATTR_TUN_ID]);
- expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUN_ID;
- }
-
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
enum odp_key_fitness res;
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
if (flow->tunnel.ip_dst) {
odp_put_tunnel_action(&base->tunnel, odp_actions);
- } else {
- commit_set_action(odp_actions, OVS_KEY_ATTR_TUN_ID,
- &base->tunnel.tun_id, sizeof base->tunnel.tun_id);
}
}
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
- ovs_be16 dl_type = flow_innermost_dl_type(flow);
-
/* Check if flow really have an IP header. */
if (!flow->nw_proto) {
return;
}
- if (dl_type == htons(ETH_TYPE_IP)) {
+ if (base->dl_type == htons(ETH_TYPE_IP)) {
commit_set_ipv4_action(flow, base, odp_actions);
- } else if (dl_type == htons(ETH_TYPE_IPV6)) {
+ } else if (base->dl_type == htons(ETH_TYPE_IPV6)) {
commit_set_ipv6_action(flow, base, odp_actions);
}
}
commit_set_port_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions)
{
- if (!base->tp_src && !base->tp_dst) {
+ if (!is_ip_any(base) || (!base->tp_src && !base->tp_dst)) {
return;
}
{
commit_set_ether_addr_action(flow, base, odp_actions);
commit_vlan_action(flow, base, odp_actions);
- commit_mpls_action(flow, base, odp_actions);
commit_set_nw_action(flow, base, odp_actions);
commit_set_port_action(flow, base, odp_actions);
+ /* Commiting MPLS actions should occur after committing nw and port
+ * actions. This is because committing MPLS actions may alter a packet so
+ * that it is no longer IP and thus nw and port actions are no longer valid.
+ */
+ commit_mpls_action(flow, base, odp_actions);
commit_set_priority_action(flow, base, odp_actions);
commit_set_skb_mark_action(flow, base, odp_actions);
}
* struct pad nl hdr total
* ------ --- ------ -----
* OVS_KEY_ATTR_PRIORITY 4 -- 4 8
- * OVS_KEY_ATTR_TUN_ID 8 -- 4 12
* OVS_KEY_ATTR_TUNNEL 0 -- 4 4
* - OVS_TUNNEL_KEY_ATTR_ID 8 -- 4 12
* - OVS_TUNNEL_KEY_ATTR_IPV4_SRC 4 -- 4 8
* OVS_KEY_ATTR_ICMPV6 2 2 4 8
* OVS_KEY_ATTR_ND 28 -- 4 32
* ----------------------------------------------------------
- * total 220
+ * total 208
*
* We include some slack space in case the calculation isn't quite right or we
* add another field and forget to adjust this value.
(const struct nx_action_reg_load *) a, out);
break;
+ case OFPUTIL_NXAST_STACK_PUSH:
+ error = nxm_stack_push_from_openflow(
+ (const struct nx_action_stack *) a, out);
+ break;
+
+ case OFPUTIL_NXAST_STACK_POP:
+ error = nxm_stack_pop_from_openflow(
+ (const struct nx_action_stack *) a, out);
+ break;
+
case OFPUTIL_NXAST_NOTE:
nan = (const struct nx_action_note *) a;
note_from_openflow(nan, out);
break;
}
+ case OFPUTIL_NXAST_SET_MPLS_TTL: {
+ struct nx_action_mpls_ttl *nxamt = (struct nx_action_mpls_ttl *)a;
+ ofpact_put_SET_MPLS_TTL(out)->ttl = nxamt->ttl;
+ break;
+ }
+
+ case OFPUTIL_NXAST_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+
case OFPUTIL_NXAST_POP_MPLS: {
struct nx_action_pop_mpls *nxapm = (struct nx_action_pop_mpls *)a;
if (eth_type_mpls(nxapm->ethertype)) {
return nxm_reg_load_from_openflow12_set_field(
(const struct ofp12_action_set_field *)a, out);
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL: {
+ struct ofp11_action_mpls_ttl *oamt = (struct ofp11_action_mpls_ttl *)a;
+ ofpact_put_SET_MPLS_TTL(out)->ttl = oamt->mpls_ttl;
+ break;
+ }
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+
case OFPUTIL_OFPAT11_PUSH_MPLS: {
struct ofp11_action_push *oap = (struct ofp11_action_push *)a;
if (!eth_type_mpls(oap->ethertype)) {
return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
}
+ case OFPACT_STACK_PUSH:
+ return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow);
+
+ case OFPACT_STACK_POP:
+ return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow);
+
case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
case OFPACT_SET_TUNNEL:
case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
break;
+ case OFPACT_STACK_PUSH:
+ nxm_stack_push_to_nxast(ofpact_get_STACK_PUSH(a), out);
+ break;
+
+ case OFPACT_STACK_POP:
+ nxm_stack_pop_to_nxast(ofpact_get_STACK_POP(a), out);
+ break;
+
case OFPACT_DEC_TTL:
ofpact_dec_ttl_to_nxast(ofpact_get_DEC_TTL(a), out);
break;
+ case OFPACT_SET_MPLS_TTL:
+ ofputil_put_NXAST_SET_MPLS_TTL(out)->ttl
+ = ofpact_get_SET_MPLS_TTL(a)->ttl;
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ofputil_put_NXAST_DEC_MPLS_TTL(out);
+ break;
+
case OFPACT_SET_TUNNEL:
ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
break;
case OFPACT_BUNDLE:
case OFPACT_REG_MOVE:
case OFPACT_REG_LOAD:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STACK_POP:
case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
case OFPACT_SET_TUNNEL:
case OFPACT_WRITE_METADATA:
case OFPACT_SET_QUEUE:
ofpact_dec_ttl_to_openflow11(ofpact_get_DEC_TTL(a), out);
break;
+ case OFPACT_SET_MPLS_TTL:
+ ofputil_put_OFPAT11_SET_MPLS_TTL(out)->mpls_ttl
+ = ofpact_get_SET_MPLS_TTL(a)->ttl;
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ofputil_put_OFPAT11_DEC_MPLS_TTL(out);
+ break;
+
case OFPACT_WRITE_METADATA:
/* OpenFlow 1.1 uses OFPIT_WRITE_METADATA to express this action. */
break;
case OFPACT_BUNDLE:
case OFPACT_REG_MOVE:
case OFPACT_REG_LOAD:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STACK_POP:
case OFPACT_SET_TUNNEL:
case OFPACT_POP_QUEUE:
case OFPACT_FIN_TIMEOUT:
case OFPACT_SET_L4_DST_PORT:
case OFPACT_REG_MOVE:
case OFPACT_REG_LOAD:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STACK_POP:
case OFPACT_DEC_TTL:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_DEC_MPLS_TTL:
case OFPACT_SET_TUNNEL:
case OFPACT_WRITE_METADATA:
case OFPACT_SET_QUEUE:
nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
break;
+ case OFPACT_STACK_PUSH:
+ nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s);
+ break;
+
+ case OFPACT_STACK_POP:
+ nxm_format_stack_pop(ofpact_get_STACK_POP(a), s);
+ break;
+
case OFPACT_DEC_TTL:
print_dec_ttl(ofpact_get_DEC_TTL(a), s);
break;
+ case OFPACT_SET_MPLS_TTL:
+ ds_put_format(s, "set_mpls_ttl(%"PRIu8")",
+ ofpact_get_SET_MPLS_TTL(a)->ttl);
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ ds_put_cstr(s, "dec_mpls_ttl");
+ break;
+
case OFPACT_SET_TUNNEL:
tunnel = ofpact_get_SET_TUNNEL(a);
ds_put_format(s, "set_tunnel%s:%#"PRIx64,
DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port, ofpact) \
DEFINE_OFPACT(REG_MOVE, ofpact_reg_move, ofpact) \
DEFINE_OFPACT(REG_LOAD, ofpact_reg_load, ofpact) \
+ DEFINE_OFPACT(STACK_PUSH, ofpact_stack, ofpact) \
+ DEFINE_OFPACT(STACK_POP, ofpact_stack, ofpact) \
DEFINE_OFPACT(DEC_TTL, ofpact_cnt_ids, cnt_ids) \
+ DEFINE_OFPACT(SET_MPLS_TTL, ofpact_mpls_ttl, ofpact) \
+ DEFINE_OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact) \
DEFINE_OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact) \
DEFINE_OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact) \
\
struct mf_subfield dst;
};
+/* OFPACT_STACK_PUSH.
+ *
+ * Used for NXAST_STACK_PUSH and NXAST_STACK_POP. */
+struct ofpact_stack {
+ struct ofpact ofpact;
+ struct mf_subfield subfield;
+};
+
/* OFPACT_REG_LOAD.
*
* Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */
uint16_t cnt_ids[];
};
+/* OFPACT_SET_MPLS_TTL.
+ *
+ * Used for NXAST_SET_MPLS_TTL */
+struct ofpact_mpls_ttl {
+ struct ofpact ofpact;
+
+ uint8_t ttl;
+};
+
/* OFPACT_GOTO_TABLE
*
* Used for OFPIT11_GOTO_TABLE */
}
}
+static void
+parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
+{
+ struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
+
+ if (*arg == '\0') {
+ ovs_fatal(0, "parse_set_mpls_ttl: expected ttl.");
+ }
+
+ mpls_ttl->ttl = atoi(arg);
+}
+
static void
set_field_parse(const char *arg, struct ofpbuf *ofpacts)
{
parse_dec_ttl(ofpacts, arg);
break;
+ case OFPUTIL_NXAST_SET_MPLS_TTL:
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+ parse_set_mpls_ttl(ofpacts, arg);
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ case OFPUTIL_NXAST_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(ofpacts);
+ break;
+
case OFPUTIL_NXAST_FIN_TIMEOUT:
parse_fin_timeout(ofpacts, arg);
break;
ofpact_put_POP_MPLS(ofpacts)->ethertype =
htons(str_to_u16(arg, "pop_mpls"));
break;
+ case OFPUTIL_NXAST_STACK_PUSH:
+ nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+ break;
+ case OFPUTIL_NXAST_STACK_POP:
+ nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+ break;
}
}
#include "openflow/openflow.h"
#include "openflow/nicira-ext.h"
#include "packets.h"
-#include "pcap.h"
#include "type-props.h"
#include "unaligned.h"
#include "util.h"
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
{
const struct flow_wildcards *wc = &match->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
/* tunnel params other than tun_id can't be sent in a flow_mod */
if (!tun_parms_fully_wildcarded(wc)) {
fr->priority = ntohs(nfr->priority);
fr->cookie = nfr->cookie;
fr->reason = nfr->reason;
- fr->table_id = 255;
+ fr->table_id = nfr->table_id ? nfr->table_id - 1 : 255;
fr->duration_sec = ntohl(nfr->duration_sec);
fr->duration_nsec = ntohl(nfr->duration_nsec);
fr->idle_timeout = ntohs(nfr->idle_timeout);
nfr->cookie = fr->cookie;
nfr->priority = htons(fr->priority);
nfr->reason = fr->reason;
+ nfr->table_id = fr->table_id + 1;
nfr->duration_sec = htonl(fr->duration_sec);
nfr->duration_nsec = htonl(fr->duration_nsec);
nfr->idle_timeout = htons(fr->idle_timeout);
//OFPAT11_ACTION(OFPAT11_SET_NW_ECN, ofp11_action_nw_ecn, "0, mod_nw_ecn")
OFPAT11_ACTION(OFPAT11_SET_TP_SRC, ofp_action_tp_port, 0, "mod_tp_src")
OFPAT11_ACTION(OFPAT11_SET_TP_DST, ofp_action_tp_port, 0, "mod_tp_dst")
+OFPAT11_ACTION(OFPAT11_SET_MPLS_TTL, ofp11_action_mpls_ttl, 0, "set_mpls_ttl")
+OFPAT11_ACTION(OFPAT11_DEC_MPLS_TTL, ofp_action_header, 0, "dec_mpls_ttl")
OFPAT11_ACTION(OFPAT11_PUSH_VLAN, ofp11_action_push, 0, "push_vlan")
OFPAT11_ACTION(OFPAT11_POP_VLAN, ofp_action_header, 0, "pop_vlan")
OFPAT11_ACTION(OFPAT11_PUSH_MPLS, ofp11_action_push, 0, "push_mpls")
NXAST_ACTION(NXAST_POP_QUEUE, nx_action_pop_queue, 0, "pop_queue")
NXAST_ACTION(NXAST_REG_MOVE, nx_action_reg_move, 0, "move")
NXAST_ACTION(NXAST_REG_LOAD, nx_action_reg_load, 0, "load")
+NXAST_ACTION(NXAST_STACK_PUSH, nx_action_stack, 0, "push")
+NXAST_ACTION(NXAST_STACK_POP, nx_action_stack, 0, "pop")
NXAST_ACTION(NXAST_NOTE, nx_action_note, 1, "note")
NXAST_ACTION(NXAST_SET_TUNNEL64, nx_action_set_tunnel64, 0, "set_tunnel64")
NXAST_ACTION(NXAST_MULTIPATH, nx_action_multipath, 0, "multipath")
NXAST_ACTION(NXAST_DEC_TTL_CNT_IDS, nx_action_cnt_ids, 1, NULL)
NXAST_ACTION(NXAST_WRITE_METADATA, nx_action_write_metadata, 0,
"write_metadata")
+NXAST_ACTION(NXAST_SET_MPLS_TTL, nx_action_mpls_ttl, 0, "set_mpls_ttl")
+NXAST_ACTION(NXAST_DEC_MPLS_TTL, nx_action_header, 0, "dec_mpls_ttl")
NXAST_ACTION(NXAST_PUSH_MPLS, nx_action_push_mpls, 0, "push_mpls")
NXAST_ACTION(NXAST_POP_MPLS, nx_action_pop_mpls, 0, "pop_mpls")
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* 'base' should be appropriately aligned. Using an array of uint32_t or
* uint64_t for the buffer is a reasonable way to ensure appropriate alignment
- * for 32- or 64-bit data. OFPBUF_STACK_BUFFER is a convenient way to do so.
+ * for 32- or 64-bit data.
*
* An ofpbuf operation that requires reallocating data will assert-fail if this
* function was used to initialize it. Thus, one need not call ofpbuf_uninit()
*
* 'base' should be appropriately aligned. Using an array of uint32_t or
* uint64_t for the buffer is a reasonable way to ensure appropriate alignment
- * for 32- or 64-bit data. OFPBUF_STACK_BUFFER is a convenient way to do so.
+ * for 32- or 64-bit data.
*
* An ofpbuf operation that requires reallocating data will copy the provided
* buffer into a malloc()'d buffer. Thus, it is wise to call ofpbuf_uninit()
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void *private_p; /* Private pointer for use by owner. */
};
-/* Declares NAME as a SIZE-byte array aligned properly for storing any kind of
- * data. For use with ofpbuf_use_stack(). */
-#define OFPBUF_STACK_BUFFER(NAME, SIZE) uint64_t NAME[DIV_ROUND_UP(SIZE, 8)]
-
void ofpbuf_use(struct ofpbuf *, void *, size_t);
void ofpbuf_use_stack(struct ofpbuf *, void *, size_t);
void ofpbuf_use_stub(struct ofpbuf *, void *, size_t);
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
+/* Returns strlen(json_to_string(ovsdb_atom_to_json(atom, type), 0)). */
+size_t
+ovsdb_atom_json_length(const union ovsdb_atom *atom,
+ enum ovsdb_atomic_type type)
+{
+ struct json json;
+
+ switch (type) {
+ case OVSDB_TYPE_VOID:
+ NOT_REACHED();
+
+ case OVSDB_TYPE_INTEGER:
+ json.type = JSON_INTEGER;
+ json.u.integer = atom->integer;
+ break;
+
+ case OVSDB_TYPE_REAL:
+ json.type = JSON_REAL;
+ json.u.real = atom->real;
+ break;
+
+ case OVSDB_TYPE_BOOLEAN:
+ json.type = atom->boolean ? JSON_TRUE : JSON_FALSE;
+ break;
+
+ case OVSDB_TYPE_STRING:
+ json.type = JSON_STRING;
+ json.u.string = atom->string;
+ break;
+
+ case OVSDB_TYPE_UUID:
+ return strlen("[\"uuid\",\"00000000-0000-0000-0000-000000000000\"]");
+
+ case OVSDB_N_TYPES:
+ default:
+ NOT_REACHED();
+ }
+
+ return json_serialized_length(&json);
+}
+
static char *
ovsdb_atom_from_string__(union ovsdb_atom *atom,
const struct ovsdb_base_type *base, const char *s,
}
}
+/* Returns strlen(json_to_string(ovsdb_datum_to_json(datum, type), 0)). */
+size_t
+ovsdb_datum_json_length(const struct ovsdb_datum *datum,
+ const struct ovsdb_type *type)
+{
+ if (ovsdb_type_is_map(type)) {
+ size_t length;
+
+ /* ["map",[...]]. */
+ length = 10;
+ if (datum->n > 0) {
+ size_t i;
+
+ /* Commas between pairs in the inner [...] */
+ length += datum->n - 1;
+
+ /* [,] in each pair. */
+ length += datum->n * 3;
+
+ /* Data. */
+ for (i = 0; i < datum->n; i++) {
+ length += ovsdb_atom_json_length(&datum->keys[i],
+ type->key.type);
+ length += ovsdb_atom_json_length(&datum->values[i],
+ type->value.type);
+ }
+ }
+ return length;
+ } else if (datum->n == 1) {
+ return ovsdb_atom_json_length(&datum->keys[0], type->key.type);
+ } else {
+ size_t length;
+ size_t i;
+
+ /* ["set",[...]]. */
+ length = 10;
+ if (datum->n > 0) {
+ /* Commas between elements in the inner [...]. */
+ length += datum->n - 1;
+
+ /* Data. */
+ for (i = 0; i < datum->n; i++) {
+ length += ovsdb_atom_json_length(&datum->keys[i],
+ type->key.type);
+ }
+ }
+ return length;
+ }
+}
+
static const char *
skip_spaces(const char *p)
{
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
WARN_UNUSED_RESULT;
struct json *ovsdb_atom_to_json(const union ovsdb_atom *,
enum ovsdb_atomic_type);
+size_t ovsdb_atom_json_length(const union ovsdb_atom *,
+ enum ovsdb_atomic_type);
char *ovsdb_atom_from_string(union ovsdb_atom *,
const struct ovsdb_base_type *, const char *,
WARN_UNUSED_RESULT;
struct json *ovsdb_datum_to_json(const struct ovsdb_datum *,
const struct ovsdb_type *);
+size_t ovsdb_datum_json_length(const struct ovsdb_datum *,
+ const struct ovsdb_type *);
char *ovsdb_datum_from_string(struct ovsdb_datum *,
const struct ovsdb_type *, const char *,
* 'class'. (Ordinarily 'class' is compiled from an OVSDB schema automatically
* by ovsdb-idlc.)
*
+ * Passes 'retry' to jsonrpc_session_open(). See that function for
+ * documentation.
+ *
* If 'monitor_everything_by_default' is true, then everything in the remote
* database will be replicated by default. ovsdb_idl_omit() and
* ovsdb_idl_omit_alert() may be used to selectively drop some columns from
*/
struct ovsdb_idl *
ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
- bool monitor_everything_by_default)
+ bool monitor_everything_by_default, bool retry)
{
struct ovsdb_idl *idl;
uint8_t default_mode;
idl = xzalloc(sizeof *idl);
idl->class = class;
- idl->session = jsonrpc_session_open(remote);
+ idl->session = jsonrpc_session_open(remote, retry);
shash_init(&idl->table_by_name);
idl->tables = xmalloc(class->n_tables * sizeof *idl->tables);
for (i = 0; i < class->n_tables; i++) {
{
idl->verify_write_only = true;
}
+
+bool
+ovsdb_idl_is_alive(const struct ovsdb_idl *idl)
+{
+ return jsonrpc_session_is_alive(idl->session);
+}
+
+int
+ovsdb_idl_get_last_error(const struct ovsdb_idl *idl)
+{
+ return jsonrpc_session_get_last_error(idl->session);
+}
\f
static unsigned char *
ovsdb_idl_get_mode(struct ovsdb_idl *idl,
* Takes ownership of what 'datum' points to (and in some cases destroys that
* data before returning) but makes a copy of 'datum' itself. (Commonly
* 'datum' is on the caller's stack.) */
-void
-ovsdb_idl_txn_write(const struct ovsdb_idl_row *row_,
- const struct ovsdb_idl_column *column,
- struct ovsdb_datum *datum)
+static void
+ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_,
+ const struct ovsdb_idl_column *column,
+ struct ovsdb_datum *datum, bool owns_datum)
{
struct ovsdb_idl_row *row = CONST_CAST(struct ovsdb_idl_row *, row_);
const struct ovsdb_idl_table_class *class;
bool write_only;
if (ovsdb_idl_row_is_synthetic(row)) {
- ovsdb_datum_destroy(datum, &column->type);
- return;
+ goto discard_datum;
}
class = row->table->class;
if (row->table->idl->verify_write_only && !write_only) {
VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
" explicitly configured not to.", class->name, column->name);
- ovsdb_datum_destroy(datum, &column->type);
- return;
+ goto discard_datum;
}
/* If this is a write-only column and the datum being written is the same
* ovsdb_idl_txn_commit().) */
if (write_only && ovsdb_datum_equals(ovsdb_idl_read(row, column),
datum, &column->type)) {
- ovsdb_datum_destroy(datum, &column->type);
- return;
+ goto discard_datum;
}
if (hmap_node_is_null(&row->txn_node)) {
} else {
bitmap_set1(row->written, column_idx);
}
- row->new[column_idx] = *datum;
+ if (owns_datum) {
+ row->new[column_idx] = *datum;
+ } else {
+ ovsdb_datum_clone(&row->new[column_idx], datum, &column->type);
+ }
(column->unparse)(row);
(column->parse)(row, &row->new[column_idx]);
+ return;
+
+discard_datum:
+ if (owns_datum) {
+ ovsdb_datum_destroy(datum, &column->type);
+ }
+}
+
+void
+ovsdb_idl_txn_write(const struct ovsdb_idl_row *row,
+ const struct ovsdb_idl_column *column,
+ struct ovsdb_datum *datum)
+{
+ ovsdb_idl_txn_write__(row, column, datum, true);
+}
+
+void
+ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *row,
+ const struct ovsdb_idl_column *column,
+ const struct ovsdb_datum *datum)
+{
+ ovsdb_idl_txn_write__(row, column,
+ CONST_CAST(struct ovsdb_datum *, datum), false);
}
/* Causes the original contents of 'column' in 'row_' to be verified as a
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct ovsdb_idl *ovsdb_idl_create(const char *remote,
const struct ovsdb_idl_class *,
- bool monitor_everything_by_default);
+ bool monitor_everything_by_default,
+ bool retry);
void ovsdb_idl_destroy(struct ovsdb_idl *);
void ovsdb_idl_run(struct ovsdb_idl *);
bool ovsdb_idl_has_ever_connected(const struct ovsdb_idl *);
void ovsdb_idl_force_reconnect(struct ovsdb_idl *);
void ovsdb_idl_verify_write_only(struct ovsdb_idl *);
+
+bool ovsdb_idl_is_alive(const struct ovsdb_idl *);
+int ovsdb_idl_get_last_error(const struct ovsdb_idl *);
\f
/* Choosing columns and tables to replicate. */
void ovsdb_idl_txn_write(const struct ovsdb_idl_row *,
const struct ovsdb_idl_column *,
struct ovsdb_datum *);
+void ovsdb_idl_txn_write_clone(const struct ovsdb_idl_row *,
+ const struct ovsdb_idl_column *,
+ const struct ovsdb_datum *);
void ovsdb_idl_txn_delete(const struct ovsdb_idl_row *);
const struct ovsdb_idl_row *ovsdb_idl_txn_insert(
struct ovsdb_idl_txn *, const struct ovsdb_idl_table_class *,
}
/* Set time to live (TTL) of an MPLS label stack entry (LSE). */
-static void
+void
set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl)
{
*lse &= ~htonl(MPLS_TTL_MASK);
uint8_t
packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
{
- ovs_be16 dl_type = flow_innermost_dl_type(flow);
- if (dl_type_is_ip_any(dl_type) &&
+ if (dl_type_is_ip_any(flow->dl_type) &&
flow->nw_proto == IPPROTO_TCP && packet->l7) {
const struct tcp_header *tcp = packet->l4;
return TCP_FLAGS(tcp->tcp_ctl);
void push_mpls(struct ofpbuf *packet, ovs_be16 ethtype, ovs_be32 lse);
void pop_mpls(struct ofpbuf *, ovs_be16 ethtype);
+void set_mpls_lse_ttl(ovs_be32 *lse, uint8_t ttl);
void set_mpls_lse_tc(ovs_be32 *lse, uint8_t tc);
void set_mpls_lse_label(ovs_be32 *lse, ovs_be32 label);
void set_mpls_lse_bos(ovs_be32 *lse, uint8_t bos);
*/
#include <config.h>
-#include "pcap.h"
+#include "pcap-file.h"
#include <errno.h>
#include <inttypes.h>
#include <string.h>
* limitations under the License.
*/
-#ifndef PCAP_H
-#define PCAP_H 1
+#ifndef PCAP_FILE_H
+#define PCAP_FILE_H 1
#include <stdio.h>
int pcap_read(FILE *, struct ofpbuf **);
void pcap_write(FILE *, struct ofpbuf *);
-#endif /* dhcp.h */
+#endif /* pcap-file.h */
/*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Configures the backoff parameters for 'fsm'. 'min_backoff' is the minimum
* number of milliseconds, and 'max_backoff' is the maximum, between connection
- * attempts.
+ * attempts. The current backoff is also the duration that 'fsm' is willing to
+ * wait for a given connection to succeed or fail.
*
* 'min_backoff' must be at least 1000, and 'max_backoff' must be greater than
* or equal to 'min_backoff'.
#include <config.h>
#include "timeval.h"
#include <errno.h>
-#if HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include "util.h"
#include "vlog.h"
+/* backtrace() from <execinfo.h> is really useful, but it is not signal safe
+ * everywhere, such as on x86-64. */
+#if HAVE_EXECINFO_H && !defined __x86_64__
+# define USE_BACKTRACE 1
+# include <execinfo.h>
+#else
+# define USE_BACKTRACE 0
+#endif
+
VLOG_DEFINE_THIS_MODULE(timeval);
/* The clock to use for measuring time intervals. This is CLOCK_MONOTONIC by
const struct timespec *a, const struct timespec *b);
static unixctl_cb_func backtrace_cb;
-#ifndef HAVE_EXECINFO_H
-#define HAVE_EXECINFO_H 0
-
+#if !USE_BACKTRACE
static int
backtrace(void **buffer OVS_UNUSED, int size OVS_UNUSED)
{
{
NOT_REACHED();
}
-#endif
+#endif /* !USE_BACKTRACE */
/* Initializes the timetracking module, if not already initialized. */
static void
* initialization which is not signal safe. This can cause deadlocks if
* run from the signal handler. As a workaround, force the initialization
* to happen here. */
- if (HAVE_EXECINFO_H) {
+ if (USE_BACKTRACE) {
void *bt[1];
backtrace(bt, ARRAY_SIZE(bt));
memset(traces, 0, sizeof traces);
- if (HAVE_EXECINFO_H && CACHE_TIME) {
+ if (USE_BACKTRACE && CACHE_TIME) {
unixctl_command_register("backtrace", "", 0, 0, backtrace_cb, NULL);
}
wall_tick = true;
monotonic_tick = true;
- if (HAVE_EXECINFO_H && CACHE_TIME) {
+ if (USE_BACKTRACE && CACHE_TIME) {
struct trace *trace = &traces[trace_head];
trace->n_frames = backtrace(trace->backtrace,
{
time_init();
- if (HAVE_EXECINFO_H && CACHE_TIME) {
+ if (USE_BACKTRACE && CACHE_TIME) {
struct hmap trace_map = HMAP_INITIALIZER(&trace_map);
struct trace *trace, *next;
sigset_t oldsigs;
{
struct ds ds = DS_EMPTY_INITIALIZER;
- ovs_assert(HAVE_EXECINFO_H && CACHE_TIME);
+ ovs_assert(USE_BACKTRACE && CACHE_TIME);
format_backtraces(&ds, 0);
unixctl_command_reply(conn, ds_cstr(&ds));
ds_destroy(&ds);
/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/* Interval between updates to the reported time, in ms. This should not be
* adjusted much below 10 ms or so with the current implementation, or too
* much time will be wasted in signal handlers and calls to clock_gettime(). */
-#define TIME_UPDATE_INTERVAL 100
+#define TIME_UPDATE_INTERVAL 25
-/* True on systems (particularly x86-64 Linux) where clock_gettime() is
- * inexpensive. On these systems, we don't bother caching the current time.
- * Instead, we consult clock_gettime() directly when needed.
- *
- * False on systems where clock_gettime() is relatively expensive. On these
- * systems, we cache the current time and set up a periodic SIGALRM to remind
- * us to update it.
- *
- * Also false on systems (e.g. ESX) that don't support setting up timers based
- * on a monotonically increasing clock. */
+/* True on systems that support a monotonic clock. Compared to just getting
+ * the value of a variable, clock_gettime() is somewhat expensive, even on
+ * systems that try hard to optimize it (such as x86-64 Linux), so it's
+ * worthwhile to minimize calls via caching. */
#ifndef CACHE_TIME
-#if defined ESX || (defined __x86_64__ && defined LINUX_DATAPATH)
+#if defined ESX
#define CACHE_TIME 0
#else
#define CACHE_TIME 1
vswitchd/ovs-vswitchd.8: \
vswitchd/ovs-vswitchd.8.in \
lib/common.man \
+ lib/coverage-unixctl.man \
lib/daemon.man \
lib/leak-checker.man \
lib/memory-unixctl.man \
ovsdb/remote-passive.man
vswitchd/ovs-vswitchd.8.in:
lib/common.man:
+lib/coverage-unixctl.man:
lib/daemon.man:
lib/leak-checker.man:
lib/memory-unixctl.man:
hmap_remove(&ofconn->connmgr->controllers, &ofconn->hmap_node);
}
+ hmap_destroy(&ofconn->monitors);
list_remove(&ofconn->node);
rconn_destroy(ofconn->rconn);
rconn_packet_counter_destroy(ofconn->packet_in_counter);
struct ofport_dpif;
struct ofproto_dpif;
struct flow_miss;
+struct facet;
struct rule_dpif {
struct rule up;
static void rule_credit_stats(struct rule_dpif *,
const struct dpif_flow_stats *);
-static void flow_push_stats(struct rule_dpif *, const struct flow *,
- const struct dpif_flow_stats *);
+static void flow_push_stats(struct facet *, const struct dpif_flow_stats *);
static tag_type rule_calculate_tag(const struct flow *,
const struct minimask *, uint32_t basis);
static void rule_invalidate(const struct rule_dpif *);
* this flow when actions change header fields. */
struct flow flow;
+ /* stack for the push and pop actions.
+ * Each stack element is of the type "union mf_subvalue". */
+ struct ofpbuf stack;
+ union mf_subvalue init_stack[1024 / sizeof(union mf_subvalue)];
+
/* The packet corresponding to 'flow', or a null pointer if we are
* revalidating without a packet to refer to. */
const struct ofpbuf *packet;
bool exit; /* No further actions should be processed. */
};
+/* Initial values of fields of the packet that may be changed during
+ * flow processing and needed later. */
+struct initial_vals {
+ /* This is the value of vlan_tci in the packet as actually received from
+ * dpif. This is the same as the facet's flow.vlan_tci unless the packet
+ * was received via a VLAN splinter. In that case, this value is 0
+ * (because the packet as actually received from the dpif had no 802.1Q
+ * tag) but the facet's flow.vlan_tci is set to the VLAN that the splinter
+ * represents.
+ *
+ * This member should be removed when the VLAN splinters feature is no
+ * longer needed. */
+ ovs_be16 vlan_tci;
+
+ /* If received on a tunnel, the IP TOS value of the tunnel. */
+ uint8_t tunnel_ip_tos;
+};
+
static void action_xlate_ctx_init(struct action_xlate_ctx *,
struct ofproto_dpif *, const struct flow *,
- ovs_be16 initial_tci, struct rule_dpif *,
+ const struct initial_vals *initial_vals,
+ struct rule_dpif *,
uint8_t tcp_flags, const struct ofpbuf *);
static void xlate_actions(struct action_xlate_ctx *,
const struct ofpact *ofpacts, size_t ofpacts_len,
struct list list_node; /* In struct facet's 'facets' list. */
struct facet *facet; /* Owning facet. */
- /* Key.
- *
- * To save memory in the common case, 'key' is NULL if 'key_fitness' is
- * ODP_FIT_PERFECT, that is, odp_flow_key_from_flow() can accurately
- * regenerate the ODP flow key from ->facet->flow. */
enum odp_key_fitness key_fitness;
struct nlattr *key;
int key_len;
long long int used; /* Time last used; time created if not used. */
+ long long int created; /* Time created. */
uint64_t dp_packet_count; /* Last known packet count in the datapath. */
uint64_t dp_byte_count; /* Last known byte count in the datapath. */
enum slow_path_reason slow; /* 0 if fast path may be used. */
enum subfacet_path path; /* Installed in datapath? */
- /* This value is normally the same as ->facet->flow.vlan_tci. Only VLAN
- * splinters can cause it to differ. This value should be removed when
- * the VLAN splinters feature is no longer needed. */
- ovs_be16 initial_tci; /* Initial VLAN TCI value. */
+ /* Initial values of the packet that may be needed later. */
+ struct initial_vals initial_vals;
/* Datapath port the packet arrived on. This is needed to remove
* flows for ports that are no longer part of the bridge. Since the
long long int now);
static struct subfacet *subfacet_find(struct ofproto_dpif *,
const struct nlattr *key, size_t key_len,
- uint32_t key_hash,
- const struct flow *flow);
+ uint32_t key_hash);
static void subfacet_destroy(struct subfacet *);
static void subfacet_destroy__(struct subfacet *);
static void subfacet_destroy_batch(struct ofproto_dpif *,
struct subfacet **, int n);
-static void subfacet_get_key(struct subfacet *, struct odputil_keybuf *,
- struct ofpbuf *key);
static void subfacet_reset_dp_stats(struct subfacet *,
struct dpif_flow_stats *);
static void subfacet_update_time(struct subfacet *, long long int used);
/* Storage for a single subfacet, to reduce malloc() time and space
* overhead. (A facet always has at least one subfacet and in the common
- * case has exactly one subfacet.) */
+ * case has exactly one subfacet. However, 'one_subfacet' may not
+ * always be valid, since it could have been removed after newer
+ * subfacets were pushed onto the 'subfacets' list.) */
struct subfacet one_subfacet;
+
+ long long int learn_rl; /* Rate limiter for facet_learn(). */
};
static struct facet *facet_create(struct rule_dpif *,
static void facet_push_stats(struct facet *);
static void facet_learn(struct facet *);
static void facet_account(struct facet *);
+static void push_all_stats(void);
+
+static struct subfacet *facet_get_subfacet(struct facet *);
static bool facet_is_controller_flow(struct facet *);
static void port_wait(struct ofport_dpif *);
static int set_cfm(struct ofport *, const struct cfm_settings *);
static void ofport_clear_priorities(struct ofport_dpif *);
+static void run_fast_rl(void);
struct dpif_completion {
struct list list_node;
static struct ofport_dpif *
odp_port_to_ofport(const struct dpif_backer *, uint32_t odp_port);
+static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto,
+ uint64_t delta);
+struct avg_subfacet_rates {
+ double add_rate; /* Moving average of new flows created per minute. */
+ double del_rate; /* Moving average of flows deleted per minute. */
+};
+static void show_dp_rates(struct ds *ds, const char *heading,
+ const struct avg_subfacet_rates *rates);
+static void exp_mavg(double *avg, int base, double new);
+
struct ofproto_dpif {
struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */
struct ofproto up;
struct hmap facets;
struct hmap subfacets;
struct governor *governor;
+ long long int consistency_rl;
/* Revalidation. */
struct table_dpif tables[N_TABLES];
struct sset ghost_ports; /* Ports with no datapath port. */
struct sset port_poll_set; /* Queued names for port_poll() reply. */
int port_poll_errno; /* Last errno for port_poll() reply. */
+
+ /* Per ofproto's dpif stats. */
+ uint64_t n_hit;
+ uint64_t n_missed;
+
+ /* Subfacet statistics.
+ *
+ * These keep track of the total number of subfacets added and deleted and
+ * flow life span. They are useful for computing the flow rates stats
+ * exposed via "ovs-appctl dpif/show". The goal is to learn about
+ * traffic patterns in ways that we can use later to improve Open vSwitch
+ * performance in new situations. */
+ long long int created; /* Time when it is created. */
+ unsigned int max_n_subfacet; /* Maximum number of flows */
+
+ /* The average number of subfacets... */
+ struct avg_subfacet_rates hourly; /* ...over the last hour. */
+ struct avg_subfacet_rates daily; /* ...over the last day. */
+ long long int last_minute; /* Last time 'hourly' was updated. */
+
+ /* Number of subfacets added or deleted since 'last_minute'. */
+ unsigned int subfacet_add_count;
+ unsigned int subfacet_del_count;
+
+ /* Number of subfacets added or deleted from 'created' to 'last_minute.' */
+ unsigned long long int total_subfacet_add_count;
+ unsigned long long int total_subfacet_del_count;
+
+ /* Sum of the number of milliseconds that each subfacet existed,
+ * over the subfacets that have been added and then later deleted. */
+ unsigned long long int total_subfacet_life_span;
+
+ /* Incremented by the number of currently existing subfacets, each
+ * time we pull statistics from the kernel. */
+ unsigned long long int total_subfacet_count;
+
+ /* Number of times we pull statistics from the kernel. */
+ unsigned long long int n_update_stats;
};
+static unsigned long long int avg_subfacet_life_span(
+ const struct ofproto_dpif *);
+static double avg_subfacet_count(const struct ofproto_dpif *ofproto);
+static void update_moving_averages(struct ofproto_dpif *ofproto);
+static void dpif_stats_update_hit_count(struct ofproto_dpif *ofproto,
+ uint64_t delta);
+static void update_max_subfacet_count(struct ofproto_dpif *ofproto);
/* Defer flow mod completion until "ovs-appctl ofproto/unclog"? (Useful only
* for debugging the asynchronous flow_mod implementation.) */
static struct ofport_dpif *get_odp_port(const struct ofproto_dpif *,
uint32_t odp_port);
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
- const struct ofpbuf *, ovs_be16 initial_tci,
- struct ds *);
+ const struct ofpbuf *,
+ const struct initial_vals *, struct ds *);
/* Packet processing. */
static void update_learning_table(struct ofproto_dpif *,
static int
type_run(const char *type)
{
+ static long long int push_timer = LLONG_MIN;
struct dpif_backer *backer;
char *devname;
int error;
dpif_run(backer->dpif);
+ /* The most natural place to push facet statistics is when they're pulled
+ * from the datapath. However, when there are many flows in the datapath,
+ * this expensive operation can occur so frequently, that it reduces our
+ * ability to quickly set up flows. To reduce the cost, we push statistics
+ * here instead. */
+ if (time_msec() > push_timer) {
+ push_timer = time_msec() + 2000;
+ push_all_stats();
+ }
+
if (backer->need_revalidate
|| !tag_set_is_empty(&backer->revalidate_set)) {
struct tag_set revalidate_set = backer->revalidate_set;
backer->need_revalidate = 0;
HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
- struct facet *facet;
+ struct facet *facet, *next;
if (ofproto->backer != backer) {
continue;
}
- HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) {
if (need_revalidate
|| tag_set_intersects(&revalidate_set, facet->tags)) {
facet_revalidate(facet);
+ run_fast_rl();
}
}
}
}
static int
-type_run_fast(const char *type)
+dpif_backer_run_fast(struct dpif_backer *backer, int max_batch)
{
- struct dpif_backer *backer;
unsigned int work;
- backer = shash_find_data(&all_dpif_backers, type);
- if (!backer) {
- /* This is not necessarily a problem, since backers are only
- * created on demand. */
- return 0;
- }
-
/* Handle one or more batches of upcalls, until there's nothing left to do
* or until we do a fixed total amount of work.
*
* optimizations can make major improvements on some benchmarks and
* presumably for real traffic as well. */
work = 0;
- while (work < FLOW_MISS_MAX_BATCH) {
- int retval = handle_upcalls(backer, FLOW_MISS_MAX_BATCH - work);
+ while (work < max_batch) {
+ int retval = handle_upcalls(backer, max_batch - work);
if (retval <= 0) {
return -retval;
}
return 0;
}
+static int
+type_run_fast(const char *type)
+{
+ struct dpif_backer *backer;
+
+ backer = shash_find_data(&all_dpif_backers, type);
+ if (!backer) {
+ /* This is not necessarily a problem, since backers are only
+ * created on demand. */
+ return 0;
+ }
+
+ return dpif_backer_run_fast(backer, FLOW_MISS_MAX_BATCH);
+}
+
+static void
+run_fast_rl(void)
+{
+ static long long int port_rl = LLONG_MIN;
+ static unsigned int backer_rl = 0;
+
+ if (time_msec() >= port_rl) {
+ struct ofproto_dpif *ofproto;
+ struct ofport_dpif *ofport;
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+
+ HMAP_FOR_EACH (ofport, up.hmap_node, &ofproto->up.ports) {
+ port_run_fast(ofport);
+ }
+ }
+ port_rl = time_msec() + 200;
+ }
+
+ /* XXX: We have to be careful not to do too much work in this function. If
+ * we call dpif_backer_run_fast() too often, or with too large a batch,
+ * performance improves signifcantly, but at a cost. It's possible for the
+ * number of flows in the datapath to increase without bound, and for poll
+ * loops to take 10s of seconds. The correct solution to this problem,
+ * long term, is to separate flow miss handling into it's own thread so it
+ * isn't affected by revalidations, and expirations. Until then, this is
+ * the best we can do. */
+ if (++backer_rl >= 10) {
+ struct shash_node *node;
+
+ backer_rl = 0;
+ SHASH_FOR_EACH (node, &all_dpif_backers) {
+ dpif_backer_run_fast(node->data, 1);
+ }
+ }
+}
+
static void
type_wait(const char *type)
{
hmap_init(&ofproto->facets);
hmap_init(&ofproto->subfacets);
ofproto->governor = NULL;
+ ofproto->consistency_rl = LLONG_MIN;
for (i = 0; i < N_TABLES; i++) {
struct table_dpif *table = &ofproto->tables[i];
error = add_internal_flows(ofproto);
ofproto->up.tables[TBL_INTERNAL].flags = OFTABLE_HIDDEN | OFTABLE_READONLY;
+ ofproto->n_hit = 0;
+ ofproto->n_missed = 0;
+
+ ofproto->max_n_subfacet = 0;
+ ofproto->created = time_msec();
+ ofproto->last_minute = ofproto->created;
+ memset(&ofproto->hourly, 0, sizeof ofproto->hourly);
+ memset(&ofproto->daily, 0, sizeof ofproto->daily);
+ ofproto->subfacet_add_count = 0;
+ ofproto->subfacet_del_count = 0;
+ ofproto->total_subfacet_add_count = 0;
+ ofproto->total_subfacet_del_count = 0;
+ ofproto->total_subfacet_life_span = 0;
+ ofproto->total_subfacet_count = 0;
+ ofproto->n_update_stats = 0;
+
return error;
}
mac_learning_run(ofproto->ml, &ofproto->backer->revalidate_set);
/* Check the consistency of a random facet, to aid debugging. */
- if (!hmap_is_empty(&ofproto->facets)
+ if (time_msec() >= ofproto->consistency_rl
+ && !hmap_is_empty(&ofproto->facets)
&& !ofproto->backer->need_revalidate) {
struct facet *facet;
+ ofproto->consistency_rl = time_msec() + 250;
+
facet = CONTAINER_OF(hmap_random_node(&ofproto->facets),
struct facet, hmap_node);
if (!tag_set_intersects(&ofproto->backer->revalidate_set,
return error;
}
-static int
-get_cfm_fault(const struct ofport *ofport_)
-{
- struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
-
- return ofport->cfm ? cfm_get_fault(ofport->cfm) : -1;
-}
-
-static int
-get_cfm_opup(const struct ofport *ofport_)
-{
- struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
-
- return ofport->cfm ? cfm_get_opup(ofport->cfm) : -1;
-}
-
-static int
-get_cfm_remote_mpids(const struct ofport *ofport_, const uint64_t **rmps,
- size_t *n_rmps)
+static bool
+get_cfm_status(const struct ofport *ofport_,
+ struct ofproto_cfm_status *status)
{
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
if (ofport->cfm) {
- cfm_get_remote_mpids(ofport->cfm, rmps, n_rmps);
- return 0;
+ status->faults = cfm_get_fault(ofport->cfm);
+ status->remote_opstate = cfm_get_opup(ofport->cfm);
+ status->health = cfm_get_health(ofport->cfm);
+ cfm_get_remote_mpids(ofport->cfm, &status->rmps, &status->n_rmps);
+ return true;
} else {
- return -1;
+ return false;
}
}
-
-static int
-get_cfm_health(const struct ofport *ofport_)
-{
- struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
-
- return ofport->cfm ? cfm_get_health(ofport->cfm) : -1;
-}
\f
/* Spanning Tree. */
return 0;
}
+ push_all_stats();
+
*packets = mirror->packet_count;
*bytes = mirror->byte_count;
struct ofport_dpif *ofport = ofport_dpif_cast(ofport_);
int error;
+ push_all_stats();
+
error = netdev_get_stats(ofport->up.netdev, stats);
if (!error && ofport_->ofp_port == OFPP_LOCAL) {
enum odp_key_fitness key_fitness;
const struct nlattr *key;
size_t key_len;
- ovs_be16 initial_tci;
+ struct initial_vals initial_vals;
struct list packets;
enum dpif_upcall_type upcall_type;
uint32_t odp_in_port;
struct flow_miss_op {
struct dpif_op dpif_op;
- struct subfacet *subfacet; /* Subfacet */
void *garbage; /* Pointer to pass to free(), NULL if none. */
uint64_t stub[1024 / 8]; /* Temporary buffer. */
};
init_flow_miss_execute_op(struct flow_miss *miss, struct ofpbuf *packet,
struct flow_miss_op *op)
{
- if (miss->flow.vlan_tci != miss->initial_tci) {
+ if (miss->flow.vlan_tci != miss->initial_vals.vlan_tci) {
/* This packet was received on a VLAN splinter port. We
* added a VLAN to the packet to make the packet resemble
* the flow, but the actions were composed assuming that
eth_pop_vlan(packet);
}
- op->subfacet = NULL;
op->garbage = NULL;
op->dpif_op.type = DPIF_OP_EXECUTE;
op->dpif_op.u.execute.key = miss->key;
dpif_flow_stats_extract(&miss->flow, packet, now, &stats);
rule_credit_stats(rule, &stats);
- action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
- rule, 0, packet);
+ action_xlate_ctx_init(&ctx, ofproto, &miss->flow,
+ &miss->initial_vals, rule, 0, packet);
ctx.resubmit_stats = &stats;
xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
struct dpif_execute *execute = &op->dpif_op.u.execute;
init_flow_miss_execute_op(miss, packet, op);
- op->subfacet = subfacet;
if (!subfacet->slow) {
execute->actions = subfacet->actions;
execute->actions_len = subfacet->actions_len;
struct flow_miss_op *op = &ops[(*n_ops)++];
struct dpif_flow_put *put = &op->dpif_op.u.flow_put;
- op->subfacet = subfacet;
+ subfacet->path = want_path;
+
op->garbage = NULL;
op->dpif_op.type = DPIF_OP_FLOW_PUT;
put->flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
* flow->vlan_tci correctly for the VLAN of the VLAN splinter port, and pushes
* a VLAN header onto 'packet' (if it is nonnull).
*
- * Optionally, if nonnull, sets '*initial_tci' to the VLAN TCI with which the
- * packet was really received, that is, the actual VLAN TCI extracted by
- * odp_flow_key_to_flow(). (This differs from the value returned in
- * flow->vlan_tci only for packets received on VLAN splinters.)
+ * Optionally, if 'initial_vals' is nonnull, sets 'initial_vals->vlan_tci'
+ * to the VLAN TCI with which the packet was really received, that is, the
+ * actual VLAN TCI extracted by odp_flow_key_to_flow(). (This differs from
+ * the value returned in flow->vlan_tci only for packets received on
+ * VLAN splinters.) Also, if received on an IP tunnel, sets
+ * 'initial_vals->tunnel_ip_tos' to the tunnel's IP TOS.
*
* Similarly, this function also includes some logic to help with tunnels. It
* may modify 'flow' as necessary to make the tunneling implementation
const struct nlattr *key, size_t key_len,
struct flow *flow, enum odp_key_fitness *fitnessp,
struct ofproto_dpif **ofproto, uint32_t *odp_in_port,
- ovs_be16 *initial_tci)
+ struct initial_vals *initial_vals)
{
const struct ofport_dpif *port;
enum odp_key_fitness fitness;
goto exit;
}
- if (initial_tci) {
- *initial_tci = flow->vlan_tci;
+ if (initial_vals) {
+ initial_vals->vlan_tci = flow->vlan_tci;
+ initial_vals->tunnel_ip_tos = flow->tunnel.ip_tos;
}
if (odp_in_port) {
error = ofproto_receive(backer, upcall->packet, upcall->key,
upcall->key_len, &flow, &miss->key_fitness,
- &ofproto, &odp_in_port, &miss->initial_tci);
+ &ofproto, &odp_in_port, &miss->initial_vals);
if (error == ENODEV) {
struct drop_key *drop_key;
if (error) {
continue;
}
+
+ ofproto->n_missed++;
flow_extract(upcall->packet, flow.skb_priority, flow.skb_mark,
&flow.tunnel, flow.in_port, &miss->flow);
}
dpif_operate(backer->dpif, dpif_ops, n_ops);
- /* Free memory and update facets. */
+ /* Free memory. */
for (i = 0; i < n_ops; i++) {
- struct flow_miss_op *op = &flow_miss_ops[i];
-
- switch (op->dpif_op.type) {
- case DPIF_OP_EXECUTE:
- break;
-
- case DPIF_OP_FLOW_PUT:
- if (!op->dpif_op.error) {
- op->subfacet->path = subfacet_want_path(op->subfacet->slow);
- }
- break;
-
- case DPIF_OP_FLOW_DEL:
- NOT_REACHED();
- }
-
- free(op->garbage);
+ free(flow_miss_ops[i].garbage);
}
hmap_destroy(&todo);
}
continue;
}
+ /* Keep track of the max number of flows per ofproto_dpif. */
+ update_max_subfacet_count(ofproto);
+
/* Expire subfacets that have been idle too long. */
dp_max_idle = subfacet_max_idle(ofproto);
expire_subfacets(ofproto, dp_max_idle);
facet_account(facet);
facet->accounted_bytes = facet->byte_count;
}
- facet_push_stats(facet);
}
/* 'key' with length 'key_len' bytes is a flow in 'dpif' that we know nothing
* avoided by calling update_stats() whenever rules are created or
* deleted. However, the performance impact of making so many calls to the
* datapath do not justify the benefit of having perfectly accurate statistics.
+ *
+ * In addition, this function maintains per ofproto flow hit counts. The patch
+ * port is not treated specially. e.g. A packet ingress from br0 patched into
+ * br1 will increase the hit count of br0 by 1, however, does not affect
+ * the hit or miss counts of br1.
*/
static void
update_stats(struct dpif_backer *backer)
continue;
}
+ ofproto->total_subfacet_count += hmap_count(&ofproto->subfacets);
+ ofproto->n_update_stats++;
+ update_moving_averages(ofproto);
+
ofport = get_ofp_port(ofproto, flow.in_port);
if (ofport && ofport->tnl_port) {
netdev_vport_inc_rx(ofport->up.netdev, stats);
}
key_hash = odp_flow_key_hash(key, key_len);
- subfacet = subfacet_find(ofproto, key, key_len, key_hash, &flow);
+ subfacet = subfacet_find(ofproto, key, key_len, key_hash);
switch (subfacet ? subfacet->path : SF_NOT_INSTALLED) {
case SF_FAST_PATH:
+ /* Update ofproto_dpif's hit count. */
+ if (stats->n_packets > subfacet->dp_packet_count) {
+ uint64_t delta = stats->n_packets - subfacet->dp_packet_count;
+ dpif_stats_update_hit_count(ofproto, delta);
+ }
+
update_subfacet_stats(subfacet, stats);
break;
delete_unexpected_flow(ofproto, key, key_len);
break;
}
+ run_fast_rl();
}
dpif_flow_dump_done(&dump);
}
netflow_flow_init(&facet->nf_flow);
netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used);
+ facet->learn_rl = time_msec() + 500;
+
return facet;
}
facet_learn(struct facet *facet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ struct subfacet *subfacet= CONTAINER_OF(list_front(&facet->subfacets),
+ struct subfacet, list_node);
+ long long int now = time_msec();
struct action_xlate_ctx ctx;
+ if (!facet->has_fin_timeout && now < facet->learn_rl) {
+ return;
+ }
+
+ facet->learn_rl = now + 500;
+
if (!facet->has_learn
&& !facet->has_normal
&& (!facet->has_fin_timeout
}
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
- facet->flow.vlan_tci,
+ &subfacet->initial_vals,
facet->rule, facet->tcp_flags, NULL);
ctx.may_learn = true;
xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts,
facet_account(struct facet *facet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
- struct subfacet *subfacet;
+ struct subfacet *subfacet = facet_get_subfacet(facet);
const struct nlattr *a;
unsigned int left;
ovs_be16 vlan_tci;
*
* We use the actions from an arbitrary subfacet because they should all
* be equally valid for our purpose. */
- subfacet = CONTAINER_OF(list_front(&facet->subfacets),
- struct subfacet, list_node);
vlan_tci = facet->flow.vlan_tci;
NL_ATTR_FOR_EACH_UNSAFE (a, left,
subfacet->actions, subfacet->actions_len) {
|| tag_set_intersects(&ofproto->backer->revalidate_set,
facet->tags))) {
facet_revalidate(facet);
+
+ /* facet_revalidate() may have destroyed 'facet'. */
+ facet = facet_find(ofproto, flow, hash);
}
return facet;
}
+/* Return a subfacet from 'facet'. A facet consists of one or more
+ * subfacets, and this function returns one of them. */
+static struct subfacet *facet_get_subfacet(struct facet *facet)
+{
+ return CONTAINER_OF(list_front(&facet->subfacets), struct subfacet,
+ list_node);
+}
+
static const char *
subfacet_path_to_string(enum subfacet_path path)
{
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
enum subfacet_path want_path;
- struct odputil_keybuf keybuf;
struct action_xlate_ctx ctx;
- struct ofpbuf key;
struct ds s;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
- subfacet->initial_tci, rule, 0, NULL);
+ &subfacet->initial_vals, rule, 0, NULL);
xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len,
&odp_actions);
}
ds_init(&s);
- subfacet_get_key(subfacet, &keybuf, &key);
- odp_flow_key_format(key.data, key.size, &s);
+ odp_flow_key_format(subfacet->key, subfacet->key_len, &s);
ds_put_cstr(&s, ": inconsistency in subfacet");
if (want_path != subfacet->path) {
* 'facet' to the new rule and recompiles its actions.
*
* - If the rule found is the same as 'facet''s current rule, leaves 'facet'
- * where it is and recompiles its actions anyway. */
+ * where it is and recompiles its actions anyway.
+ *
+ * - If any of 'facet''s subfacets correspond to a new flow according to
+ * ofproto_receive(), 'facet' is removed. */
static void
facet_revalidate(struct facet *facet)
{
COVERAGE_INC(facet_revalidate);
+ /* Check that child subfacets still correspond to this facet. Tunnel
+ * configuration changes could cause a subfacet's OpenFlow in_port to
+ * change. */
+ LIST_FOR_EACH (subfacet, list_node, &facet->subfacets) {
+ struct ofproto_dpif *recv_ofproto;
+ struct flow recv_flow;
+ int error;
+
+ error = ofproto_receive(ofproto->backer, NULL, subfacet->key,
+ subfacet->key_len, &recv_flow, NULL,
+ &recv_ofproto, NULL, NULL);
+ if (error
+ || recv_ofproto != ofproto
+ || memcmp(&recv_flow, &facet->flow, sizeof recv_flow)) {
+ facet_remove(facet);
+ return;
+ }
+ }
+
new_rule = rule_dpif_lookup(ofproto, &facet->flow);
/* Calculate new datapath actions.
enum slow_path_reason slow;
action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
- subfacet->initial_tci, new_rule, 0, NULL);
+ &subfacet->initial_vals, new_rule, 0, NULL);
xlate_actions(&ctx, new_rule->up.ofpacts, new_rule->up.ofpacts_len,
&odp_actions);
facet->prev_byte_count = facet->byte_count;
facet->prev_used = facet->used;
- flow_push_stats(facet->rule, &facet->flow, &stats);
+ flow_push_stats(facet, &stats);
update_mirror_stats(ofproto_dpif_cast(facet->rule->up.ofproto),
facet->mirrors, stats.n_packets, stats.n_bytes);
}
}
+static void
+push_all_stats(void)
+{
+ static long long int rl = LLONG_MIN;
+ struct ofproto_dpif *ofproto;
+
+ if (time_msec() < rl) {
+ return;
+ }
+
+ HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
+ struct facet *facet;
+
+ HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) {
+ facet_push_stats(facet);
+ run_fast_rl();
+ }
+ }
+
+ rl = time_msec() + 100;
+}
+
static void
rule_credit_stats(struct rule_dpif *rule, const struct dpif_flow_stats *stats)
{
ofproto_rule_update_used(&rule->up, stats->used);
}
-/* Pushes flow statistics to the rules which 'flow' resubmits into given
- * 'rule''s actions and mirrors. */
+/* Pushes flow statistics to the rules which 'facet->flow' resubmits
+ * into given 'facet->rule''s actions and mirrors. */
static void
-flow_push_stats(struct rule_dpif *rule,
- const struct flow *flow, const struct dpif_flow_stats *stats)
+flow_push_stats(struct facet *facet, const struct dpif_flow_stats *stats)
{
+ struct rule_dpif *rule = facet->rule;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
+ struct subfacet *subfacet = facet_get_subfacet(facet);
struct action_xlate_ctx ctx;
ofproto_rule_update_used(&rule->up, stats->used);
- action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
- 0, NULL);
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ &subfacet->initial_vals, rule, 0, NULL);
ctx.resubmit_stats = stats;
xlate_actions_for_side_effects(&ctx, rule->up.ofpacts,
rule->up.ofpacts_len);
static struct subfacet *
subfacet_find(struct ofproto_dpif *ofproto,
- const struct nlattr *key, size_t key_len, uint32_t key_hash,
- const struct flow *flow)
+ const struct nlattr *key, size_t key_len, uint32_t key_hash)
{
struct subfacet *subfacet;
HMAP_FOR_EACH_WITH_HASH (subfacet, hmap_node, key_hash,
&ofproto->subfacets) {
- if (subfacet->key
- ? (subfacet->key_len == key_len
- && !memcmp(key, subfacet->key, key_len))
- : flow_equal(flow, &subfacet->facet->flow)) {
+ if (subfacet->key_len == key_len
+ && !memcmp(key, subfacet->key, key_len)) {
return subfacet;
}
}
if (list_is_empty(&facet->subfacets)) {
subfacet = &facet->one_subfacet;
} else {
- subfacet = subfacet_find(ofproto, key, key_len, key_hash,
- &facet->flow);
+ subfacet = subfacet_find(ofproto, key, key_len, key_hash);
if (subfacet) {
if (subfacet->facet == facet) {
return subfacet;
list_push_back(&facet->subfacets, &subfacet->list_node);
subfacet->facet = facet;
subfacet->key_fitness = key_fitness;
- if (key_fitness != ODP_FIT_PERFECT) {
- subfacet->key = xmemdup(key, key_len);
- subfacet->key_len = key_len;
- } else {
- subfacet->key = NULL;
- subfacet->key_len = 0;
- }
+ subfacet->key = xmemdup(key, key_len);
+ subfacet->key_len = key_len;
subfacet->used = now;
+ subfacet->created = now;
subfacet->dp_packet_count = 0;
subfacet->dp_byte_count = 0;
subfacet->actions_len = 0;
? SLOW_MATCH
: 0);
subfacet->path = SF_NOT_INSTALLED;
- subfacet->initial_tci = miss->initial_tci;
+ subfacet->initial_vals = miss->initial_vals;
subfacet->odp_in_port = miss->odp_in_port;
+ ofproto->subfacet_add_count++;
return subfacet;
}
struct facet *facet = subfacet->facet;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
+ /* Update ofproto stats before uninstall the subfacet. */
+ ofproto->subfacet_del_count++;
+ ofproto->total_subfacet_life_span += (time_msec() - subfacet->created);
+
subfacet_uninstall(subfacet);
hmap_remove(&ofproto->subfacets, &subfacet->hmap_node);
list_remove(&subfacet->list_node);
subfacet_destroy_batch(struct ofproto_dpif *ofproto,
struct subfacet **subfacets, int n)
{
- struct odputil_keybuf keybufs[SUBFACET_DESTROY_MAX_BATCH];
struct dpif_op ops[SUBFACET_DESTROY_MAX_BATCH];
struct dpif_op *opsp[SUBFACET_DESTROY_MAX_BATCH];
- struct ofpbuf keys[SUBFACET_DESTROY_MAX_BATCH];
struct dpif_flow_stats stats[SUBFACET_DESTROY_MAX_BATCH];
int i;
for (i = 0; i < n; i++) {
ops[i].type = DPIF_OP_FLOW_DEL;
- subfacet_get_key(subfacets[i], &keybufs[i], &keys[i]);
- ops[i].u.flow_del.key = keys[i].data;
- ops[i].u.flow_del.key_len = keys[i].size;
+ ops[i].u.flow_del.key = subfacets[i]->key;
+ ops[i].u.flow_del.key_len = subfacets[i]->key_len;
ops[i].u.flow_del.stats = &stats[i];
opsp[i] = &ops[i];
}
subfacet_reset_dp_stats(subfacets[i], &stats[i]);
subfacets[i]->path = SF_NOT_INSTALLED;
subfacet_destroy(subfacets[i]);
- }
-}
-
-/* Initializes 'key' with the sequence of OVS_KEY_ATTR_* Netlink attributes
- * that can be used to refer to 'subfacet'. The caller must provide 'keybuf'
- * for use as temporary storage. */
-static void
-subfacet_get_key(struct subfacet *subfacet, struct odputil_keybuf *keybuf,
- struct ofpbuf *key)
-{
-
- if (!subfacet->key) {
- struct flow *flow = &subfacet->facet->flow;
-
- ofpbuf_use_stack(key, keybuf, sizeof *keybuf);
- odp_flow_key_from_flow(key, flow, subfacet->odp_in_port);
- } else {
- ofpbuf_use_const(key, subfacet->key, subfacet->key_len);
+ run_fast_rl();
}
}
struct action_xlate_ctx ctx;
- action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
- rule, 0, packet);
+ action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
+ &subfacet->initial_vals, rule, 0, packet);
xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, odp_actions);
facet->tags = ctx.tags;
facet->has_learn = ctx.has_learn;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto);
enum subfacet_path path = subfacet_want_path(slow);
uint64_t slow_path_stub[128 / 8];
- struct odputil_keybuf keybuf;
enum dpif_flow_put_flags flags;
- struct ofpbuf key;
int ret;
flags = DPIF_FP_CREATE | DPIF_FP_MODIFY;
&actions, &actions_len);
}
- subfacet_get_key(subfacet, &keybuf, &key);
- ret = dpif_flow_put(ofproto->backer->dpif, flags, key.data, key.size,
- actions, actions_len, stats);
+ ret = dpif_flow_put(ofproto->backer->dpif, flags, subfacet->key,
+ subfacet->key_len, actions, actions_len, stats);
if (stats) {
subfacet_reset_dp_stats(subfacet, stats);
if (subfacet->path != SF_NOT_INSTALLED) {
struct rule_dpif *rule = subfacet->facet->rule;
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
- struct odputil_keybuf keybuf;
struct dpif_flow_stats stats;
- struct ofpbuf key;
int error;
- subfacet_get_key(subfacet, &keybuf, &key);
- error = dpif_flow_del(ofproto->backer->dpif,
- key.data, key.size, &stats);
+ error = dpif_flow_del(ofproto->backer->dpif, subfacet->key,
+ subfacet->key_len, &stats);
subfacet_reset_dp_stats(subfacet, &stats);
if (!error) {
subfacet_update_stats(subfacet, &stats);
facet->packet_count += stats->n_packets;
facet->byte_count += stats->n_bytes;
facet->tcp_flags |= stats->tcp_flags;
- facet_push_stats(facet);
netflow_flow_update_flags(&facet->nf_flow, stats->tcp_flags);
}
}
struct rule_dpif *rule = rule_dpif_cast(rule_);
struct facet *facet;
+ push_all_stats();
+
/* Start from historical data for 'rule' itself that are no longer tracked
* in facets. This counts, for example, facets that have expired. */
*packets = rule->packet_count;
struct ofpbuf *packet)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
-
+ struct initial_vals initial_vals;
struct dpif_flow_stats stats;
-
struct action_xlate_ctx ctx;
uint64_t odp_actions_stub[1024 / 8];
struct ofpbuf odp_actions;
dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
rule_credit_stats(rule, &stats);
+ initial_vals.vlan_tci = flow->vlan_tci;
+ initial_vals.tunnel_ip_tos = flow->tunnel.ip_tos;
ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
- action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
+ action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals,
rule, stats.tcp_flags, packet);
ctx.resubmit_stats = &stats;
xlate_actions(&ctx, rule->up.ofpacts, rule->up.ofpacts_len, &odp_actions);
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 19);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20);
if (!ofport) {
xlate_report(ctx, "Nonexistent output port");
ctx->flow.nw_tos |= pdscp->dscp;
}
- odp_port = ofp_port_to_odp_port(ctx->ofproto, ofp_port);
if (ofport->tnl_port) {
odp_port = tnl_port_send(ofport->tnl_port, &ctx->flow);
if (odp_port == OVSP_NONE) {
commit_odp_tunnel_action(&ctx->flow, &ctx->base_flow,
ctx->odp_actions);
} else {
+ odp_port = ofport->odp_port;
out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
ctx->flow.vlan_tci);
if (out_port != odp_port) {
ctx->flow.vlan_tci = htons(0);
}
+ ctx->flow.skb_mark &= ~IPSEC_MARK;
}
commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
compose_output_action__(ctx, ofp_port, true);
}
+static void
+tag_the_flow(struct action_xlate_ctx *ctx, struct rule_dpif *rule)
+{
+ struct ofproto_dpif *ofproto = ctx->ofproto;
+ uint8_t table_id = ctx->table_id;
+
+ if (table_id > 0 && table_id < N_TABLES) {
+ struct table_dpif *table = &ofproto->tables[table_id];
+ if (table->other_table) {
+ ctx->tags |= (rule && rule->tag
+ ? rule->tag
+ : rule_calculate_tag(&ctx->flow,
+ &table->other_table->mask,
+ table->basis));
+ }
+ }
+}
+
+/* Common rule processing in one place to avoid duplicating code. */
+static struct rule_dpif *
+ctx_rule_hooks(struct action_xlate_ctx *ctx, struct rule_dpif *rule,
+ bool may_packet_in)
+{
+ if (ctx->resubmit_hook) {
+ ctx->resubmit_hook(ctx, rule);
+ }
+ if (rule == NULL && may_packet_in) {
+ /* XXX
+ * check if table configuration flags
+ * OFPTC_TABLE_MISS_CONTROLLER, default.
+ * OFPTC_TABLE_MISS_CONTINUE,
+ * OFPTC_TABLE_MISS_DROP
+ * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
+ */
+ rule = rule_dpif_miss_rule(ctx->ofproto, &ctx->flow);
+ }
+ if (rule && ctx->resubmit_stats) {
+ rule_credit_stats(rule, ctx->resubmit_stats);
+ }
+ return rule;
+}
+
static void
xlate_table_action(struct action_xlate_ctx *ctx,
uint16_t in_port, uint8_t table_id, bool may_packet_in)
{
if (ctx->recurse < MAX_RESUBMIT_RECURSION) {
- struct ofproto_dpif *ofproto = ctx->ofproto;
struct rule_dpif *rule;
- uint16_t old_in_port;
- uint8_t old_table_id;
+ uint16_t old_in_port = ctx->flow.in_port;
+ uint8_t old_table_id = ctx->table_id;
- old_table_id = ctx->table_id;
ctx->table_id = table_id;
/* Look up a flow with 'in_port' as the input port. */
- old_in_port = ctx->flow.in_port;
ctx->flow.in_port = in_port;
- rule = rule_dpif_lookup__(ofproto, &ctx->flow, table_id);
-
- /* Tag the flow. */
- if (table_id > 0 && table_id < N_TABLES) {
- struct table_dpif *table = &ofproto->tables[table_id];
- if (table->other_table) {
- ctx->tags |= (rule && rule->tag
- ? rule->tag
- : rule_calculate_tag(&ctx->flow,
- &table->other_table->mask,
- table->basis));
- }
- }
+ rule = rule_dpif_lookup__(ctx->ofproto, &ctx->flow, table_id);
+
+ tag_the_flow(ctx, rule);
/* Restore the original input port. Otherwise OFPP_NORMAL and
* OFPP_IN_PORT will have surprising behavior. */
ctx->flow.in_port = old_in_port;
- if (ctx->resubmit_hook) {
- ctx->resubmit_hook(ctx, rule);
- }
-
- if (rule == NULL && may_packet_in) {
- /* XXX
- * check if table configuration flags
- * OFPTC_TABLE_MISS_CONTROLLER, default.
- * OFPTC_TABLE_MISS_CONTINUE,
- * OFPTC_TABLE_MISS_DROP
- * When OF1.0, OFPTC_TABLE_MISS_CONTINUE is used. What to do?
- */
- rule = rule_dpif_miss_rule(ofproto, &ctx->flow);
- }
+ rule = ctx_rule_hooks(ctx, rule, may_packet_in);
if (rule) {
struct rule_dpif *old_rule = ctx->rule;
- if (ctx->resubmit_stats) {
- rule_credit_stats(rule, ctx->resubmit_stats);
- }
-
ctx->recurse++;
ctx->rule = rule;
do_xlate_actions(rule->up.ofpacts, rule->up.ofpacts_len, ctx);
tc = (ctx->flow.nw_tos & IP_DSCP_MASK) >> 2;
ttl = ctx->flow.nw_ttl ? ctx->flow.nw_ttl : 0x40;
ctx->flow.mpls_lse = set_mpls_lse_values(ttl, tc, 1, label);
- ctx->flow.encap_dl_type = ctx->flow.dl_type;
ctx->flow.mpls_depth = 1;
}
ctx->flow.dl_type = eth_type;
ctx->flow.mpls_lse = htonl(0);
if (!ctx->flow.mpls_depth) {
ctx->flow.dl_type = eth_type;
- ctx->flow.encap_dl_type = htons(0);
}
}
}
}
}
+static bool
+execute_set_mpls_ttl_action(struct action_xlate_ctx *ctx, uint8_t ttl)
+{
+ if (!eth_type_mpls(ctx->flow.dl_type)) {
+ return true;
+ }
+
+ set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
+ return false;
+}
+
+static bool
+execute_dec_mpls_ttl_action(struct action_xlate_ctx *ctx)
+{
+ uint8_t ttl = mpls_lse_to_ttl(ctx->flow.mpls_lse);
+
+ if (!eth_type_mpls(ctx->flow.dl_type)) {
+ return false;
+ }
+
+ if (ttl > 1) {
+ ttl--;
+ set_mpls_lse_ttl(&ctx->flow.mpls_lse, ttl);
+ return false;
+ } else {
+ execute_controller_action(ctx, UINT16_MAX, OFPR_INVALID_TTL, 0);
+
+ /* Stop processing for current table. */
+ return true;
+ }
+}
+
static void
xlate_output_action(struct action_xlate_ctx *ctx,
uint16_t port, uint16_t max_len, bool may_packet_in)
return true;
}
+static bool
+tunnel_ecn_ok(struct action_xlate_ctx *ctx)
+{
+ if (is_ip_any(&ctx->base_flow)
+ && (ctx->base_flow.tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE) {
+ if ((ctx->base_flow.nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
+ VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE"
+ " but is not ECN capable");
+ return false;
+ } else {
+ /* Set the ECN CE value in the tunneled packet. */
+ ctx->flow.nw_tos |= IP_ECN_CE;
+ }
+ }
+
+ return true;
+}
+
static void
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct action_xlate_ctx *ctx)
was_evictable = ctx->rule->up.evictable;
ctx->rule->up.evictable = false;
}
+
+ do_xlate_actions_again:
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
struct ofpact_controller *controller;
const struct ofpact_metadata *metadata;
break;
case OFPACT_SET_IPV4_SRC:
- ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
+ ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
+ }
break;
case OFPACT_SET_IPV4_DST:
- ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
+ ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
+ }
break;
case OFPACT_SET_IPV4_DSCP:
break;
case OFPACT_SET_L4_SRC_PORT:
- ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ if (is_ip_any(&ctx->flow)) {
+ ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
+ }
break;
case OFPACT_SET_L4_DST_PORT:
- ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ if (is_ip_any(&ctx->flow)) {
+ ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
+ }
break;
case OFPACT_RESUBMIT:
nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
break;
+ case OFPACT_STACK_PUSH:
+ nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), &ctx->flow,
+ &ctx->stack);
+ break;
+
+ case OFPACT_STACK_POP:
+ nxm_execute_stack_pop(ofpact_get_STACK_POP(a), &ctx->flow,
+ &ctx->stack);
+ break;
+
case OFPACT_PUSH_MPLS:
execute_mpls_push_action(ctx, ofpact_get_PUSH_MPLS(a)->ethertype);
break;
execute_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
break;
+ case OFPACT_SET_MPLS_TTL:
+ if (execute_set_mpls_ttl_action(ctx, ofpact_get_SET_MPLS_TTL(a)->ttl)) {
+ goto out;
+ }
+ break;
+
+ case OFPACT_DEC_MPLS_TTL:
+ if (execute_dec_mpls_ttl_action(ctx)) {
+ goto out;
+ }
+ break;
+
case OFPACT_DEC_TTL:
if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) {
goto out;
break;
case OFPACT_GOTO_TABLE: {
- /* XXX remove recursion */
- /* It is assumed that goto-table is last action */
+ /* It is assumed that goto-table is the last action. */
struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
+ struct rule_dpif *rule;
+
ovs_assert(ctx->table_id < ogt->table_id);
- xlate_table_action(ctx, ctx->flow.in_port, ogt->table_id, true);
+
+ ctx->table_id = ogt->table_id;
+
+ /* Look up a flow from the new table. */
+ rule = rule_dpif_lookup__(ctx->ofproto, &ctx->flow, ctx->table_id);
+
+ tag_the_flow(ctx, rule);
+
+ rule = ctx_rule_hooks(ctx, rule, true);
+
+ if (rule) {
+ if (ctx->rule) {
+ ctx->rule->up.evictable = was_evictable;
+ }
+ ctx->rule = rule;
+ was_evictable = rule->up.evictable;
+ rule->up.evictable = false;
+
+ /* Tail recursion removal. */
+ ofpacts = rule->up.ofpacts;
+ ofpacts_len = rule->up.ofpacts_len;
+ goto do_xlate_actions_again;
+ }
break;
}
}
static void
action_xlate_ctx_init(struct action_xlate_ctx *ctx,
struct ofproto_dpif *ofproto, const struct flow *flow,
- ovs_be16 initial_tci, struct rule_dpif *rule,
+ const struct initial_vals *initial_vals,
+ struct rule_dpif *rule,
uint8_t tcp_flags, const struct ofpbuf *packet)
{
ovs_be64 initial_tun_id = flow->tunnel.tun_id;
ctx->flow = *flow;
memset(&ctx->flow.tunnel, 0, sizeof ctx->flow.tunnel);
ctx->base_flow = ctx->flow;
- ctx->base_flow.vlan_tci = initial_tci;
+ ctx->base_flow.vlan_tci = initial_vals->vlan_tci;
+ ctx->base_flow.tunnel.ip_tos = initial_vals->tunnel_ip_tos;
ctx->flow.tunnel.tun_id = initial_tun_id;
ctx->rule = rule;
ctx->packet = packet;
ctx->table_id = 0;
ctx->exit = false;
+ ofpbuf_use_stub(&ctx->stack, ctx->init_stack, sizeof ctx->init_stack);
+
if (ctx->ofproto->has_mirrors || hit_resubmit_limit) {
/* Do this conditionally because the copy is expensive enough that it
* shows up in profiles. */
ctx->slow |= special;
} else {
static struct vlog_rate_limit trace_rl = VLOG_RATE_LIMIT_INIT(1, 1);
- ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
+ struct initial_vals initial_vals;
uint32_t local_odp_port;
+ initial_vals.vlan_tci = ctx->base_flow.vlan_tci;
+ initial_vals.tunnel_ip_tos = ctx->base_flow.tunnel.ip_tos;
+
add_sflow_action(ctx);
- if (!in_port || may_receive(in_port, ctx)) {
+ if (tunnel_ecn_ok(ctx) && (!in_port || may_receive(in_port, ctx))) {
do_xlate_actions(ofpacts, ofpacts_len, ctx);
/* We've let OFPP_NORMAL and the learning action look at the
struct ds ds = DS_EMPTY_INITIALIZER;
ofproto_trace(ctx->ofproto, &orig_flow, ctx->packet,
- initial_tci, &ds);
+ &initial_vals, &ds);
VLOG_ERR("Trace triggered by excessive resubmit "
"recursion:\n%s", ds_cstr(&ds));
ds_destroy(&ds);
}
fix_sflow_action(ctx);
}
+
+ ofpbuf_uninit(&ctx->stack);
}
/* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts'
const struct ofpact *ofpacts, size_t ofpacts_len)
{
struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
+ struct initial_vals initial_vals;
struct odputil_keybuf keybuf;
struct dpif_flow_stats stats;
dpif_flow_stats_extract(flow, packet, time_msec(), &stats);
- action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, NULL,
+ initial_vals.vlan_tci = flow->vlan_tci;
+ initial_vals.tunnel_ip_tos = 0;
+ action_xlate_ctx_init(&ctx, ofproto, flow, &initial_vals, NULL,
packet_get_tcp_flags(packet, flow), packet);
ctx.resubmit_stats = &stats;
struct ofproto_dpif *ofproto;
struct ofpbuf odp_key;
struct ofpbuf *packet;
- ovs_be16 initial_tci;
+ struct initial_vals initial_vals;
struct ds result;
struct flow flow;
char *s;
goto exit;
}
- /* XXX: Since we allow the user to specify an ofproto, it's
- * possible they will specify a different ofproto than the one the
- * port actually belongs too. Ideally we should simply remove the
- * ability to specify the ofproto. */
+ /* The user might have specified the wrong ofproto but within the
+ * same backer. That's OK, ofproto_receive() can find the right
+ * one for us. */
if (ofproto_receive(ofproto->backer, NULL, odp_key.data,
- odp_key.size, &flow, NULL, NULL, NULL,
- &initial_tci)) {
+ odp_key.size, &flow, NULL, &ofproto, NULL,
+ &initial_vals)) {
unixctl_command_reply_error(conn, "Invalid flow");
goto exit;
}
+ ds_put_format(&result, "Bridge: %s\n", ofproto->up.name);
} else {
char *error_s;
goto exit;
}
- initial_tci = flow.vlan_tci;
+ initial_vals.vlan_tci = flow.vlan_tci;
+ initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
}
/* Generate a packet, if requested. */
flow_extract(packet, priority, mark, NULL, in_port, &flow);
flow.tunnel.tun_id = tun_id;
- initial_tci = flow.vlan_tci;
+ initial_vals.vlan_tci = flow.vlan_tci;
+ initial_vals.tunnel_ip_tos = flow.tunnel.ip_tos;
} else {
unixctl_command_reply_error(conn, "Bad command syntax");
goto exit;
}
- ofproto_trace(ofproto, &flow, packet, initial_tci, &result);
+ ofproto_trace(ofproto, &flow, packet, &initial_vals, &result);
unixctl_command_reply(conn, ds_cstr(&result));
exit:
static void
ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
- const struct ofpbuf *packet, ovs_be16 initial_tci,
- struct ds *ds)
+ const struct ofpbuf *packet,
+ const struct initial_vals *initial_vals, struct ds *ds)
{
struct rule_dpif *rule;
trace.flow = *flow;
ofpbuf_use_stub(&odp_actions,
odp_actions_stub, sizeof odp_actions_stub);
- action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
+ action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_vals,
rule, tcp_flags, packet);
trace.ctx.resubmit_hook = trace_resubmit;
trace.ctx.report_hook = trace_report;
static void
show_dp_format(const struct ofproto_dpif *ofproto, struct ds *ds)
{
- struct dpif_dp_stats s;
const struct shash_node **ports;
int i;
+ struct avg_subfacet_rates lifetime;
+ unsigned long long int minutes;
+ const int min_ms = 60 * 1000; /* milliseconds in one minute. */
- dpif_get_dp_stats(ofproto->backer->dpif, &s);
+ minutes = (time_msec() - ofproto->created) / min_ms;
+
+ if (minutes > 0) {
+ lifetime.add_rate = (double)ofproto->total_subfacet_add_count
+ / minutes;
+ lifetime.del_rate = (double)ofproto->total_subfacet_del_count
+ / minutes;
+ }else {
+ lifetime.add_rate = 0.0;
+ lifetime.del_rate = 0.0;
+ }
ds_put_format(ds, "%s (%s):\n", ofproto->up.name,
dpif_name(ofproto->backer->dpif));
- /* xxx It would be better to show bridge-specific stats instead
- * xxx of dp ones. */
ds_put_format(ds,
- "\tlookups: hit:%"PRIu64" missed:%"PRIu64" lost:%"PRIu64"\n",
- s.n_hit, s.n_missed, s.n_lost);
- ds_put_format(ds, "\tflows: %zu\n",
- hmap_count(&ofproto->subfacets));
+ "\tlookups: hit:%"PRIu64" missed:%"PRIu64"\n",
+ ofproto->n_hit, ofproto->n_missed);
+ ds_put_format(ds, "\tflows: cur: %zu, avg: %5.3f, max: %d,"
+ " life span: %llu(ms)\n",
+ hmap_count(&ofproto->subfacets),
+ avg_subfacet_count(ofproto),
+ ofproto->max_n_subfacet,
+ avg_subfacet_life_span(ofproto));
+ if (minutes >= 60) {
+ show_dp_rates(ds, "\t\thourly avg:", &ofproto->hourly);
+ }
+ if (minutes >= 60 * 24) {
+ show_dp_rates(ds, "\t\tdaily avg:", &ofproto->daily);
+ }
+ show_dp_rates(ds, "\t\toverall avg:", &lifetime);
ports = shash_sort(&ofproto->up.port_by_name);
for (i = 0; i < shash_count(&ofproto->up.port_by_name); i++) {
update_stats(ofproto->backer);
HMAP_FOR_EACH (subfacet, hmap_node, &ofproto->subfacets) {
- struct odputil_keybuf keybuf;
- struct ofpbuf key;
-
- subfacet_get_key(subfacet, &keybuf, &key);
- odp_flow_key_format(key.data, key.size, &ds);
+ odp_flow_key_format(subfacet->key, subfacet->key_len, &ds);
ds_put_format(&ds, ", packets:%"PRIu64", bytes:%"PRIu64", used:",
subfacet->dp_packet_count, subfacet->dp_byte_count);
}
ds_put_cstr(&ds, ", actions:");
- format_odp_actions(&ds, subfacet->actions, subfacet->actions_len);
+ if (subfacet->slow) {
+ uint64_t slow_path_stub[128 / 8];
+ const struct nlattr *actions;
+ size_t actions_len;
+
+ compose_slow_path(ofproto, &subfacet->facet->flow, subfacet->slow,
+ slow_path_stub, sizeof slow_path_stub,
+ &actions, &actions_len);
+ format_odp_actions(&ds, actions, actions_len);
+ } else {
+ format_odp_actions(&ds, subfacet->actions, subfacet->actions_len);
+ }
ds_put_char(&ds, '\n');
}
return OFPP_NONE;
}
}
+static unsigned long long int
+avg_subfacet_life_span(const struct ofproto_dpif *ofproto)
+{
+ unsigned long long int dc;
+ unsigned long long int avg;
+
+ dc = ofproto->total_subfacet_del_count + ofproto->subfacet_del_count;
+ avg = dc ? ofproto->total_subfacet_life_span / dc : 0;
+
+ return avg;
+}
+
+static double
+avg_subfacet_count(const struct ofproto_dpif *ofproto)
+{
+ double avg_c = 0.0;
+
+ if (ofproto->n_update_stats) {
+ avg_c = (double)ofproto->total_subfacet_count
+ / ofproto->n_update_stats;
+ }
+
+ return avg_c;
+}
+
+static void
+show_dp_rates(struct ds *ds, const char *heading,
+ const struct avg_subfacet_rates *rates)
+{
+ ds_put_format(ds, "%s add rate: %5.3f/min, del rate: %5.3f/min\n",
+ heading, rates->add_rate, rates->del_rate);
+}
+
+static void
+update_max_subfacet_count(struct ofproto_dpif *ofproto)
+{
+ ofproto->max_n_subfacet = MAX(ofproto->max_n_subfacet,
+ hmap_count(&ofproto->subfacets));
+}
+
+/* Compute exponentially weighted moving average, adding 'new' as the newest,
+ * most heavily weighted element. 'base' designates the rate of decay: after
+ * 'base' further updates, 'new''s weight in the EWMA decays to about 1/e
+ * (about .37). */
+static void
+exp_mavg(double *avg, int base, double new)
+{
+ *avg = (*avg * (base - 1) + new) / base;
+}
+
+static void
+update_moving_averages(struct ofproto_dpif *ofproto)
+{
+ const int min_ms = 60 * 1000; /* milliseconds in one minute. */
+
+ /* Update hourly averages on the minute boundaries. */
+ if (time_msec() - ofproto->last_minute >= min_ms) {
+ exp_mavg(&ofproto->hourly.add_rate, 60, ofproto->subfacet_add_count);
+ exp_mavg(&ofproto->hourly.del_rate, 60, ofproto->subfacet_del_count);
+
+ /* Update daily averages on the hour boundaries. */
+ if ((ofproto->last_minute - ofproto->created) / min_ms % 60 == 59) {
+ exp_mavg(&ofproto->daily.add_rate, 24, ofproto->hourly.add_rate);
+ exp_mavg(&ofproto->daily.del_rate, 24, ofproto->hourly.del_rate);
+ }
+
+ ofproto->total_subfacet_add_count += ofproto->subfacet_add_count;
+ ofproto->total_subfacet_del_count += ofproto->subfacet_del_count;
+ ofproto->subfacet_add_count = 0;
+ ofproto->subfacet_del_count = 0;
+ ofproto->last_minute += min_ms;
+ }
+}
+
+static void
+dpif_stats_update_hit_count(struct ofproto_dpif *ofproto, uint64_t delta)
+{
+ ofproto->n_hit += delta;
+}
const struct ofproto_class ofproto_dpif_class = {
init,
get_netflow_ids,
set_sflow,
set_cfm,
- get_cfm_fault,
- get_cfm_opup,
- get_cfm_remote_mpids,
- get_cfm_health,
+ get_cfm_status,
set_stp,
get_stp_status,
set_stp_port,
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* support CFM, as does a null pointer. */
int (*set_cfm)(struct ofport *ofport, const struct cfm_settings *s);
- /* Checks the fault status of CFM configured on 'ofport'. Returns a
- * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally
- * indicating a connectivity problem). Returns zero if CFM is not faulted,
- * and -1 if CFM is not enabled on 'port'.
+ /* Checks the status of CFM configured on 'ofport'. Returns true if the
+ * port's CFM status was successfully stored into '*status'. Returns false
+ * if the port did not have CFM configured, in which case '*status' is
+ * indeterminate.
*
- * This function may be a null pointer if the ofproto implementation does
- * not support CFM. */
- int (*get_cfm_fault)(const struct ofport *ofport);
-
- /* Check the operational status reported by the remote CFM endpoint of
- * 'ofp_port' Returns 1 if operationally up, 0 if operationally down, and
- * -1 if CFM is not enabled on 'ofp_port' or does not support operational
- * status.
- *
- * This function may be a null pointer if the ofproto implementation does
- * not support CFM. */
- int (*get_cfm_opup)(const struct ofport *ofport);
-
- /* Gets the MPIDs of the remote maintenance points broadcasting to
- * 'ofport'. Populates 'rmps' with a provider owned array of MPIDs, and
- * 'n_rmps' with the number of MPIDs in 'rmps'. Returns a number less than
- * 0 if CFM is not enabled of 'ofport'.
- *
- * This function may be a null pointer if the ofproto implementation does
- * not support CFM. */
- int (*get_cfm_remote_mpids)(const struct ofport *ofport,
- const uint64_t **rmps, size_t *n_rmps);
-
- /* Checks the health of CFM configured on 'ofport'. Returns an integer
- * to indicate the health percentage of the 'ofport' which is an average of
- * the health of all the remote_mps. Returns an integer between 0 and 100
- * where 0 means that the 'ofport' is very unhealthy and 100 means the
- * 'ofport' is perfectly healthy. Returns -1 if CFM is not enabled on
- * 'port' or if the number of remote_mpids is > 1.
- *
- * This function may be a null pointer if the ofproto implementation does
- * not support CFM. */
- int (*get_cfm_health)(const struct ofport *ofport);
+ * The caller must provide and owns '*status', but it does not own and must
+ * not modify or free the array returned in 'status->rmps'. */
+ bool (*get_cfm_status)(const struct ofport *ofport,
+ struct ofproto_cfm_status *status);
/* Configures spanning tree protocol (STP) on 'ofproto' using the
* settings defined in 's'.
ofproto->ofproto_class->get_netflow_ids(ofproto, engine_type, engine_id);
}
-/* Checks the fault status of CFM for 'ofp_port' within 'ofproto'. Returns a
- * bitmask of 'cfm_fault_reason's to indicate a CFM fault (generally
- * indicating a connectivity problem). Returns zero if CFM is not faulted,
- * and -1 if CFM is not enabled on 'ofp_port'. */
-int
-ofproto_port_get_cfm_fault(const struct ofproto *ofproto, uint16_t ofp_port)
-{
- struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
- return (ofport && ofproto->ofproto_class->get_cfm_fault
- ? ofproto->ofproto_class->get_cfm_fault(ofport)
- : -1);
-}
-
-/* Checks the operational status reported by the remote CFM endpoint of
- * 'ofp_port' Returns 1 if operationally up, 0 if operationally down, and -1
- * if CFM is not enabled on 'ofp_port' or does not support operational status.
- */
-int
-ofproto_port_get_cfm_opup(const struct ofproto *ofproto, uint16_t ofp_port)
-{
- struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
- return (ofport && ofproto->ofproto_class->get_cfm_opup
- ? ofproto->ofproto_class->get_cfm_opup(ofport)
- : -1);
-}
-
-/* Gets the MPIDs of the remote maintenance points broadcasting to 'ofp_port'
- * within 'ofproto'. Populates 'rmps' with an array of MPIDs owned by
- * 'ofproto', and 'n_rmps' with the number of MPIDs in 'rmps'. Returns a
- * number less than 0 if CFM is not enabled on 'ofp_port'. */
-int
-ofproto_port_get_cfm_remote_mpids(const struct ofproto *ofproto,
- uint16_t ofp_port, const uint64_t **rmps,
- size_t *n_rmps)
-{
- struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
-
- *rmps = NULL;
- *n_rmps = 0;
- return (ofport && ofproto->ofproto_class->get_cfm_remote_mpids
- ? ofproto->ofproto_class->get_cfm_remote_mpids(ofport, rmps,
- n_rmps)
- : -1);
-}
-
-/* Checks the health of the CFM for 'ofp_port' within 'ofproto'. Returns an
- * integer value between 0 and 100 to indicate the health of the port as a
- * percentage which is the average of cfm health of all the remote_mpids or
- * returns -1 if CFM is not enabled on 'ofport'. */
-int
-ofproto_port_get_cfm_health(const struct ofproto *ofproto, uint16_t ofp_port)
+/* Checks the status of CFM configured on 'ofp_port' within 'ofproto'. Returns
+ * true if the port's CFM status was successfully stored into '*status'.
+ * Returns false if the port did not have CFM configured, in which case
+ * '*status' is indeterminate.
+ *
+ * The caller must provide and owns '*status', but it does not own and must not
+ * modify or free the array returned in 'status->rmps'. */
+bool
+ofproto_port_get_cfm_status(const struct ofproto *ofproto, uint16_t ofp_port,
+ struct ofproto_cfm_status *status)
{
struct ofport *ofport = ofproto_get_port(ofproto, ofp_port);
- return (ofport && ofproto->ofproto_class->get_cfm_health
- ? ofproto->ofproto_class->get_cfm_health(ofport)
- : -1);
+ return (ofport
+ && ofproto->ofproto_class->get_cfm_status
+ && ofproto->ofproto_class->get_cfm_status(ofport, status));
}
static enum ofperr
extern "C" {
#endif
-struct cfm_settings;
struct cls_rule;
struct netdev;
struct ofproto;
: (ofproto_port_dump_done(DUMP), false)); \
)
-#define OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT 1000
+#define OFPROTO_FLOW_EVICTION_THRESHOLD_DEFAULT 2500
#define OFPROTO_FLOW_EVICTION_THRESHOLD_MIN 100
const char *ofproto_port_open_type(const char *datapath_type,
void ofproto_get_all_flows(struct ofproto *p, struct ds *);
void ofproto_get_netflow_ids(const struct ofproto *,
uint8_t *engine_type, uint8_t *engine_id);
-int ofproto_port_get_cfm_fault(const struct ofproto *, uint16_t ofp_port);
-int ofproto_port_get_cfm_opup(const struct ofproto *, uint16_t ofp_port);
-int ofproto_port_get_cfm_remote_mpids(const struct ofproto *,
- uint16_t ofp_port, const uint64_t **rmps,
- size_t *n_rmps);
-int ofproto_port_get_cfm_health(const struct ofproto *ofproto,
- uint16_t ofp_port);
+
void ofproto_get_ofproto_controller_info(const struct ofproto *, struct shash *);
void ofproto_free_ofproto_controller_info(struct shash *);
+
+/* CFM status query. */
+struct ofproto_cfm_status {
+ /* 0 if not faulted, otherwise a combination of one or more reasons. */
+ enum cfm_fault_reason faults;
+
+ /* 0 if the remote CFM endpoint is operationally down,
+ * 1 if the remote CFM endpoint is operationally up,
+ * -1 if we don't know because the remote CFM endpoint is not in extended
+ * mode. */
+ int remote_opstate;
+
+ /* Ordinarily a "health status" in the range 0...100 inclusive, with 0
+ * being worst and 100 being best, or -1 if the health status is not
+ * well-defined. */
+ int health;
+
+ /* MPIDs of remote maintenance points whose CCMs have been received. */
+ const uint64_t *rmps;
+ size_t n_rmps;
+};
+
+bool ofproto_port_get_cfm_status(const struct ofproto *, uint16_t ofp_port,
+ struct ofproto_cfm_status *);
\f
/* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
*
/* XXX:
*
- * Ability to generate actions on input for ECN
* Ability to generate metadata for packet-outs
- * VXLAN.
- * Multicast group management (possibly).
* Disallow netdevs with names like "gre64_system" to prevent collisions. */
VLOG_DEFINE_THIS_MODULE(tunnel);
-/* skb mark used for IPsec tunnel packets */
-#define IPSEC_MARK 1
-
struct tnl_match {
ovs_be64 in_key;
ovs_be32 ip_src;
return NULL;
}
- if (is_ip_any(flow)
- && ((flow->tunnel.ip_tos & IP_ECN_MASK) == IP_ECN_CE)
- && (flow->nw_tos & IP_ECN_MASK) == IP_ECN_NOT_ECT) {
- VLOG_WARN_RL(&rl, "dropping tunnel packet marked ECN CE but is not ECN"
- " capable");
- return NULL;
- }
-
if (!VLOG_DROP_DBG(&dbg_rl)) {
pre_flow_str = flow_to_string(flow);
}
tnl_find(struct tnl_match *match_)
{
struct tnl_match match = *match_;
- bool is_multicast = ip_is_multicast(match.ip_src);
struct tnl_port *tnl_port;
/* remote_ip, local_ip, in_key */
- if (!is_multicast) {
- tnl_port = tnl_find_exact(&match);
- if (tnl_port) {
- return tnl_port;
- }
+ tnl_port = tnl_find_exact(&match);
+ if (tnl_port) {
+ return tnl_port;
}
/* remote_ip, in_key */
match.ip_src = match_->ip_src;
/* remote_ip, local_ip */
- if (!is_multicast) {
- match.in_key = 0;
- match.in_key_flow = true;
- tnl_port = tnl_find_exact(&match);
- if (tnl_port) {
- return tnl_port;
- }
- match.in_key = match_->in_key;
- match.in_key_flow = false;
+ match.in_key = 0;
+ match.in_key_flow = true;
+ tnl_port = tnl_find_exact(&match);
+ if (tnl_port) {
+ return tnl_port;
}
/* remote_ip */
match.ip_src = 0;
- match.in_key = 0;
- match.in_key_flow = true;
tnl_port = tnl_find_exact(&match);
if (tnl_port) {
return tnl_port;
}
- match.ip_src = match_->ip_src;
- match.in_key = match_->in_key;
- match.in_key_flow = false;
-
- if (is_multicast) {
- match.ip_src = 0;
- match.ip_dst = match_->ip_src;
- /* multicast remote_ip, in_key */
- tnl_port = tnl_find_exact(&match);
- if (tnl_port) {
- return tnl_port;
- }
-
- /* multicast remote_ip */
- match.in_key = 0;
- match.in_key_flow = true;
- tnl_port = tnl_find_exact(&match);
- if (tnl_port) {
- return tnl_port;
- }
- }
return NULL;
}
#include <stdint.h>
#include "flow.h"
+/* skb mark used for IPsec tunnel packets */
+#define IPSEC_MARK 1
+
/* Tunnel port emulation layer.
*
* These functions emulate tunnel virtual ports based on the outer
struct json_array *params,
const struct json *request_id);
static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *);
+static size_t ovsdb_jsonrpc_monitor_json_length_all(
+ struct ovsdb_jsonrpc_session *);
\f
/* JSON-RPC database server. */
shash_add(&svr->remotes, name, remote);
if (!listener) {
- ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name));
+ ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name, true));
}
return remote;
}
struct list node; /* Element in remote's sessions list. */
struct ovsdb_session up;
struct ovsdb_jsonrpc_remote *remote;
+ size_t backlog_threshold; /* See ovsdb_jsonrpc_session_run(). */
+ size_t reply_backlog;
/* Triggers. */
struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */
list_push_back(&remote->sessions, &s->node);
hmap_init(&s->triggers);
hmap_init(&s->monitors);
+ s->reply_backlog = 0;
+ s->backlog_threshold = 1024 * 1024;
s->js = js;
s->js_seqno = jsonrpc_session_get_seqno(js);
{
ovsdb_jsonrpc_monitor_remove_all(s);
ovsdb_jsonrpc_session_unlock_all(s);
+ ovsdb_jsonrpc_trigger_complete_all(s);
+
+ hmap_destroy(&s->monitors);
+ hmap_destroy(&s->triggers);
+
jsonrpc_session_close(s->js);
list_remove(&s->node);
s->remote->server->n_sessions--;
static int
ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s)
{
+ size_t backlog;
+
jsonrpc_session_run(s->js);
if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) {
s->js_seqno = jsonrpc_session_get_seqno(s->js);
ovsdb_jsonrpc_trigger_complete_done(s);
- if (!jsonrpc_session_get_backlog(s->js)) {
+ backlog = jsonrpc_session_get_backlog(s->js);
+ if (!backlog) {
struct jsonrpc_msg *msg = jsonrpc_session_recv(s->js);
if (msg) {
if (msg->type == JSONRPC_REQUEST) {
jsonrpc_msg_destroy(msg);
}
}
+ s->reply_backlog = jsonrpc_session_get_backlog(s->js);
+ } else if (backlog > s->reply_backlog + s->backlog_threshold) {
+ /* We have a lot of data queued to send to the client. The data is
+ * likely to be mostly monitor updates. It is unlikely that the
+ * monitor updates are due to transactions by 's', because we will not
+ * let 's' make any more transactions until it drains its backlog to 0
+ * (see previous 'if' case). So the monitor updates are probably due
+ * to transactions made by database clients other than 's'. We can't
+ * fix that by preventing 's' from executing more transactions. We
+ * could fix it by preventing every client from executing transactions,
+ * but then one slow or hung client could prevent other clients from
+ * doing useful work.
+ *
+ * Our solution is to cap the maximum backlog to O(1) in the amount of
+ * data in the database. If the backlog exceeds that amount, then we
+ * disconnect the client. When it reconnects, it can fetch the entire
+ * contents of the database using less data than was previously
+ * backlogged. */
+ size_t monitor_length;
+
+ monitor_length = ovsdb_jsonrpc_monitor_json_length_all(s);
+ if (backlog > s->reply_backlog + monitor_length * 2) {
+ VLOG_INFO("%s: %zu bytes backlogged but a complete replica "
+ "would only take %zu bytes, disconnecting",
+ jsonrpc_session_get_name(s->js),
+ backlog - s->reply_backlog, monitor_length);
+ jsonrpc_session_force_reconnect(s->js);
+ } else {
+ /* The backlog is not unreasonably big. Only check again after it
+ * becomes much bigger. */
+ s->backlog_threshold = 2 * MAX(s->backlog_threshold * 2,
+ monitor_length);
+ }
}
return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT;
}
static void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *);
static struct json *ovsdb_jsonrpc_monitor_get_initial(
const struct ovsdb_jsonrpc_monitor *);
+static size_t ovsdb_jsonrpc_monitor_json_length(
+ const struct ovsdb_jsonrpc_monitor *);
static bool
parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value)
}
}
+/* Returns an overestimate of the number of bytes of JSON data required to
+ * report the current contents of the database over all the monitors currently
+ * configured in 's'. */
+static size_t
+ovsdb_jsonrpc_monitor_json_length_all(struct ovsdb_jsonrpc_session *s)
+{
+ struct ovsdb_jsonrpc_monitor *m;
+ size_t length;
+
+ length = 0;
+ HMAP_FOR_EACH (m, node, &s->monitors) {
+ length += ovsdb_jsonrpc_monitor_json_length(m);
+ }
+ return length;
+}
+
static struct ovsdb_jsonrpc_monitor *
ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica)
{
return true;
}
+/* Returns an overestimate of the number of bytes of JSON data required to
+ * report the current contents of the database over monitor 'm'. */
+static size_t
+ovsdb_jsonrpc_monitor_json_length(const struct ovsdb_jsonrpc_monitor *m)
+{
+ const struct shash_node *node;
+ size_t length;
+
+ /* Top-level overhead of monitor JSON. */
+ length = 256;
+
+ SHASH_FOR_EACH (node, &m->tables) {
+ const struct ovsdb_jsonrpc_monitor_table *mt = node->data;
+ const struct ovsdb_table *table = mt->table;
+ const struct ovsdb_row *row;
+ size_t i;
+
+ /* Per-table JSON overhead: "<table>":{...}. */
+ length += strlen(table->schema->name) + 32;
+
+ /* Per-row JSON overhead: ,"<uuid>":{"old":{...},"new":{...}} */
+ length += hmap_count(&table->rows) * (UUID_LEN + 32);
+
+ /* Per-row, per-column JSON overhead: ,"<column>": */
+ for (i = 0; i < mt->n_columns; i++) {
+ const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i];
+ const struct ovsdb_column *column = c->column;
+
+ length += hmap_count(&table->rows) * (8 + strlen(column->name));
+ }
+
+ /* Data. */
+ HMAP_FOR_EACH (row, hmap_node, &table->rows) {
+ for (i = 0; i < mt->n_columns; i++) {
+ const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i];
+ const struct ovsdb_column *column = c->column;
+
+ length += ovsdb_datum_json_length(&row->fields[column->index],
+ &column->type);
+ }
+ }
+ }
+
+ return length;
+}
+
static void
ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux,
const struct ovsdb_jsonrpc_monitor *m,
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
struct lockfile *lockfile;
FILE *stream;
struct ovsdb_error *read_error;
- struct ovsdb_error *write_error;
+ bool write_error;
enum ovsdb_log_mode mode;
};
file->prev_offset = 0;
file->offset = 0;
file->read_error = NULL;
- file->write_error = NULL;
+ file->write_error = false;
file->mode = OVSDB_LOG_READ;
*filep = file;
return NULL;
fclose(file->stream);
lockfile_unlock(file->lockfile);
ovsdb_error_destroy(file->read_error);
- ovsdb_error_destroy(file->write_error);
free(file);
}
}
json_string = NULL;
- if (file->write_error) {
- return ovsdb_error_clone(file->write_error);
- } else if (file->mode == OVSDB_LOG_READ) {
+ if (file->mode == OVSDB_LOG_READ || file->write_error) {
file->mode = OVSDB_LOG_WRITE;
+ file->write_error = false;
if (fseeko(file->stream, file->offset, SEEK_SET)) {
error = ovsdb_io_error(errno, "%s: cannot seek to offset %lld",
file->name, (long long int) file->offset);
return NULL;
error:
- file->write_error = ovsdb_error_clone(error);
+ file->write_error = true;
free(json_string);
return error;
}
print "{"
print " struct ovsdb_datum datum;"
if type.n_min == 1 and type.n_max == 1:
+ print " union ovsdb_atom key;"
+ if type.value:
+ print " union ovsdb_atom value;"
print
print " ovs_assert(inited);"
print " datum.n = 1;"
- print " datum.keys = xmalloc(sizeof *datum.keys);"
- print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type.to_string(), keyVar)
+ print " datum.keys = &key;"
+ print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar)
if type.value:
- print " datum.values = xmalloc(sizeof *datum.values);"
- print " "+ type.value.copyCValue("datum.values[0].%s" % type.value.type.to_string(), valueVar)
+ print " datum.values = &value;"
+ print " "+ type.value.assign_c_value_casting_away_const("value.%s" % type.value.type.to_string(), valueVar)
else:
print " datum.values = NULL;"
+ txn_write_func = "ovsdb_idl_txn_write_clone"
elif type.is_optional_pointer():
+ print " union ovsdb_atom key;"
print
print " ovs_assert(inited);"
print " if (%s) {" % keyVar
print " datum.n = 1;"
- print " datum.keys = xmalloc(sizeof *datum.keys);"
- print " " + type.key.copyCValue("datum.keys[0].%s" % type.key.type.to_string(), keyVar)
+ print " datum.keys = &key;"
+ print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), keyVar)
+ print " } else {"
+ print " datum.n = 0;"
+ print " datum.keys = NULL;"
+ print " }"
+ print " datum.values = NULL;"
+ txn_write_func = "ovsdb_idl_txn_write_clone"
+ elif type.n_max == 1:
+ print " union ovsdb_atom key;"
+ print
+ print " ovs_assert(inited);"
+ print " if (%s) {" % nVar
+ print " datum.n = 1;"
+ print " datum.keys = &key;"
+ print " " + type.key.assign_c_value_casting_away_const("key.%s" % type.key.type.to_string(), "*" + keyVar)
print " } else {"
print " datum.n = 0;"
print " datum.keys = NULL;"
print " }"
print " datum.values = NULL;"
+ txn_write_func = "ovsdb_idl_txn_write_clone"
else:
print " size_t i;"
print
print " ovs_assert(inited);"
print " datum.n = %s;" % nVar
- print " datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
+ print " datum.keys = %s ? xmalloc(%s * sizeof *datum.keys) : NULL;" % (nVar, nVar)
if type.value:
print " datum.values = xmalloc(%s * sizeof *datum.values);" % nVar
else:
valueType = "OVSDB_TYPE_VOID"
print " ovsdb_datum_sort_unique(&datum, %s, %s);" % (
type.key.toAtomicType(), valueType)
- print " ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
- % {'s': structName,
+ txn_write_func = "ovsdb_idl_txn_write"
+ print " %(f)s(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
+ % {'f': txn_write_func,
+ 's': structName,
'S': structName.upper(),
'C': columnName.upper()}
print "}"
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
void ovsdb_get_memory_usage(const struct ovsdb *, struct simap *usage);
-struct ovsdb_error *ovsdb_from_json(const struct json *, struct ovsdb **)
- WARN_UNUSED_RESULT;
-struct json *ovsdb_to_json(const struct ovsdb *);
-
struct ovsdb_table *ovsdb_get_table(const struct ovsdb *, const char *);
struct json *ovsdb_execute(struct ovsdb *, const struct ovsdb_session *,
-# Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
else:
return "%(dst)s = %(src)s;" % args
+ def assign_c_value_casting_away_const(self, dst, src):
+ args = {'dst': dst, 'src': src}
+ if self.ref_table_name:
+ return ("%(dst)s = %(src)s->header_.uuid;") % args
+ elif self.type == StringType:
+ return "%(dst)s = CONST_CAST(char *, %(src)s);" % args
+ else:
+ return "%(dst)s = %(src)s;" % args
+
def initCDefault(self, var, is_optional):
if self.ref_table_name:
return "%s = NULL;" % var
return
now = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
- message = ("%s|%s|%s|%s|%s"
- % (now, Vlog.__msg_num, self.name, level, message))
+ syslog_message = ("%s|%s|%s|%s"
+ % (Vlog.__msg_num, self.name, level, message))
level = LEVELS.get(level.lower(), logging.DEBUG)
Vlog.__msg_num += 1
for f, f_level in Vlog.__mfl[self.name].iteritems():
f_level = LEVELS.get(f_level, logging.CRITICAL)
if level >= f_level:
+ if f == "syslog":
+ message = syslog_message
+ else:
+ message = "%s|%s" % (now, syslog_message)
logging.getLogger(f).log(level, message, **kwargs)
def emer(self, message, **kwargs):
Note
----
-"ifdown" on a bridge will not bring individual ports on the bridge
+* "ifdown" on a bridge will not bring individual ports on the bridge
down. "ifup" on a bridge will not add ports to the bridge. This
behavior should be compatible with standard bridges (with
TYPE=Bridge).
+* If 'ifup' on an interface is called multiple times, one can see
+"RTNETLINK answers: File exists" printed on the console. This comes from
+ifup-eth trying to add zeroconf route multiple times and is harmless.
+
Examples
--------
rhel/openvswitch-kmod-rhel5.spec.in \
rhel/openvswitch-kmod-rhel6.spec \
rhel/openvswitch-kmod-rhel6.spec.in \
+ rhel/openvswitch-kmod.files \
rhel/openvswitch-kmod-fedora.spec \
rhel/openvswitch-kmod-fedora.spec.in \
rhel/openvswitch.spec \
test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch
start () {
- set $ovs_ctl ${1-start}
+ set ovs_ctl ${1-start}
set "$@" --system-id=random
if test X"$FORCE_COREFILES" != X; then
set "$@" --force-corefiles="$FORCE_COREFILES"
set "$@" $OVS_CTL_OPTS
"$@"
- $ovs_ctl --protocol=gre enable-protocol
+ ovs_ctl --protocol=gre enable-protocol
touch /var/lock/subsys/openvswitch
}
stop () {
- $ovs_ctl stop
+ ovs_ctl stop
rm -f /var/lock/subsys/openvswitch
}
fi
}
-ovs_ctl=/usr/share/openvswitch/scripts/ovs-ctl
case $1 in
start)
start
# Nothing to do.
;;
status)
- $ovs_ctl status
+ ovs_ctl status
;;
version)
- $ovs_ctl version
+ ovs_ctl version
;;
force-reload-kmod)
start force-reload-kmod
OTHERSCRIPT="/etc/sysconfig/network-scripts/ifup-eth"
fi
-check_recursion()
+check_recursion ()
{
[ -n "${UPPEDSTACK}" ] && for _r in ${UPPEDSTACK}; do
[ "$_r" = "$1" ] && return 1
return 0
}
+ifup_ovs_bridge ()
+{
+ if ovs-vsctl br-exists "${OVS_BRIDGE}"; then :; else
+ /sbin/ifup "${OVS_BRIDGE}"
+ fi
+}
+
if [ -z "${UPPEDSTACK}" ]; then
UPPEDSTACK="${DEVICE}"
fi
case "$TYPE" in
OVSBridge)
- ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
+ # If bridge already exists and is up, it has been configured through
+ # other cases like OVSPort, OVSIntPort and OVSBond. If it is down or
+ # it does not exist, create it. It is possible for a bridge to exist
+ # because it remained in the OVSDB for some reason, but it won't be up.
+ if check_device_down "${DEVICE}"; then
+ ovs-vsctl -t ${TIMEOUT} -- --may-exist add-br "$DEVICE" $OVS_OPTIONS \
+ ${OVS_EXTRA+-- $OVS_EXTRA} \
+ ${STP+-- set bridge "$DEVICE" stp_enable="${STP}"}
+ else
+ OVSBRIDGECONFIGURED="yes"
+ fi
+
+ # When dhcp is enabled, the assumption is that there will be a port to
+ # attach (otherwise, we can't reach out for dhcp). So, we do not
+ # configure the bridge through rhel's ifup infrastructure unless
+ # it is being configured after the port has been configured.
+ # The "OVSINTF" is set only after the port is configured.
if [ "${OVSBOOTPROTO}" = "dhcp" ] && [ -n "${OVSINTF}" ]; then
case " ${OVSDHCPINTERFACES} " in
*" ${OVSINTF} "*)
;;
esac
fi
- if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ]; then
+
+ # When dhcp is not enabled, it is possible that someone may want
+ # a standalone bridge (i.e it may not have any ports). Configure it.
+ if [ "${OVSBOOTPROTO}" != "dhcp" ] && [ -z "${OVSINTF}" ] && \
+ [ "${OVSBRIDGECONFIGURED}" != "yes" ]; then
${OTHERSCRIPT} ${CONFIG}
fi
- [ -n "${STP}" ] && ovs-vsctl --no-wait set bridge "${DEVICE}" stp_enable="${STP}"
+ exit 0
;;
OVSPort)
- /sbin/ifup "$OVS_BRIDGE"
+ ifup_ovs_bridge
${OTHERSCRIPT} ${CONFIG} ${2}
ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS ${OVS_EXTRA+-- $OVS_EXTRA}
OVSINTF=${DEVICE} /sbin/ifup "$OVS_BRIDGE"
;;
OVSIntPort)
- /sbin/ifup "$OVS_BRIDGE"
+ ifup_ovs_bridge
ovs-vsctl -t ${TIMEOUT} -- --may-exist add-port "$OVS_BRIDGE" "$DEVICE" $OVS_OPTIONS -- set Interface "$DEVICE" type=internal ${OVS_EXTRA+-- $OVS_EXTRA}
${OTHERSCRIPT} ${CONFIG} ${2}
;;
OVSBond)
- /sbin/ifup "$OVS_BRIDGE"
+ ifup_ovs_bridge
for _iface in $BOND_IFACES; do
/sbin/ifup ${_iface}
done
License: GPLv2
URL: http://openvswitch.org/
Source0: %{oname}-%{version}.tar.gz
+Source1: %{oname}-kmod.files
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
BuildRequires: %kernel_module_package_buildreqs
# specified kernel variants.
%{!?kflavors:%define kflavors default}
-%kernel_module_package -n %{oname} %kflavors
+%kernel_module_package -n %{oname} -f %{SOURCE1} %kflavors
%description
Open vSwitch Linux kernel module.
%prep
%setup -n %{oname}-%{version}
+cat > %{oname}.conf << EOF
+override %{oname} * extra/%{oname}
+override %{oname} * weak-updates/%{oname}
+EOF
%build
for flavor in %flavors_to_build; do
make -C %{kernel_source $flavor} modules_install \
M="`pwd`"/_$flavor/datapath/linux
done
+install -d %{buildroot}%{_sysconfdir}/depmod.d/
+install -m 644 %{oname}.conf %{buildroot}%{_sysconfdir}/depmod.d/
%clean
rm -rf $RPM_BUILD_ROOT
--- /dev/null
+%defattr(644,root,root,755)
+/lib/modules/%2-%1
+/etc/depmod.d/openvswitch.conf
# -*- shell-script -*-
HAVE_OPENSSL='@HAVE_OPENSSL@'
HAVE_PYTHON='@HAVE_PYTHON@'
+EGREP='@EGREP@'
PERL='@PERL@'
if test x"$PYTHON" = x; then
tests_test_stp_SOURCES = tests/test-stp.c
tests_test_stp_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+noinst_PROGRAMS += tests/test-sflow
+tests_test_sflow_SOURCES = tests/test-sflow.c
+tests_test_sflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
+
noinst_PROGRAMS += tests/test-netflow
tests_test_netflow_SOURCES = tests/test-netflow.c
tests_test_netflow_LDADD = lib/libopenvswitch.a $(SSL_LIBS)
configure_datapath: physical - [u'eth2']
configure_datapath: extra ports - []
configure_datapath: extra bonds - []
-/usr/bin/ovs-vsctl --timeout=5 -vconsole:off get-fail-mode xenbr2
+/usr/bin/ovs-vsctl -vconsole:off get-fail-mode xenbr2
Applying changes to /etc/sysconfig/network-scripts/route-xenbr2 configuration
Applying changes to /etc/sysconfig/network configuration
Applying changes to /etc/sysconfig/network-scripts/ifcfg-xenbr2 configuration
set Bridge xenbr2 fail_mode=secure
remove Bridge xenbr2 other_config disable-in-band
br-set-external-id xenbr2 xs-network-uuids d08c8749-0c8f-9e8d-ce25-fd364661ee99
-/usr/bin/ovs-vsctl --timeout=5 -vconsole:off get interface eth2 ofport
+/usr/bin/ovs-vsctl -vconsole:off get interface eth2 ofport
/usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=5,arp,nw_proto=1,actions=local
/usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=local,arp,dl_src=00:15:17:a0:29:80,actions=5
/usr/bin/ovs-ofctl add-flow xenbr2 idle_timeout=0,priority=0,in_port=5,dl_dst=00:15:17:a0:29:80,actions=local
AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK
])
+ovs-appctl time/stop
+
# Wait for up to 5 (simulated) seconds, until LACP negotiation finishes.
i=0
while :; do
])
# Redirect the patch link between p0 and p2 so that no packets get
-# back and forth across them anymore. Then wait 4 simulated
+# back and forth across them anymore. Then wait 2.5 simulated
# seconds. The LACP state should become "expired" for p0 and p2.
AT_CHECK([ovs-vsctl \
-- add-port br0 null0 -- set int null0 type=patch options:peer=p2 -- set int p2 options:peer=null0 \
userspace(pid=9765,slow_path(cfm,match))
userspace(pid=9123,userdata=0x815309)
userspace(pid=1234567,userdata(0102030405060708090a0b0c0d0e0f))
-set(tun_id(0x7f10354))
set(in_port(2))
set(eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15))
set(eth_type(0x1234))
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print "\
01 04 00 78 00 00 00 00 00 00 23 20 00 00 00 0e \
-00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 06 \
+00 00 00 00 00 00 00 00 ff ff 00 02 00 00 00 06 \
01 6e 36 00 00 05 00 3c 00 00 00 00 00 00 00 01 \
00 00 00 00 00 00 00 3c 00 00 00 02 00 03 00 00 \
02 06 50 54 00 00 00 06 00 00 04 06 50 54 00 00 \
1e 02 00 02 00 00 20 04 c0 a8 00 01 00 00 22 04 \
c0 a8 00 02 00 00 00 00 \
"], [0], [dnl
-NXT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2 reason=idle duration6.024s idle5 pkts1 bytes60
+NXT_FLOW_REMOVED (xid=0x0): priority=65535,arp,in_port=3,vlan_tci=0x0000,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:06,arp_spa=192.168.0.1,arp_tpa=192.168.0.2,arp_op=2 reason=idle table_id=1 duration6.024s idle5 pkts1 bytes60
])
AT_CLEANUP
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - goto table])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11])
+echo "table=0 in_port=1 actions=output(10),goto_table(1)" > flows.txt
+for i in `seq 1 252`; do echo "table=$i actions=goto_table($(($i+1)))"; done >> flows.txt
+echo "table=253 actions=output(11)" >> flows.txt
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 10,11
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - registers])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
in_port=13 actions=load:0x13->NXM_NX_REG3[[]],load:0x14->NXM_NX_REG4[[]],load:0x15->NXM_NX_REG5[[]]
in_port=14 actions=load:0x16->NXM_NX_REG6[[]],load:0x17->NXM_NX_REG7[[]]
in_port=15,reg0=0x10,reg1=0x11,reg2=0x12,reg3=0x13,reg4=0x14,reg5=0x15,reg6=0x16,reg7=0x17 actions=output:33
+
])
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - push-pop])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [20], [21], [22], [33], [90])
+AT_DATA([flows.txt], [dnl
+in_port=90 actions=load:20->NXM_NX_REG0[[0..7]],load:21->NXM_NX_REG1[[0..7]],load:22->NXM_NX_REG2[[0..7]], load:33->NXM_NX_REG3[[0..7]], push:NXM_NX_REG0[[]], push:NXM_NX_REG1[[0..7]],push:NXM_NX_REG2[[0..15]], push:NXM_NX_REG3[[]], resubmit:2, resubmit:3, resubmit:4, resubmit:5
+in_port=2 actions=pop:NXM_NX_REG0[[0..7]],output:NXM_NX_REG0[[]]
+in_port=3 actions=pop:NXM_NX_REG1[[0..7]],output:NXM_NX_REG1[[]]
+in_port=4 actions=pop:NXM_NX_REG2[[0..15]],output:NXM_NX_REG2[[]]
+in_port=5 actions=pop:NXM_NX_REG3[[]],output:NXM_NX_REG3[[]]
+
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: 33,22,21,20
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - output])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [1], [9], [10], [11], [55], [66], [77], [88])
cookie=0xa dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
cookie=0xa dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
cookie=0xa dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],controller
+cookie=0xa dl_src=40:44:44:44:44:45 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,controller
+cookie=0xa dl_src=40:44:44:44:44:46 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),controller
+cookie=0xa dl_src=40:44:44:44:44:47 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),controller
+cookie=0xa dl_src=40:44:44:44:44:48 actions=push_mpls:0x8847,load:10->OXM_OF_MPLS_LABEL[[]],load:3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,controller
cookie=0xb dl_src=50:55:55:55:55:55 dl_type=0x8847 actions=load:1000->OXM_OF_MPLS_LABEL[[]],controller
cookie=0xd dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,controller
cookie=0xc dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:1000->OXM_OF_MPLS_LABEL[[]],load:7->OXM_OF_MPLS_TC[[]],controller
mpls(label:10,tc:3,ttl:64,bos:1),metadata=0,in_port=0,dl_vlan=99,dl_vlan_pcp=7,dl_src=40:44:44:44:44:44,dl_dst=50:54:00:00:00:07
])
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:45,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:63,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:63,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:63,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:45,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:46,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:46,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:47,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:10,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:47,dl_dst=50:54:00:00:00:07
+])
+
+dnl Modified MPLS controller action.
+AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
+
+for i in 1 2 3; do
+ ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=40:44:44:44:44:48,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+done
+
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:9,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:9,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0xa total_len=64 in_port=1 (via action) data_len=64 (unbuffered)
+mpls(label:10,tc:3,ttl:9,bos:1),metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:44:48,dl_dst=50:54:00:00:00:07
+])
+
dnl Modified MPLS actions.
AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
dnl Modified MPLS pop action.
+dnl The input is a frame with two MPLS headers which tcpdump -vve shows as:
+dnl 60:66:66:66:66:66 > 50:54:00:00:00:07, ethertype MPLS multicast (0x8847), length 66: MPLS (label 20, exp 0, ttl 32)
+dnl (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto TCP (6), length 44)
+
AT_CHECK([ovs-ofctl monitor br0 65534 -P nxm --detach --pidfile 2> ofctl_monitor.log])
for i in 1 2 3; do
- ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no)'
+ ovs-appctl netdev-dummy/receive p1 '50 54 00 00 00 07 60 66 66 66 66 66 88 47 00 01 41 20 45 00 00 2c 00 00 00 00 40 06 3b 78 c0 a8 00 01 c0 a8 00 02 00 50 00 00 00 00 00 2a 00 00 00 2a 50 00 27 10 77 44 00 00 48 4f 47 45'
done
+#for i in 2 3; do
+# ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=60:66:66:66:66:66,dst=50:54:00:00:00:07),eth_type(0x8847),mpls(label=10,tc=3,ttl=100,bos=1)'
+#done
OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
AT_CHECK([cat ofctl_monitor.log], [0], [dnl
-NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
dnl
-NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
dnl
-NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=56 in_port=1 (via action) data_len=56 (unbuffered)
-tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64 tcp_csum:0
+NXT_PACKET_IN (xid=0x0): cookie=0xd total_len=58 in_port=1 (via action) data_len=58 (unbuffered)
+tcp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=60:66:66:66:66:66,dl_dst=50:54:00:00:00:07,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=80,tp_dst=0 tcp_csum:7744
])
dnl Checksum TCP.
udp,metadata=0,in_port=0,dl_vlan=80,dl_vlan_pcp=0,dl_src=80:81:81:81:81:81,dl_dst=82:82:82:82:82:82,nw_src=83.83.83.83,nw_dst=84.84.84.84,nw_tos=0,nw_ecn=0,nw_ttl=0,tp_src=85,tp_dst=86 udp_csum:43a1
])
+AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
cookie=0x1, n_packets=2, n_bytes=120, dl_src=20:22:22:22:22:22 actions=CONTROLLER:65535,resubmit(80,1)
cookie=0x2, n_packets=3, n_bytes=180, dl_src=30:33:33:33:33:33 actions=mod_vlan_vid:15,CONTROLLER:65535
cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:42 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:43 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:44 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:45 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:46 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:47 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],dec_mpls_ttl,set_mpls_ttl(10),CONTROLLER:65535
+ cookie=0xa, n_packets=3, n_bytes=180, dl_src=40:44:44:44:44:48 actions=push_mpls:0x8847,load:0xa->OXM_OF_MPLS_LABEL[[]],load:0x3->OXM_OF_MPLS_TC[[]],set_mpls_ttl(10),dec_mpls_ttl,CONTROLLER:65535
cookie=0xb, n_packets=3, n_bytes=180, dl_src=50:55:55:55:55:55,dl_type=0x8847 actions=load:0x3e8->OXM_OF_MPLS_LABEL[[]],CONTROLLER:65535
cookie=0xc, n_packets=3, n_bytes=180, dl_src=70:77:77:77:77:77 actions=push_mpls:0x8848,load:0x3e8->OXM_OF_MPLS_LABEL[[]],load:0x7->OXM_OF_MPLS_TC[[]],CONTROLLER:65535
- cookie=0xd, n_packets=3, n_bytes=180, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535
+ cookie=0xd, n_packets=3, n_bytes=186, dl_src=60:66:66:66:66:66 actions=pop_mpls:0x0800,CONTROLLER:65535
n_packets=3, n_bytes=180, dl_src=10:11:11:11:11:11 actions=CONTROLLER:65535
NXST_FLOW reply:
])
OVS_VSWITCHD_STOP
AT_CLEANUP
+dnl Test that sFlow samples packets correctly.
+AT_SETUP([ofproto-dpif - sFlow packet sampling])
+AT_CHECK([perl $srcdir/choose-port.pl], [0], [stdout])
+SFLOW_PORT=`cat stdout`
+OVS_VSWITCHD_START([set Bridge br0 fail-mode=standalone])
+
+ovs-appctl time/stop
+
+ADD_OF_PORTS([br0], 1, 2)
+ovs-vsctl \
+ set Interface br0 options:ifindex=1002 -- \
+ set Interface p1 options:ifindex=1004 -- \
+ set Interface p2 options:ifindex=1003 -- \
+ set Bridge br0 sflow=@sf -- \
+ --id=@sf create sflow targets=\"127.0.0.1:$SFLOW_PORT\" \
+ header=128 sampling=1 polling=1
+ON_EXIT([kill `cat test-sflow.pid`])
+AT_CHECK([test-sflow --detach --no-chdir --pidfile $SFLOW_PORT:127.0.0.1 > sflow.log])
+AT_CAPTURE_FILE([sflow.log])
+
+dnl open with ARP packets to seed the bridge-learning. The output
+dnl ifIndex numbers should be reported predictably after that.
+dnl Since we set sampling=1 we should see all of these packets
+dnl reported. Sorting the output by data-source and seqNo makes
+dnl it deterministic. Ensuring that we send at least two packets
+dnl into each port means we get to check the seq nos are
+dnl incrementing correctly.
+
+ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.2,tip=192.168.0.1,op=1,sha=50:54:00:00:00:05,tha=00:00:00:00:00:00)'
+ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=FF:FF:FF:FF:FF:FF),eth_type(0x0806),arp(sip=192.168.0.1,tip=192.168.0.2,op=1,sha=50:54:00:00:00:07,tha=00:00:00:00:00:00)'
+ovs-appctl netdev-dummy/receive p1 'in_port(2),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0)'
+ovs-appctl netdev-dummy/receive p2 'in_port(1),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x86dd),ipv6(src=fe80::1,dst=fe80::2,label=0,proto=10,tclass=0x70,hlimit=128,frag=no)'
+
+dnl sleep long enough to get more than one counter sample
+dnl from each datasource so we can check sequence numbers
+for i in `seq 1 30`; do
+ ovs-appctl time/warp 100
+done
+OVS_VSWITCHD_STOP
+ovs-appctl -t test-sflow exit
+
+AT_CHECK([[sort sflow.log | $EGREP 'HEADER|ERROR' | sed 's/ /\
+ /g']], [0], [dnl
+HEADER
+ dgramSeqNo=1
+ ds=127.0.0.1>0:1003
+ fsSeqNo=1
+ in_vlan=0
+ in_priority=0
+ out_vlan=0
+ out_priority=0
+ meanSkip=1
+ samplePool=1
+ dropEvents=0
+ in_ifindex=1003
+ in_format=0
+ out_ifindex=2
+ out_format=2
+ hdr_prot=1
+ pkt_len=64
+ stripped=4
+ hdr_len=60
+ hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-07-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-07-C0-A8-00-01-00-00-00-00-00-00-C0-A8-00-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+HEADER
+ dgramSeqNo=1
+ ds=127.0.0.1>0:1003
+ fsSeqNo=2
+ in_vlan=0
+ in_priority=0
+ out_vlan=0
+ out_priority=0
+ meanSkip=1
+ samplePool=2
+ dropEvents=0
+ in_ifindex=1003
+ in_format=0
+ out_ifindex=1004
+ out_format=0
+ hdr_prot=1
+ pkt_len=64
+ stripped=4
+ hdr_len=60
+ hdr=50-54-00-00-00-05-50-54-00-00-00-07-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-02-C0-A8-00-01-00-00-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+HEADER
+ dgramSeqNo=1
+ ds=127.0.0.1>0:1003
+ fsSeqNo=3
+ in_vlan=0
+ in_priority=0
+ out_vlan=0
+ out_priority=0
+ meanSkip=1
+ samplePool=3
+ dropEvents=0
+ in_ifindex=1003
+ in_format=0
+ out_ifindex=1004
+ out_format=0
+ hdr_prot=1
+ pkt_len=64
+ stripped=4
+ hdr_len=60
+ hdr=50-54-00-00-00-05-50-54-00-00-00-07-86-DD-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+HEADER
+ dgramSeqNo=1
+ ds=127.0.0.1>0:1004
+ fsSeqNo=1
+ in_vlan=0
+ in_priority=0
+ out_vlan=0
+ out_priority=0
+ meanSkip=1
+ samplePool=1
+ dropEvents=0
+ in_ifindex=1004
+ in_format=0
+ out_ifindex=2
+ out_format=2
+ hdr_prot=1
+ pkt_len=64
+ stripped=4
+ hdr_len=60
+ hdr=FF-FF-FF-FF-FF-FF-50-54-00-00-00-05-08-06-00-01-08-00-06-04-00-01-50-54-00-00-00-05-C0-A8-00-02-00-00-00-00-00-00-C0-A8-00-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+HEADER
+ dgramSeqNo=1
+ ds=127.0.0.1>0:1004
+ fsSeqNo=2
+ in_vlan=0
+ in_priority=0
+ out_vlan=0
+ out_priority=0
+ meanSkip=1
+ samplePool=2
+ dropEvents=0
+ in_ifindex=1004
+ in_format=0
+ out_ifindex=1003
+ out_format=0
+ hdr_prot=1
+ pkt_len=64
+ stripped=4
+ hdr_len=60
+ hdr=50-54-00-00-00-07-50-54-00-00-00-05-08-00-45-00-00-1C-00-00-00-00-40-01-F9-8D-C0-A8-00-01-C0-A8-00-02-08-00-F7-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
+])
+
+AT_CHECK([[sort sflow.log | $EGREP 'IFCOUNTERS|ERROR' | head -6 | sed 's/ /\
+ /g']], [0], [dnl
+IFCOUNTERS
+ dgramSeqNo=2
+ ds=127.0.0.1>0:1002
+ csSeqNo=1
+ ifindex=1002
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=3
+ in_octets=0
+ in_unicasts=0
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=120
+ out_unicasts=2
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+IFCOUNTERS
+ dgramSeqNo=2
+ ds=127.0.0.1>0:1003
+ csSeqNo=1
+ ifindex=1003
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=0
+ in_octets=98
+ in_unicasts=3
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=120
+ out_unicasts=2
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+IFCOUNTERS
+ dgramSeqNo=2
+ ds=127.0.0.1>0:1004
+ csSeqNo=1
+ ifindex=1004
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=0
+ in_octets=84
+ in_unicasts=2
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=180
+ out_unicasts=3
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+IFCOUNTERS
+ dgramSeqNo=3
+ ds=127.0.0.1>0:1002
+ csSeqNo=2
+ ifindex=1002
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=3
+ in_octets=0
+ in_unicasts=0
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=120
+ out_unicasts=2
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+IFCOUNTERS
+ dgramSeqNo=3
+ ds=127.0.0.1>0:1003
+ csSeqNo=2
+ ifindex=1003
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=0
+ in_octets=98
+ in_unicasts=3
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=120
+ out_unicasts=2
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+IFCOUNTERS
+ dgramSeqNo=3
+ ds=127.0.0.1>0:1004
+ csSeqNo=2
+ ifindex=1004
+ type=6
+ ifspeed=100000000
+ direction=0
+ status=0
+ in_octets=84
+ in_unicasts=2
+ in_multicasts=0
+ in_broadcasts=4294967295
+ in_discards=0
+ in_errors=0
+ in_unknownprotos=4294967295
+ out_octets=180
+ out_unicasts=3
+ out_multicasts=4294967295
+ out_broadcasts=4294967295
+ out_discards=0
+ out_errors=0
+ promiscuous=0
+])
+AT_CLEANUP
+
+
+
dnl Test that basic NetFlow reports flow statistics correctly:
dnl - The initial packet of a flow are correctly accounted.
dnl - Later packets within a flow are correctly accounted.
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (dummy)
p2 2/2: (dummy)
br1 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br1 65534/101: (dummy)
p3 3/3: (dummy)
])
AT_CHECK([ovs-appctl dpif/show br0], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (dummy)
p2 2/2: (dummy)
])
AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
-in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
-in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
])
AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
])
OVS_VSWITCHD_STOP
])
AT_CHECK([ovs-appctl dpif/dump-flows br0 | sort | STRIP_USED], [0], [dnl
-in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
-in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
+in_port(2),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=0,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
])
AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
])
AT_CHECK([ovs-appctl dpif/del-flows br0])
])
AT_CHECK([ovs-appctl dpif/dump-flows br1 | sort | STRIP_USED], [0], [dnl
-in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:drop
+in_port(3),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0), packets:0, bytes:0, used:0.0s, actions:userspace(pid=0,slow_path(controller))
])
OVS_VSWITCHD_STOP
ovs-appctl netdev-dummy/receive br1 'in_port(101),eth(src=50:54:00:00:00:07,dst=50:54:00:00:00:05),eth_type(0x0800),ipv4(src=192.168.0.2,dst=192.168.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
done
+AT_CHECK([ovs-appctl time/warp 1000 && ovs-appctl time/warp 1000], [0], [warped
+warped
+])
+
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:13 missed:2 lost:0
- flows: 1
+ lookups: hit:9 missed:1
+ flows: cur: 1, avg: 1.000, max: 1, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p2 2/2: (dummy)
pbr0 1/none: (patch: peer=pbr1)
br1 (dummy@ovs-dummy):
- lookups: hit:13 missed:2 lost:0
- flows: 1
+ lookups: hit:4 missed:1
+ flows: cur: 1, avg: 1.000, max: 1, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br1 65534/101: (dummy)
p3 3/3: (dummy)
pbr1 1/none: (patch: peer=pbr0)
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([ofproto-dpif - ovs-appctl dpif/show rates])
+OVS_VSWITCHD_START([set Bridge br0 fail-mode=secure])
+ADD_OF_PORTS([br0], 1, 2)
+
+AT_CHECK([ovs-ofctl add-flow br0 actions=LOCAL,output:1,output:2])
+
+for i in $(seq 1 61); do
+ ovs-appctl netdev-dummy/receive br0 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)'
+ ovs-appctl time/warp 10000
+ ovs-appctl time/warp 50000
+done
+
+AT_CHECK([ovs-appctl time/warp 10000], [0], [warped
+])
+
+AT_CHECK([ovs-appctl dpif/show | sed 's/ 10[[0-9]]\{3\}(ms)$/ 10000(ms)/'], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:61
+ flows: cur: 0, avg: 1.000, max: 1, life span: 10000(ms)
+ hourly avg: add rate: 0.641/min, del rate: 0.635/min
+ overall avg: add rate: 1.000/min, del rate: 0.984/min
+ br0 65534/100: (dummy)
+ p1 1/1: (dummy)
+ p2 2/2: (dummy)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
touch etc/racoon/certs/ovs-stale.pem
ovs_vsctl () {
- ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket "$@"
+ ovs-vsctl --no-wait -vreconnect:emer --db=unix:socket "$@"
}
trim () { # Removes blank lines and lines starting with # from input.
sed -e '/^#/d' -e '/^[ ]*$/d' "$@"
actions=drop
reg0=123,actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:55->NXM_NX_REG2[0..31],move:NXM_NX_REG0[0..31]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
actions=move:OXM_OF_ETH_DST[]->OXM_OF_ETH_SRC[]
+actions=push:NXM_NX_REG0[0..31],pop:NXM_NX_REG0[]
vlan_tci=0x1123/0x1fff,actions=drop
]])
AT_CHECK([ovs-ofctl -F nxm -mmm parse-flows flows.txt], [0], [stdout], [stderr])
NXT_FLOW_MOD: ADD <any> actions=drop
NXT_FLOW_MOD: ADD NXM_NX_REG0(0000007b) actions=move:NXM_NX_REG0[0..5]->NXM_NX_REG1[26..31],load:0x37->NXM_NX_REG2[],move:NXM_NX_REG0[]->NXM_NX_TUN_ID[0..31],move:NXM_NX_REG0[0..15]->NXM_OF_VLAN_TCI[]
NXT_FLOW_MOD: ADD <any> actions=move:NXM_OF_ETH_DST[]->NXM_OF_ETH_SRC[]
+NXT_FLOW_MOD: ADD <any> actions=push:NXM_NX_REG0[],pop:NXM_NX_REG0[]
NXT_FLOW_MOD: ADD NXM_OF_VLAN_TCI_W(1123/1fff) actions=drop
]])
AT_CLEANUP
dnl
dnl Executes each ovs-vsctl COMMAND.
m4_define([RUN_OVS_VSCTL],
- [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket command
+ [m4_foreach([command], [$@], [ovs-vsctl --no-wait -vreconnect:emer --db=unix:socket command
])])
m4_define([RUN_OVS_VSCTL_ONELINE],
- [m4_foreach([command], [$@], [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket --oneline -- command
+ [m4_foreach([command], [$@], [ovs-vsctl --no-wait -vreconnect:emer --db=unix:socket --oneline -- command
])])
dnl RUN_OVS_VSCTL_TOGETHER(COMMAND, ...)
dnl
dnl Executes each ovs-vsctl COMMAND in a single run of ovs-vsctl.
m4_define([RUN_OVS_VSCTL_TOGETHER],
- [ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket --oneline dnl
+ [ovs-vsctl --no-wait -vreconnect:emer --db=unix:socket --oneline dnl
m4_foreach([command], [$@], [ -- command])])
dnl CHECK_BRIDGES([BRIDGE, PARENT, VLAN], ...)
],
[], [OVS_VSCTL_CLEANUP])])])
+dnl ----------------------------------------------------------------------
+AT_BANNER([ovs-vsctl unit tests])
+
+AT_SETUP([ovs-vsctl connection retry])
+OVS_RUNDIR=$PWD; export OVS_RUNDIR
+
+dnl Without --retry, there should be no retry for active connections.
+AT_CHECK([ovs-vsctl --db=unix:foo --timeout=10 -vreconnect:emer -- init],
+ [1], [], [stderr])
+AT_CHECK([[sed 's/([^()]*)/(...reason...)/' stderr]], [0],
+ [ovs-vsctl: unix:foo: database connection failed (...reason...)
+])
+
+dnl With --retry, we should retry for active connections.
+AT_CHECK(
+ [ovs-vsctl --db=unix:foo --timeout=1 --retry -vreconnect:emer -vPATTERN:console:'%c|%p|%m' -- init
+ echo $? > status],
+ [0], [], [stderr])
+AT_CHECK([grep -c 'terminating with signal' stderr], [0], [1
+])
+AT_CHECK([kill -l `cat status`], [0], [ALRM
+])
+
+dnl Without --retry, we should retry for passive connections.
+AT_CHECK(
+ [ovs-vsctl --db=punix:foo --timeout=1 -vreconnect:emer -vPATTERN:console:'%c|%p|%m' -- init
+ echo $? > status],
+ [0], [], [stderr])
+AT_CHECK([grep -c 'terminating with signal' stderr], [0], [1
+])
+AT_CHECK([kill -l `cat status`], [0], [ALRM
+])
+AT_CLEANUP
+
dnl ----------------------------------------------------------------------
AT_BANNER([ovs-vsctl unit tests -- real bridges])
])
m4_define([VSCTL_CHECK_FIND],
- [AT_CHECK([echo `ovs-vsctl --bare --timeout=5 --no-wait -vreconnect:emer --db=unix:socket -- --columns=name find bridge '$1' | sort`], [0], [$2
+ [AT_CHECK([echo `ovs-vsctl --bare --no-wait -vreconnect:emer --db=unix:socket -- --columns=name find bridge '$1' | sort`], [0], [$2
])])
# Arithmetic relational operators without keys.
AT_KEYWORDS([ovs-vsctl])
OVS_VSCTL_SETUP
AT_CHECK(
- [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
+ [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer --db=unix:socket \
-- create Bridge name=br0 | $srcdir/uuidfilt.pl],
[0], [<0>
], [vsctl|WARN|applying "create" command to table Bridge without --id option will have no effect
], [OVS_VSCTL_CLEANUP])
AT_CHECK(
- [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
+ [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer --db=unix:socket \
-- --id=@br0 create Bridge name=br0 | $srcdir/uuidfilt.pl],
[0], [<0>
], [vsctl|WARN|row id "@br0" was created but no reference to it was inserted, so it will not actually appear in the database
], [OVS_VSCTL_CLEANUP])
AT_CHECK(
- [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --timeout=5 --no-wait -vreconnect:emer --db=unix:socket \
+ [ovs-vsctl -vPATTERN:console:'%c|%p|%m' --no-wait -vreconnect:emer --db=unix:socket \
-- --id=@eth0_iface create Interface name=eth0 \
-- --id=@eth0 create Port name=eth0 interfaces=@eth0_iface \
-- --id=@m0 create Mirror name=m0 output_port=@eth0 \
touch var/run/xapi_init_complete.cookie
ovs_vsctl () {
- ovs-vsctl --timeout=5 --no-wait -vreconnect:emer --db=unix:socket "$@"
+ ovs-vsctl --no-wait -vreconnect:emer --db=unix:socket "$@"
}
# Start ovsdb-server.
#include "ofpbuf.h"
#include "ofp-print.h"
#include "ofp-util.h"
-#include "pcap.h"
+#include "pcap-file.h"
#include "util.h"
#include "vlog.h"
/*
- * Copyright (c) 2009, 2010 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
ok = false;
} else {
char *s = json_to_string(json, JSSF_SORT | (pretty ? JSSF_PRETTY : 0));
+ ovs_assert(pretty || json_serialized_length(json) == strlen(s));
puts(s);
free(s);
ok = true;
/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
}
-static void
+static size_t
print_and_free_json(struct json *json)
{
char *string = json_to_string(json, JSSF_SORT);
+ size_t length = strlen(string);
json_destroy(json);
puts(string);
free(string);
+
+ return length;
}
static void
if (error) {
print_and_free_ovsdb_error(error);
} else {
- print_and_free_json(ovsdb_atom_to_json(&atom, base.type));
+ size_t length;
+
+ length = print_and_free_json(ovsdb_atom_to_json(&atom, base.type));
+ ovs_assert(length == ovsdb_atom_json_length(&atom, base.type));
ovsdb_atom_destroy(&atom, base.type);
}
}
for (i = 2; i < argc; i++) {
struct ovsdb_datum datum;
+ size_t length;
json = unbox_json(parse_json(argv[i]));
check_ovsdb_error(parse(&datum, &type, json, NULL));
json_destroy(json);
- print_and_free_json(ovsdb_datum_to_json(&datum, &type));
+ length = print_and_free_json(ovsdb_datum_to_json(&datum, &type));
+ ovs_assert(length == ovsdb_datum_json_length(&datum, &type));
ovsdb_datum_destroy(&datum, &type);
}
idltest_init();
- idl = ovsdb_idl_create(argv[1], &idltest_idl_class, true);
+ idl = ovsdb_idl_create(argv[1], &idltest_idl_class, true, true);
if (argc > 2) {
struct stream *stream;
--- /dev/null
+/*
+ * Copyright (c) 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2013 InMon Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <setjmp.h>
+
+#include "command-line.h"
+#include "daemon.h"
+#include "dynamic-string.h"
+#include "netflow.h"
+#include "ofpbuf.h"
+#include "packets.h"
+#include "poll-loop.h"
+#include "socket-util.h"
+#include "unixctl.h"
+#include "util.h"
+#include "vlog.h"
+
+static void usage(void) NO_RETURN;
+static void parse_options(int argc, char *argv[]);
+
+static unixctl_cb_func test_sflow_exit;
+
+/* Datagram. */
+#define SFLOW_VERSION_5 5
+#define SFLOW_MIN_LEN 36
+#define SFLOW_MAX_AGENTIP_STRLEN 64
+
+/* Sample tag numbers. */
+#define SFLOW_FLOW_SAMPLE 1
+#define SFLOW_COUNTERS_SAMPLE 2
+#define SFLOW_FLOW_SAMPLE_EXPANDED 3
+#define SFLOW_COUNTERS_SAMPLE_EXPANDED 4
+
+/* Structure element tag numbers. */
+#define SFLOW_TAG_CTR_IFCOUNTERS 1
+#define SFLOW_TAG_PKT_HEADER 1
+#define SFLOW_TAG_PKT_SWITCH 1001
+
+struct sflow_addr {
+ enum {
+ SFLOW_ADDRTYPE_undefined = 0,
+ SFLOW_ADDRTYPE_IP4,
+ SFLOW_ADDRTYPE_IP6
+ } type;
+
+ union {
+ ovs_be32 ip4;
+ ovs_be32 ip6[4];
+ } a;
+};
+
+struct sflow_xdr {
+ /* Exceptions. */
+ jmp_buf env;
+ int errline;
+
+ /* Cursor. */
+ ovs_be32 *datap;
+ uint32_t i;
+ uint32_t quads;
+
+ /* Agent. */
+ struct sflow_addr agentAddr;
+ char agentIPStr[SFLOW_MAX_AGENTIP_STRLEN];
+ uint32_t subAgentId;
+ uint32_t uptime_mS;
+
+ /* Datasource. */
+ uint32_t dsClass;
+ uint32_t dsIndex;
+
+ /* Sequence numbers. */
+ uint32_t dgramSeqNo;
+ uint32_t fsSeqNo;
+ uint32_t csSeqNo;
+
+ /* Structure offsets. */
+ struct {
+ uint32_t HEADER;
+ uint32_t SWITCH;
+ uint32_t IFCOUNTERS;
+ } offset;
+
+ /* Flow sample fields. */
+ uint32_t meanSkipCount;
+ uint32_t samplePool;
+ uint32_t dropEvents;
+ uint32_t inputPortFormat;
+ uint32_t inputPort;
+ uint32_t outputPortFormat;
+ uint32_t outputPort;
+};
+
+#define SFLOWXDR_try(x) ((x->errline = setjmp(x->env)) == 0)
+#define SFLOWXDR_throw(x) longjmp(x->env, __LINE__)
+#define SFLOWXDR_assert(x, t) if (!(t)) SFLOWXDR_throw(x)
+
+static void
+sflowxdr_init(struct sflow_xdr *x, void *buf, size_t len)
+{
+ x->datap = buf;
+ x->quads = len >> 2;
+}
+
+static uint32_t
+sflowxdr_next(struct sflow_xdr *x)
+{
+ return ntohl(x->datap[x->i++]);
+}
+
+static ovs_be32
+sflowxdr_next_n(struct sflow_xdr *x)
+{
+ return x->datap[x->i++];
+}
+
+static bool
+sflowxdr_more(const struct sflow_xdr *x, uint32_t q)
+{
+ return q + x->i <= x->quads;
+}
+
+static void
+sflowxdr_skip(struct sflow_xdr *x, uint32_t q)
+{
+ x->i += q;
+}
+
+static uint32_t
+sflowxdr_mark(const struct sflow_xdr *x, uint32_t q)
+{
+ return x->i + q;
+}
+
+static bool
+sflowxdr_mark_ok(const struct sflow_xdr *x, uint32_t m)
+{
+ return m == x->i;
+}
+
+static void
+sflowxdr_mark_unique(struct sflow_xdr *x, uint32_t *pi)
+{
+ if (*pi) {
+ SFLOWXDR_throw(x);
+ }
+ *pi = x->i;
+}
+
+static void
+sflowxdr_setc(struct sflow_xdr *x, uint32_t j)
+{
+ x->i = j;
+}
+
+static const char *
+sflowxdr_str(const struct sflow_xdr *x)
+{
+ return (const char *) (x->datap + x->i);
+}
+
+static uint64_t
+sflowxdr_next_int64(struct sflow_xdr *x)
+{
+ uint64_t scratch;
+ scratch = sflowxdr_next(x);
+ scratch <<= 32;
+ scratch += sflowxdr_next(x);
+ return scratch;
+}
+
+static void
+process_counter_sample(struct sflow_xdr *x)
+{
+ if (x->offset.IFCOUNTERS) {
+ sflowxdr_setc(x, x->offset.IFCOUNTERS);
+ printf("IFCOUNTERS");
+ printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
+ printf(" ds=%s>%"PRIu32":%"PRIu32,
+ x->agentIPStr, x->dsClass, x->dsIndex);
+ printf(" csSeqNo=%"PRIu32, x->csSeqNo);
+ printf(" ifindex=%"PRIu32, sflowxdr_next(x));
+ printf(" type=%"PRIu32, sflowxdr_next(x));
+ printf(" ifspeed=%"PRIu64, sflowxdr_next_int64(x));
+ printf(" direction=%"PRIu32, sflowxdr_next(x));
+ printf(" status=%"PRIu32, sflowxdr_next(x));
+ printf(" in_octets=%"PRIu64, sflowxdr_next_int64(x));
+ printf(" in_unicasts=%"PRIu32, sflowxdr_next(x));
+ printf(" in_multicasts=%"PRIu32, sflowxdr_next(x));
+ printf(" in_broadcasts=%"PRIu32, sflowxdr_next(x));
+ printf(" in_discards=%"PRIu32, sflowxdr_next(x));
+ printf(" in_errors=%"PRIu32, sflowxdr_next(x));
+ printf(" in_unknownprotos=%"PRIu32, sflowxdr_next(x));
+ printf(" out_octets=%"PRIu64, sflowxdr_next_int64(x));
+ printf(" out_unicasts=%"PRIu32, sflowxdr_next(x));
+ printf(" out_multicasts=%"PRIu32, sflowxdr_next(x));
+ printf(" out_broadcasts=%"PRIu32, sflowxdr_next(x));
+ printf(" out_discards=%"PRIu32, sflowxdr_next(x));
+ printf(" out_errors=%"PRIu32, sflowxdr_next(x));
+ printf(" promiscuous=%"PRIu32, sflowxdr_next(x));
+ printf("\n");
+ }
+}
+
+static char
+bin_to_hex(int hexit)
+{
+ return "0123456789ABCDEF"[hexit];
+}
+
+static int
+print_hex(const char *a, int len, char *buf, int bufLen)
+{
+ unsigned char nextByte;
+ int b = 0;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (b > bufLen - 10) {
+ break;
+ }
+ nextByte = a[i];
+ buf[b++] = bin_to_hex(nextByte >> 4);
+ buf[b++] = bin_to_hex(nextByte & 0x0f);
+ if (i < len - 1) {
+ buf[b++] = '-';
+ }
+ }
+ buf[b] = '\0';
+ return b;
+}
+
+#define SFLOW_HEX_SCRATCH 1024
+
+static void
+process_flow_sample(struct sflow_xdr *x)
+{
+ if (x->offset.HEADER) {
+ uint32_t headerLen;
+ char scratch[SFLOW_HEX_SCRATCH];
+
+ printf("HEADER");
+ printf(" dgramSeqNo=%"PRIu32, x->dgramSeqNo);
+ printf(" ds=%s>%"PRIu32":%"PRIu32,
+ x->agentIPStr, x->dsClass, x->dsIndex);
+ printf(" fsSeqNo=%"PRIu32, x->fsSeqNo);
+
+ if (x->offset.SWITCH) {
+ sflowxdr_setc(x, x->offset.SWITCH);
+ printf(" in_vlan=%"PRIu32, sflowxdr_next(x));
+ printf(" in_priority=%"PRIu32, sflowxdr_next(x));
+ printf(" out_vlan=%"PRIu32, sflowxdr_next(x));
+ printf(" out_priority=%"PRIu32, sflowxdr_next(x));
+ }
+
+ sflowxdr_setc(x, x->offset.HEADER);
+ printf(" meanSkip=%"PRIu32, x->meanSkipCount);
+ printf(" samplePool=%"PRIu32, x->samplePool);
+ printf(" dropEvents=%"PRIu32, x->dropEvents);
+ printf(" in_ifindex=%"PRIu32, x->inputPort);
+ printf(" in_format=%"PRIu32, x->inputPortFormat);
+ printf(" out_ifindex=%"PRIu32, x->outputPort);
+ printf(" out_format=%"PRIu32, x->outputPortFormat);
+ printf(" hdr_prot=%"PRIu32, sflowxdr_next(x));
+ printf(" pkt_len=%"PRIu32, sflowxdr_next(x));
+ printf(" stripped=%"PRIu32, sflowxdr_next(x));
+ headerLen = sflowxdr_next(x);
+ printf(" hdr_len=%"PRIu32, headerLen);
+ print_hex(sflowxdr_str(x), headerLen, scratch, SFLOW_HEX_SCRATCH);
+ printf(" hdr=%s", scratch);
+ printf("\n");
+ }
+}
+
+static void
+process_datagram(struct sflow_xdr *x)
+{
+ uint32_t samples, s;
+
+ SFLOWXDR_assert(x, (sflowxdr_next(x) == SFLOW_VERSION_5));
+
+ /* Read the sFlow header. */
+ x->agentAddr.type = sflowxdr_next(x);
+ switch (x->agentAddr.type) {
+ case SFLOW_ADDRTYPE_IP4:
+ x->agentAddr.a.ip4 = sflowxdr_next_n(x);
+ break;
+
+ case SFLOW_ADDRTYPE_IP6:
+ x->agentAddr.a.ip6[0] = sflowxdr_next_n(x);
+ x->agentAddr.a.ip6[1] = sflowxdr_next_n(x);
+ x->agentAddr.a.ip6[2] = sflowxdr_next_n(x);
+ x->agentAddr.a.ip6[3] = sflowxdr_next_n(x);
+ break;
+
+ case SFLOW_ADDRTYPE_undefined:
+ default:
+ SFLOWXDR_throw(x);
+ break;
+ }
+ x->subAgentId = sflowxdr_next(x);
+ x->dgramSeqNo = sflowxdr_next(x);
+ x->uptime_mS = sflowxdr_next(x);
+
+ /* Store the agent address as a string. */
+ if (x->agentAddr.type == SFLOW_ADDRTYPE_IP6) {
+ snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
+ "%04x:%04x:%04x:%04x",
+ x->agentAddr.a.ip6[0],
+ x->agentAddr.a.ip6[1],
+ x->agentAddr.a.ip6[2],
+ x->agentAddr.a.ip6[3]);
+ } else {
+ snprintf(x->agentIPStr, SFLOW_MAX_AGENTIP_STRLEN,
+ IP_FMT, IP_ARGS(x->agentAddr.a.ip4));
+ }
+
+ /* Array of flow/counter samples. */
+ samples = sflowxdr_next(x);
+ for (s = 0; s < samples; s++) {
+ uint32_t sType = sflowxdr_next(x);
+ uint32_t sQuads = sflowxdr_next(x) >> 2;
+ uint32_t sMark = sflowxdr_mark(x, sQuads);
+ SFLOWXDR_assert(x, sflowxdr_more(x, sQuads));
+
+ switch (sType) {
+ case SFLOW_COUNTERS_SAMPLE_EXPANDED:
+ case SFLOW_COUNTERS_SAMPLE:
+ {
+ uint32_t csElements, e;
+ uint32_t ceTag, ceQuads, ceMark, csEnd;
+
+ x->csSeqNo = sflowxdr_next(x);
+ if (sType == SFLOW_COUNTERS_SAMPLE_EXPANDED) {
+ x->dsClass = sflowxdr_next(x);
+ x->dsIndex = sflowxdr_next(x);
+ } else {
+ uint32_t dsCombined = sflowxdr_next(x);
+ x->dsClass = dsCombined >> 24;
+ x->dsIndex = dsCombined & 0x00FFFFFF;
+ }
+
+ csElements = sflowxdr_next(x);
+ for (e = 0; e < csElements; e++) {
+ SFLOWXDR_assert(x, sflowxdr_more(x,2));
+ ceTag = sflowxdr_next(x);
+ ceQuads = sflowxdr_next(x) >> 2;
+ ceMark = sflowxdr_mark(x, ceQuads);
+ SFLOWXDR_assert(x, sflowxdr_more(x,ceQuads));
+ /* Only care about selected structures. Just record their
+ * offsets here. We'll read the fields out later. */
+ switch (ceTag) {
+ case SFLOW_TAG_CTR_IFCOUNTERS:
+ sflowxdr_mark_unique(x, &x->offset.IFCOUNTERS);
+ break;
+
+ /* Add others here... */
+ }
+
+ sflowxdr_skip(x, ceQuads);
+ SFLOWXDR_assert(x, sflowxdr_mark_ok(x, ceMark));
+ }
+
+ csEnd = sflowxdr_mark(x, 0);
+ process_counter_sample(x);
+ /* Make sure we pick up the decoding where we left off. */
+ sflowxdr_setc(x, csEnd);
+
+ /* Clear the offsets for the next sample. */
+ memset(&x->offset, 0, sizeof x->offset);
+ }
+ break;
+
+ case SFLOW_FLOW_SAMPLE:
+ case SFLOW_FLOW_SAMPLE_EXPANDED:
+ {
+ uint32_t fsElements, e;
+ uint32_t feTag, feQuads, feMark, fsEnd;
+ x->fsSeqNo = sflowxdr_next(x);
+ if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
+ x->dsClass = sflowxdr_next(x);
+ x->dsIndex = sflowxdr_next(x);
+ } else {
+ uint32_t dsCombined = sflowxdr_next(x);
+ x->dsClass = dsCombined >> 24;
+ x->dsIndex = dsCombined & 0x00FFFFFF;
+ }
+ x->meanSkipCount = sflowxdr_next(x);
+ x->samplePool = sflowxdr_next(x);
+ x->dropEvents = sflowxdr_next(x);
+ if (sType == SFLOW_FLOW_SAMPLE_EXPANDED) {
+ x->inputPortFormat = sflowxdr_next(x);
+ x->inputPort = sflowxdr_next(x);
+ x->outputPortFormat = sflowxdr_next(x);
+ x->outputPort = sflowxdr_next(x);
+ } else {
+ uint32_t inp, outp;
+
+ inp = sflowxdr_next(x);
+ outp = sflowxdr_next(x);
+ x->inputPortFormat = inp >> 30;
+ x->inputPort = inp & 0x3fffffff;
+ x->outputPortFormat = outp >> 30;
+ x->outputPort = outp & 0x3fffffff;
+ }
+ fsElements = sflowxdr_next(x);
+ for (e = 0; e < fsElements; e++) {
+ SFLOWXDR_assert(x, sflowxdr_more(x,2));
+ feTag = sflowxdr_next(x);
+ feQuads = sflowxdr_next(x) >> 2;
+ feMark = sflowxdr_mark(x, feQuads);
+ SFLOWXDR_assert(x, sflowxdr_more(x,feQuads));
+ /* Only care about selected structures. Just record their
+ * offsets here. We'll read the fields out below. */
+ switch (feTag) {
+ case SFLOW_TAG_PKT_HEADER:
+ sflowxdr_mark_unique(x, &x->offset.HEADER);
+ break;
+
+ case SFLOW_TAG_PKT_SWITCH:
+ sflowxdr_mark_unique(x, &x->offset.SWITCH);
+ break;
+
+ /* Add others here... */
+ }
+
+ sflowxdr_skip(x, feQuads);
+ SFLOWXDR_assert(x, sflowxdr_mark_ok(x, feMark));
+ }
+
+ fsEnd = sflowxdr_mark(x, 0);
+ process_flow_sample(x);
+ /* Make sure we pick up the decoding where we left off. */
+ sflowxdr_setc(x, fsEnd);
+
+ /* Clear the offsets for the next counter/flow sample. */
+ memset(&x->offset, 0, sizeof x->offset);
+ }
+ break;
+
+ default:
+ /* Skip other sample types. */
+ sflowxdr_skip(x, sQuads);
+ }
+ SFLOWXDR_assert(x, sflowxdr_mark_ok(x, sMark));
+ }
+}
+
+static void
+print_sflow(struct ofpbuf *buf)
+{
+ char *dgram_buf;
+ int dgram_len = buf->size;
+ struct sflow_xdr xdrDatagram;
+ struct sflow_xdr *x = &xdrDatagram;
+
+ memset(x, 0, sizeof *x);
+ if (SFLOWXDR_try(x)) {
+ SFLOWXDR_assert(x, (dgram_buf = ofpbuf_try_pull(buf, buf->size)));
+ sflowxdr_init(x, dgram_buf, dgram_len);
+ SFLOWXDR_assert(x, dgram_len >= SFLOW_MIN_LEN);
+ process_datagram(x);
+ } else {
+ // CATCH
+ printf("\n>>>>> ERROR in " __FILE__ " at line %u\n", x->errline);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct unixctl_server *server;
+ enum { MAX_RECV = 1500 };
+ const char *target;
+ struct ofpbuf buf;
+ bool exiting = false;
+ int error;
+ int sock;
+
+ proctitle_init(argc, argv);
+ set_program_name(argv[0]);
+ parse_options(argc, argv);
+
+ if (argc - optind != 1) {
+ ovs_fatal(0, "exactly one non-option argument required "
+ "(use --help for help)");
+ }
+ target = argv[optind];
+
+ sock = inet_open_passive(SOCK_DGRAM, target, 0, NULL, 0);
+ if (sock < 0) {
+ ovs_fatal(0, "%s: failed to open (%s)", argv[1], strerror(-sock));
+ }
+
+ daemon_save_fd(STDOUT_FILENO);
+ daemonize_start();
+
+ error = unixctl_server_create(NULL, &server);
+ if (error) {
+ ovs_fatal(error, "failed to create unixctl server");
+ }
+ unixctl_command_register("exit", "", 0, 0, test_sflow_exit, &exiting);
+
+ daemonize_complete();
+
+ ofpbuf_init(&buf, MAX_RECV);
+ for (;;) {
+ int retval;
+
+ unixctl_server_run(server);
+
+ ofpbuf_clear(&buf);
+ do {
+ retval = read(sock, buf.data, buf.allocated);
+ } while (retval < 0 && errno == EINTR);
+ if (retval > 0) {
+ ofpbuf_put_uninit(&buf, retval);
+ print_sflow(&buf);
+ fflush(stdout);
+ }
+
+ if (exiting) {
+ break;
+ }
+
+ poll_fd_wait(sock, POLLIN);
+ unixctl_server_wait(server);
+ poll_block();
+ }
+
+ return 0;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+ enum {
+ DAEMON_OPTION_ENUMS
+ };
+ static struct option long_options[] = {
+ {"verbose", optional_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ DAEMON_LONG_OPTIONS,
+ {NULL, 0, NULL, 0},
+ };
+ char *short_options = long_options_to_short_options(long_options);
+
+ for (;;) {
+ int c = getopt_long(argc, argv, short_options, long_options, NULL);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'h':
+ usage();
+
+ case 'v':
+ vlog_set_verbosity(optarg);
+ break;
+
+ DAEMON_OPTION_HANDLERS
+
+ case '?':
+ exit(EXIT_FAILURE);
+
+ default:
+ abort();
+ }
+ }
+ free(short_options);
+}
+
+static void
+usage(void)
+{
+ printf("%s: sflow collector test utility\n"
+ "usage: %s [OPTIONS] PORT[:IP]\n"
+ "where PORT is the UDP port to listen on and IP is optionally\n"
+ "the IP address to listen on.\n",
+ program_name, program_name);
+ daemon_usage();
+ vlog_usage();
+ printf("\nOther options:\n"
+ " -h, --help display this help message\n");
+ exit(EXIT_SUCCESS);
+}
+
+static void
+test_sflow_exit(struct unixctl_conn *conn,
+ int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
+ void *exiting_)
+{
+ bool *exiting = exiting_;
+ *exiting = true;
+ unixctl_command_reply(conn, NULL);
+}
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: remote_ip=1.1.1.1)
p2 2/1: (gre: local_ip=2.2.2.2, remote_ip=1.1.1.1)
-- set Interface p3 type=gre64])
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: remote_ip=1.1.1.1)
p2 2/1: (gre: csum=true, df_default=false, local_ip=2.2.2.3, remote_ip=1.1.1.1, ttl=1)
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: remote_ip=1.1.1.1)
p2 2/2: (dummy)
])
-AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+dnl Tunnel CE and encapsulated packet CE
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=3,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
[Datapath actions: 2
])
-OVS_VSWITCHD_STOP
+
+dnl Tunnel CE and encapsulated packet ECT(1)
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=1,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
+])
+
+dnl Tunnel CE and encapsulated packet ECT(2)
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=2,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0x3,ttl=64,frag=no)),2
+])
+
+dnl Tunnel CE and encapsulated packet Non-ECT
+AT_CHECK([ovs-appctl ofproto/trace br0 'tunnel(tun_id=0x0,src=1.1.1.1,dst=2.2.2.2,tos=0x3,ttl=64,flags()),in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=6,tos=0,ttl=64,frag=no),tcp(src=8,dst=9)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: drop
+])
+OVS_VSWITCHD_STOP(["/dropping tunnel packet marked ECN CE but is not ECN capable/d"])
AT_CLEANUP
AT_SETUP([tunnel - output])
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: key=5, local_ip=2.2.2.2, remote_ip=1.1.1.1)
p2 2/2: (dummy)
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: remote_ip=1.1.1.1, tos=inherit, ttl=inherit)
p2 2/2: (dummy)
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
p2 2/1: (gre: key=flow, remote_ip=2.2.2.2)
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: key=1, remote_ip=1.1.1.1)
p2 2/1: (gre: in_key=2, out_key=3, remote_ip=1.1.1.1)
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
br0 (dummy@ovs-dummy):
- lookups: hit:0 missed:0 lost:0
- flows: 0
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
br0 65534/100: (dummy)
p1 1/1: (gre: key=flow, remote_ip=1.1.1.1)
p2 2/1: (gre: key=3, remote_ip=3.3.3.3)
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([tunnel - VXLAN])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
+ options:remote_ip=1.1.1.1 ofport_request=1])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
+ br0 65534/100: (dummy)
+ p1 1/1: (vxlan: remote_ip=1.1.1.1)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - LISP])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=lisp \
+ options:remote_ip=1.1.1.1 ofport_request=1])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
+ br0 65534/100: (dummy)
+ p1 1/1: (lisp: remote_ip=1.1.1.1)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - different VXLAN UDP port])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=vxlan \
+ options:remote_ip=1.1.1.1 ofport_request=1 options:dst_port=4341])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
+ br0 65534/100: (dummy)
+ p1 1/1: (vxlan: dst_port=4341, remote_ip=1.1.1.1)
+])
+
+dnl change UDP port
+
+AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=5000])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
+ br0 65534/100: (dummy)
+ p1 1/2: (vxlan: dst_port=5000, remote_ip=1.1.1.1)
+])
+
+dnl change UDP port to default
+
+AT_CHECK([ovs-vsctl -- set Interface p1 options:dst_port=8472])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+br0 (dummy@ovs-dummy):
+ lookups: hit:0 missed:0
+ flows: cur: 0, avg: 0.000, max: 0, life span: 0(ms)
+ overall avg: add rate: 0.000/min, del rate: 0.000/min
+ br0 65534/100: (dummy)
+ p1 1/1: (vxlan: remote_ip=1.1.1.1)
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
utilities/bugtool/ovs-bugtool-vsctl-show \
utilities/bugtool/ovs-bugtool-ovsdb-dump \
utilities/bugtool/ovs-bugtool-daemons-ver \
+ utilities/bugtool/ovs-bugtool-ovs-ofctl-show \
+ utilities/bugtool/ovs-bugtool-ovs-ofctl-dump-flows \
+ utilities/bugtool/ovs-bugtool-ovs-appctl-dpif \
utilities/bugtool/ovs-bugtool-bond-show
scripts_SCRIPTS += $(bugtool_scripts)
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2013 Nicira, Inc.
+
+for bridge in `ovs-vsctl -- --real list-br`
+do
+ echo "ovs-appctl dpif/show ${bridge}"
+ ovs-appctl dpif/show "${bridge}"
+ echo "ovs-appctl dpif/dump-flows ${bridge}"
+ ovs-appctl dpif/dump-flows "$bridge"
+ echo ""
+done
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2013 Nicira, Inc.
+
+for bridge in `ovs-vsctl list-br`
+do
+ echo "ovs-ofctl dump-flows ${bridge}"
+ ovs-ofctl dump-flows "$bridge"
+ echo ""
+done
--- /dev/null
+#! /bin/sh
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General
+# Public License as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+#
+# Copyright (C) 2013 Nicira, Inc.
+
+for bridge in `ovs-vsctl list-br`
+do
+ echo "ovs-ofctl show ${bridge}"
+ ovs-ofctl show "$bridge"
+ echo ""
+done
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
-# Copyright (C) 2012 Nicira, Inc.
+# Copyright (C) 2012, 2013 Nicira, Inc.
-ovs-vsctl --timeout=5 show
+ovs-vsctl show
cmd_output(CAP_NETWORK_STATUS, [NETSTAT, '-an'])
for dir in DHCP_LEASE_DIR:
tree_output(CAP_NETWORK_STATUS, dir)
- cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-nL'])
+ for table in ['filter', 'nat', 'mangle', 'raw', 'security']:
+ cmd_output(CAP_NETWORK_STATUS, [IPTABLES, '-t', table, '-nL'])
for p in os.listdir('/sys/class/net/'):
try:
f = open('/sys/class/net/%s/type' % p, 'r')
if os.path.exists(OPENVSWITCH_VSWITCHD_PID):
cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'show', '-s'])
for d in dp_list():
- cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'show', d])
- cmd_output(CAP_NETWORK_STATUS, [OVS_OFCTL, 'dump-flows', d])
cmd_output(CAP_NETWORK_STATUS, [OVS_DPCTL, 'dump-flows', d])
try:
vspidfile = open(OPENVSWITCH_VSWITCHD_PID)
'syslog', 'messages', 'secure', 'debug', 'dmesg', 'boot']])
ovs_logs = ([ OPENVSWITCH_LOG_DIR + x for x in
['ovs-vswitchd.log', 'ovsdb-server.log',
- 'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log']])
+ 'ovs-xapi-sync.log', 'ovs-monitor-ipsec.log', 'ovs-ctl.log']])
log_output(CAP_SYSTEM_LOGS, system_logs)
log_output(CAP_OPENVSWITCH_LOGS, ovs_logs)
<command label="ovs-appctl-coverage-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-coverage-show</command>
<command label="ovs-appctl-bond-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-bond-show</command>
<command label="ovs-appctl-memory-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-memory-show</command>
+ <command label="ovs-ofctl-show" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-show</command>
+ <command label="ovs-ofctl-dump-flows" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-ofctl-dump-flows</command>
+ <command label="ovs-appctl-dpif" filters="ovs">/usr/share/openvswitch/scripts/ovs-bugtool-ovs-appctl-dpif</command>
</collect>
}
ovs_vsctl () {
- ovs-vsctl --no-wait --timeout=5 "$@"
+ ovs-vsctl --no-wait "$@"
}
ovsdb_tool () {
check_force_cores () {
if test X"$FORCE_COREFILES" = Xyes; then
- ulimit -Sc 67108864
+ ulimit -c 67108864
fi
}
chmod +x "$2"
return 0
fi
- eval $3=""
[ -z "${bridges}" ] && return 0
}
case `ovs-appctl version | sed 1q` in
"ovs-vswitchd (Open vSwitch) 1."[0-9].*)
action "Saving ofport values" ovs_save save-ofports \
- "${script_ofports}" script_ofports
- ;;
- *)
- script_ofports=""
+ "${script_ofports}"
;;
esac
}
}
restore_ofports () {
- [ -n "${script_ofports}" ] && \
+ [ -x "${script_ofports}" ] && \
action "Restoring ofport values" "${script_ofports}"
}
restore_flows () {
- [ -n "${script_flows}" ] && \
+ [ -x "${script_flows}" ] && \
action "Restoring saved flows" "${script_flows}"
}
-force_reload_kmod () {
- ifaces=`internal_interfaces`
- action "Detected internal interfaces: $ifaces" true
+restore_interfaces () {
+ [ ! -x "${script_interfaces}" ] && return 0
+ action "Restoring interface configuration" "${script_interfaces}"
+ rc=$?
+ if test $rc = 0; then
+ level=debug
+ else
+ level=err
+ fi
+ log="logger -p daemon.$level -t ovs-save"
+ $log "interface restore script exited with status $rc:"
+ $log -f "$script_interfaces"
+}
+init_restore_scripts () {
script_interfaces=`mktemp`
script_flows=`mktemp`
script_ofports=`mktemp`
trap 'rm -f "${script_interfaces}" "${script_flows}" "${script_ofports}"' 0
+}
+
+force_reload_kmod () {
+ ifaces=`internal_interfaces`
+ action "Detected internal interfaces: $ifaces" true
+
+ init_restore_scripts
- action "Saving flows" ovs_save save-flows "${script_flows}" script_flows
+ action "Saving flows" ovs_save save-flows "${script_flows}"
save_ofports_if_required
restore_flows
- action "Restoring interface configuration" "$script_interfaces"
- rc=$?
- if test $rc = 0; then
- level=debug
- else
- level=err
- fi
- log="logger -p daemon.$level -t ovs-save"
- $log "force-reload-kmod interface restore script exited with status $rc:"
- $log -f "$script_interfaces"
+ restore_interfaces
"$datadir/scripts/ovs-check-dead-ifs"
}
## restart ##
## ------- ##
+save_interfaces_if_required () {
+ # Save interfaces if we are upgrading from a pre-1.10 branch.
+ case `ovs-appctl version | sed 1q` in
+ "ovs-vswitchd (Open vSwitch) 1."[0-9].*)
+ ifaces=`internal_interfaces`
+ action "Detected internal interfaces: $ifaces" true
+ if action "Saving interface configuration" save_interfaces; then
+ chmod +x "$script_interfaces"
+ fi
+ ;;
+ esac
+}
+
restart () {
if daemon_is_running ovsdb-server && daemon_is_running ovs-vswitchd; then
- script_flows=`mktemp`
- trap 'rm -f "${script_flows}"' 0
-
- action "Saving flows" ovs_save save-flows "${script_flows}" \
- script_flows
+ init_restore_scripts
+ save_interfaces_if_required
+ action "Saving flows" ovs_save save-flows "${script_flows}"
+ save_ofports_if_required
fi
# Restart the database first, since a large database may take a
stop_ovsdb
start_ovsdb
+ # Restore of ofports, if required, should happen before vswitchd is
+ # restarted.
+ restore_ofports
+
stop_forwarding
start_forwarding
- # Restore the saved flows. Do not return error if restore fails.
- restore_flows || true
+ # Restore the saved flows.
+ restore_flows
+
+ # Restore the interfaces if required. Return true even if restore fails.
+ restore_interfaces || true
}
## --------------- ##
If one or more datapaths are specified, information on only those
datapaths are displayed. Otherwise, \fBovs\-dpctl\fR displays information
about all configured datapaths.
+.SS "DEBUGGING COMMANDS"
+The following commands are primarily useful for debugging Open
+vSwitch. The flow table entries (both matches and actions) that they
+work with are not OpenFlow flow entries. Instead, they are different
+and considerably simpler flows maintained by the Open vSwitch kernel
+module. Use \fBovs\-ofctl\fR(8), instead, to work with OpenFlow flow
+entries.
+.
+.PP
+The \fIdp\fR argument to each of these commands is optional when
+exactly one datapath exists, in which case that datapath is the
+default. When multiple datapaths exist, then a datapath name is
+required.
.
.IP "\fBdump\-flows\fR [\fIdp\fR]"
Prints to the console all flow entries in datapath \fIdp\fR's
-flow table. If \fIdp\fR is not specified and exactly one datapath
-exists, the flows for that datapath will be printed.
+flow table.
+.
+.IP "\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR"
+.IQ "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | \fB\-\-statistics\fR] \fBmod\-flow\fR [\fIdp\fR] \fIflow actions\fR"
+Adds or modifies a flow in \fIdp\fR's flow table that, when a packet
+matching \fIflow\fR arrives, causes \fIactions\fR to be executed.
+.IP
+The \fBadd\-flow\fR command succeeds only if \fIflow\fR does not
+already exist in \fIdp\fR. Contrariwise, \fBmod\-flow\fR without
+\fB\-\-may\-create\fR only modifies the actions for an existing flow.
+With \fB\-\-may\-create\fR, \fBmod\-flow\fR will add a new flow or
+modify an existing one.
.IP
-This command is primarily useful for debugging Open vSwitch. The flow
-table entries that it displays are not
-OpenFlow flow entries. Instead, they are different and considerably
-simpler flows maintained by the Open vSwitch kernel module. If you wish
-to see the OpenFlow flow entries, use \fBovs\-ofctl dump\-flows\fR.
+If \fB\-s\fR or \fB\-\-statistics\fR is specified, then
+\fBmod\-flows\fR prints the modified flow's statistics. A flow's
+statistics are the number of packets and bytes that have passed
+through the flow, the elapsed time since the flow last processed a
+packet (if ever), and (for TCP flows) the union of the TCP flags
+processed through the flow.
+.IP
+With \fB\-\-clear\fR, \fBmod\-flows\fR zeros out the flow's
+statistics. The statistics printed if \fB\-s\fR or
+\fB\-\-statistics\fR is also specified are those from just before
+clearing the statistics.
+.
+.IP "[\fB\-s\fR | \fB\-\-statistics\fR] \fBdel\-flow\fR [\fIdp\fR] \fIflow\fR"
+Deletes the flow from \fIdp\fR's flow table that matches \fIflow\fR.
+If \fB\-s\fR or \fB\-\-statistics\fR is specified, then
+\fBmod\-flows\fR prints the deleted flow's statistics.
.
.IP "\fBdel\-flows\fR [\fIdp\fR]"
-Deletes all flow entries from datapath \fIdp\fR's flow table. If
-\fIdp\fR is not specified and exactly one datapath exists, the flows for
-that datapath will be deleted.
-.IP
-This command is primarily useful for debugging Open vSwitch. As
-discussed in \fBdump\-flows\fR, these entries are
-not OpenFlow flow entries. By deleting them, the process that set them
-up may be confused about their disappearance.
+Deletes all flow entries from datapath \fIdp\fR's flow table.
.
.SH OPTIONS
.IP "\fB\-s\fR"
VLOG_DEFINE_THIS_MODULE(dpctl);
-/* -s, --statistics: Print port statistics? */
+/* -s, --statistics: Print port/flow statistics? */
static bool print_statistics;
+/* --clear: Reset existing statistics to zero when modifying a flow? */
+static bool zero_statistics;
+
+/* --may-create: Allow mod-flows command to create a new flow? */
+static bool may_create;
+
/* -m, --more: Output verbosity.
*
* So far only undocumented commands honor this option, so we don't document
parse_options(int argc, char *argv[])
{
enum {
- OPT_DUMMY = UCHAR_MAX + 1,
+ OPT_CLEAR = UCHAR_MAX + 1,
+ OPT_MAY_CREATE,
VLOG_OPTION_ENUMS
};
static struct option long_options[] = {
{"statistics", no_argument, NULL, 's'},
+ {"clear", no_argument, NULL, OPT_CLEAR},
+ {"may-create", no_argument, NULL, OPT_MAY_CREATE},
{"more", no_argument, NULL, 'm'},
{"timeout", required_argument, NULL, 't'},
{"help", no_argument, NULL, 'h'},
print_statistics = true;
break;
+ case OPT_CLEAR:
+ zero_statistics = true;
+ break;
+
+ case OPT_MAY_CREATE:
+ may_create = true;
+ break;
+
case 'm':
verbosity++;
break;
" show show basic info on all datapaths\n"
" show DP... show basic info on each DP\n"
" dump-flows DP display flows in DP\n"
+ " add-flow DP FLOW ACTIONS add FLOW with ACTIONS to DP\n"
+ " mod-flow DP FLOW ACTIONS change FLOW actions to ACTIONS in DP\n"
+ " del-flow DP FLOW delete FLOW from DP\n"
" del-flows DP delete all flows from DP\n"
"Each IFACE on add-dp, add-if, and set-if may be followed by\n"
"comma-separated options. See ovs-dpctl(8) for syntax, or the\n"
"Interface table in ovs-vswitchd.conf.db(5) for an options list.\n",
program_name, program_name);
vlog_usage();
- printf("\nOptions for show:\n"
- " -s, --statistics print port statistics\n"
+ printf("\nOptions for show and mod-flow:\n"
+ " -s, --statistics print statistics for port or flow\n"
+ "\nOptions for mod-flow:\n"
+ " --may-create create flow if it doesn't exist\n"
+ " --clear reset existing stats to zero\n"
"\nOther options:\n"
" -t, --timeout=SECS give up after SECS seconds\n"
" -h, --help display this help message\n"
dpif_close(dpif);
}
+static void
+dpctl_put_flow(int argc, char *argv[], enum dpif_flow_put_flags flags)
+{
+ const char *key_s = argv[argc - 2];
+ const char *actions_s = argv[argc - 1];
+ struct dpif_flow_stats stats;
+ struct ofpbuf actions;
+ struct ofpbuf key;
+ struct dpif *dpif;
+ char *dp_name;
+
+ ofpbuf_init(&key, 0);
+ run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
+
+ ofpbuf_init(&actions, 0);
+ run(odp_actions_from_string(actions_s, NULL, &actions), "parsing actions");
+
+ dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp();
+ run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
+ free(dp_name);
+
+ run(dpif_flow_put(dpif, flags,
+ key.data, key.size,
+ actions.data, actions.size,
+ print_statistics ? &stats : NULL),
+ "updating flow table");
+
+ ofpbuf_uninit(&key);
+ ofpbuf_uninit(&actions);
+
+ if (print_statistics) {
+ struct ds s;
+
+ ds_init(&s);
+ dpif_flow_stats_format(&stats, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+ }
+}
+
+static void
+dpctl_add_flow(int argc, char *argv[])
+{
+ dpctl_put_flow(argc, argv, DPIF_FP_CREATE);
+}
+
+static void
+dpctl_mod_flow(int argc OVS_UNUSED, char *argv[])
+{
+ enum dpif_flow_put_flags flags;
+
+ flags = DPIF_FP_MODIFY;
+ if (may_create) {
+ flags |= DPIF_FP_CREATE;
+ }
+ if (zero_statistics) {
+ flags |= DPIF_FP_ZERO_STATS;
+ }
+
+ dpctl_put_flow(argc, argv, flags);
+}
+
+static void
+dpctl_del_flow(int argc, char *argv[])
+{
+ const char *key_s = argv[argc - 1];
+ struct dpif_flow_stats stats;
+ struct ofpbuf key;
+ struct dpif *dpif;
+ char *dp_name;
+
+ ofpbuf_init(&key, 0);
+ run(odp_flow_key_from_string(key_s, NULL, &key), "parsing flow key");
+
+ dp_name = argc == 2 ? xstrdup(argv[1]) : get_one_dp();
+ run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
+ free(dp_name);
+
+ run(dpif_flow_del(dpif,
+ key.data, key.size,
+ print_statistics ? &stats : NULL), "deleting flow");
+
+ ofpbuf_uninit(&key);
+
+ if (print_statistics) {
+ struct ds s;
+
+ ds_init(&s);
+ dpif_flow_stats_format(&stats, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+ }
+}
+
static void
dpctl_del_flows(int argc, char *argv[])
{
{ "dump-dps", 0, 0, dpctl_dump_dps },
{ "show", 0, INT_MAX, dpctl_show },
{ "dump-flows", 0, 1, dpctl_dump_flows },
+ { "add-flow", 2, 3, dpctl_add_flow },
+ { "mod-flow", 2, 3, dpctl_mod_flow },
+ { "del-flow", 1, 2, dpctl_del_flow },
{ "del-flows", 0, 1, dpctl_del_flows },
{ "help", 0, INT_MAX, dpctl_help },
dbdir='@DBDIR@'
fi
+ovs_ctl () {
+ echo "`date -u`:$@" >> "${logdir}/ovs-ctl.log"
+ "${datadir}/scripts/ovs-ctl" "$@" 2>&1 | tee -a "${logdir}/ovs-ctl.log"
+}
+
VERSION='@VERSION@'
DAEMON_CWD=/
stop_daemon () {
if test -e "$rundir/$1.pid"; then
if pid=`cat "$rundir/$1.pid"`; then
- for action in TERM .1 .25 .65 1 1 1 1 KILL 1 1 1 1 FAIL; do
+ for action in TERM .1 .25 .65 1 1 1 1 KILL 1 1 1 2 10 15 30 FAIL; do
+ if pid_exists "$pid" >/dev/null 2>&1; then :; else
+ return 0
+ fi
case $action in
TERM)
action "Killing $1 ($pid)" kill $pid
return 1
;;
*)
- if pid_exists $pid >/dev/null 2>&1; then
- sleep $action
- else
- return 0
- fi
+ sleep $action
;;
esac
done
Sets the TCP or UDP destination port to \fIport\fR.
.
.IP \fBmod_nw_tos\fB:\fItos\fR
-Sets the IPv4 ToS/DSCP field to \fItos\fR. Valid values are between 0 and
-255, inclusive. Note that the two lower reserved bits are never
-modified.
+Sets the IPv4 ToS/DSCP field to \fItos\fR, which must be a multiple of
+4 between 0 and 255. This action does not modify the two least
+significant bits of the ToS field (the ECN bits).
.RE
.IP
The following actions are Nicira vendor extensions that, as of this writing, are
``packet_in'' message will be sent only to the controllers having
controller id zero which have registered for the invalid ttl packets.
.
+.IP \fBset_mpls_ttl\fR:\fIttl\fR
+Set the TTL of the outer MPLS label stack entry of a packet.
+\fIttl\fR should be in the range 0 to 255 inclusive.
+.
+.IP \fBdec_mpls_ttl\fR
+Decrement TTL of the outer MPLS label stack entry of a packet. If the TTL
+is initially zero, no decrement occurs. Instead, a ``packet-in'' message
+with reason code \fBOFPR_INVALID_TTL\fR is sent to each connected
+controller with controller id zer that has enabled receiving them.
+Processing the current set of actions then stops. However, if the current
+set of actions was reached through ``resubmit'' then remaining actions in
+outer levels resume processing.
+.
.IP \fBnote:\fR[\fIhh\fR]...
Does nothing at all. Any number of bytes represented as hex digits
\fIhh\fR may be included. Pairs of hex digits may be separated by
Example: \fBload:55\->NXM_NX_REG2[0..5]\fR loads value 55 (bit pattern
\fB110111\fR) into bits 0 through 5, inclusive, in register 2.
.
+.IP "\fBpush:\fIsrc\fB[\fIstart\fB..\fIend\fB]"
+Pushes \fIstart\fR to \fIend\fR bits inclusive, in fields
+on top of the stack.
+.IP
+Example: \fBpush:NXM_NX_REG2[0..5]\fR push the value stored in register
+2 bits 0 through 5, inclusive, on to the internal stack.
+.
+.IP "\fBpop:\fIdst\fB[\fIstart\fB..\fIend\fB]"
+Pops from the top of the stack, retrieves the \fIstart\fR to \fIend\fR bits
+inclusive, from the value popped and store them into the corresponding
+bits in \fIdst\fR.
+.
+.IP
+Example: \fBpop:NXM_NX_REG2[0..5]\fR pops the value from top of the stack.
+Set register 2 bits 0 through 5, inclusive, based on bits 0 through 5 from the
+value just popped.
+.
.IP "\fBset_field:\fIvalue\fB\->\fIdst"
Writes the literal \fIvalue\fR into the field \fIdst\fR, which should
be specified as a name used for matching. (This is similar to
#! /bin/sh
-# Copyright (c) 2011 Nicira, Inc.
+# Copyright (c) 2011, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
}
ovs_vsctl () {
- ovs-vsctl --no-wait --timeout=1 "$@"
+ ovs-vsctl --no-wait "$@"
}
save_ofports ()
count=0
for iface in `ovs_vsctl list-ifaces ${bridge}`; do
ofport=`ovs_vsctl get interface ${iface} ofport`
- [ "${count}" -eq 0 ] && cmd="ovs-vsctl --no-wait --timeout=1"
+ [ "${count}" -eq 0 ] && cmd="ovs-vsctl --no-wait"
cmd="${cmd} -- --if-exists set interface "${iface}" \
ofport_request="${ofport}""
would normally happen only if the database cannot be contacted, or if
the system is overloaded.)
.
+.IP "\fB\-\-retry\fR"
+Without this option, if \fBovs\-vsctl\fR connects outward to the
+database server (the default) then \fBovs\-vsctl\fR will try to
+connect once and exit with an error if the connection fails (which
+usually means that \fBovsdb\-server\fR is not running).
+.IP
+With this option, or if \fB\-\-db\fR specifies that \fBovs\-vsctl\fR
+should listen for an incoming connection from the database server,
+then \fBovs\-vsctl\fR will wait for a connection to the database
+forever.
+.IP
+Regardless of this setting, \fB\-\-timeout\fR always limits how long
+\fBovs\-vsctl\fR will wait.
+.
.SS "Table Formatting Options"
These options control the format of output from the \fBlist\fR and
\fBfind\fR commands.
/* --timeout: Time to wait for a connection to 'db'. */
static int timeout;
+/* --retry: If true, ovs-vsctl will retry connecting to the database forever.
+ * If false and --db says to use an active connection method (e.g. "unix:",
+ * "tcp:", "ssl:"), then ovs-vsctl will try to connect once and exit with an
+ * error if the database server cannot be contacted (e.g. ovsdb-server is not
+ * running).
+ *
+ * Regardless of this setting, --timeout always limits how long ovs-vsctl will
+ * wait. */
+static bool retry;
+
/* Format for table output. */
static struct table_style table_style = TABLE_STYLE_DEFAULT;
}
/* Initialize IDL. */
- idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class, false);
+ idl = the_idl = ovsdb_idl_create(db, &ovsrec_idl_class, false, retry);
run_prerequisites(commands, n_commands, idl);
/* Execute the commands.
seqno = ovsdb_idl_get_seqno(idl);
for (;;) {
ovsdb_idl_run(idl);
+ if (!ovsdb_idl_is_alive(idl)) {
+ int retval = ovsdb_idl_get_last_error(idl);
+ vsctl_fatal("%s: database connection failed (%s)",
+ db, ovs_retval_to_string(retval));
+ }
if (seqno != ovsdb_idl_get_seqno(idl)) {
seqno = ovsdb_idl_get_seqno(idl);
OPT_DRY_RUN,
OPT_PEER_CA_CERT,
OPT_LOCAL,
+ OPT_RETRY,
VLOG_OPTION_ENUMS,
TABLE_OPTION_ENUMS
};
{"dry-run", no_argument, NULL, OPT_DRY_RUN},
{"oneline", no_argument, NULL, OPT_ONELINE},
{"timeout", required_argument, NULL, 't'},
+ {"retry", no_argument, NULL, OPT_RETRY},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
VLOG_LONG_OPTIONS,
}
break;
+ case OPT_RETRY:
+ retry = true;
+ break;
+
VLOG_OPTION_HANDLERS
TABLE_OPTION_HANDLERS(&table_style)
--db=DATABASE connect to DATABASE\n\
(default: %s)\n\
--no-wait do not wait for ovs-vswitchd to reconfigure\n\
+ --retry keep trying to connect to server forever\n\
-t, --timeout=SECS wait at most SECS seconds for ovs-vswitchd\n\
--dry-run do not commit changes to database\n\
--oneline print exactly one line of output per command\n",
bridge_init(const char *remote)
{
/* Create connection to database. */
- idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true);
+ idl = ovsdb_idl_create(remote, &ovsrec_idl_class, true, true);
idl_seqno = ovsdb_idl_get_seqno(idl);
ovsdb_idl_set_lock(idl, "ovs_vswitchd");
ovsdb_idl_verify_write_only(idl);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_speed);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_state);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_link_resets);
+ ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mac_in_use);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_mtu);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_ofport);
ovsdb_idl_omit_alert(idl, &ovsrec_interface_col_statistics);
int64_t bps;
int mtu;
int64_t mtu_64;
+ uint8_t mac[ETH_ADDR_LEN];
int error;
if (iface_is_synthetic(iface)) {
netdev_features_is_full_duplex(current)
? "full" : "half");
ovsrec_interface_set_link_speed(iface->cfg, &bps, 1);
- }
- else {
+ } else {
ovsrec_interface_set_duplex(iface->cfg, NULL);
ovsrec_interface_set_link_speed(iface->cfg, NULL, 0);
}
if (!error) {
mtu_64 = mtu;
ovsrec_interface_set_mtu(iface->cfg, &mtu_64, 1);
- }
- else {
+ } else {
ovsrec_interface_set_mtu(iface->cfg, NULL, 0);
}
+
+ error = netdev_get_etheraddr(iface->netdev, mac);
+ if (!error) {
+ char mac_string[32];
+
+ sprintf(mac_string, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
+ ovsrec_interface_set_mac_in_use(iface->cfg, mac_string);
+ } else {
+ ovsrec_interface_set_mac_in_use(iface->cfg, NULL);
+ }
}
/* Writes 'iface''s CFM statistics to the database. 'iface' must not be
iface_refresh_cfm_stats(struct iface *iface)
{
const struct ovsrec_interface *cfg = iface->cfg;
- int fault, opup, error;
- const uint64_t *rmps;
- size_t n_rmps;
- int health;
-
- fault = ofproto_port_get_cfm_fault(iface->port->bridge->ofproto,
- iface->ofp_port);
- if (fault >= 0) {
+ struct ofproto_cfm_status status;
+
+ if (!ofproto_port_get_cfm_status(iface->port->bridge->ofproto,
+ iface->ofp_port, &status)) {
+ ovsrec_interface_set_cfm_fault(cfg, NULL, 0);
+ ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
+ ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
+ ovsrec_interface_set_cfm_health(cfg, NULL, 0);
+ ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0);
+ } else {
const char *reasons[CFM_FAULT_N_REASONS];
- bool fault_bool = fault;
+ int64_t cfm_health = status.health;
+ bool faulted = status.faults != 0;
size_t i, j;
+ ovsrec_interface_set_cfm_fault(cfg, &faulted, 1);
+
j = 0;
for (i = 0; i < CFM_FAULT_N_REASONS; i++) {
int reason = 1 << i;
- if (fault & reason) {
+ if (status.faults & reason) {
reasons[j++] = cfm_fault_reason_to_str(reason);
}
}
-
- ovsrec_interface_set_cfm_fault(cfg, &fault_bool, 1);
ovsrec_interface_set_cfm_fault_status(cfg, (char **) reasons, j);
- } else {
- ovsrec_interface_set_cfm_fault(cfg, NULL, 0);
- ovsrec_interface_set_cfm_fault_status(cfg, NULL, 0);
- }
- opup = ofproto_port_get_cfm_opup(iface->port->bridge->ofproto,
- iface->ofp_port);
- if (opup >= 0) {
- ovsrec_interface_set_cfm_remote_opstate(cfg, opup ? "up" : "down");
- } else {
- ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
- }
-
- error = ofproto_port_get_cfm_remote_mpids(iface->port->bridge->ofproto,
- iface->ofp_port, &rmps, &n_rmps);
- if (error >= 0) {
- ovsrec_interface_set_cfm_remote_mpids(cfg, (const int64_t *)rmps,
- n_rmps);
- } else {
- ovsrec_interface_set_cfm_remote_mpids(cfg, NULL, 0);
- }
+ if (status.remote_opstate >= 0) {
+ const char *remote_opstate = status.remote_opstate ? "up" : "down";
+ ovsrec_interface_set_cfm_remote_opstate(cfg, remote_opstate);
+ } else {
+ ovsrec_interface_set_cfm_remote_opstate(cfg, NULL);
+ }
- health = ofproto_port_get_cfm_health(iface->port->bridge->ofproto,
- iface->ofp_port);
- if (health >= 0) {
- int64_t cfm_health = health;
- ovsrec_interface_set_cfm_health(cfg, &cfm_health, 1);
- } else {
- ovsrec_interface_set_cfm_health(cfg, NULL, 0);
+ ovsrec_interface_set_cfm_remote_mpids(cfg,
+ (const int64_t *)status.rmps,
+ status.n_rmps);
+ if (cfm_health >= 0) {
+ ovsrec_interface_set_cfm_health(cfg, &cfm_health, 1);
+ } else {
+ ovsrec_interface_set_cfm_health(cfg, NULL, 0);
+ }
}
}
ofproto_free_ofproto_controller_info(&info);
}
+\f
+/* "Instant" stats.
+ *
+ * Some information in the database must be kept as up-to-date as possible to
+ * allow controllers to respond rapidly to network outages. We call these
+ * statistics "instant" stats.
+ *
+ * We wish to update these statistics every INSTANT_INTERVAL_MSEC milliseconds,
+ * assuming that they've changed. The only means we have to determine whether
+ * they have changed are:
+ *
+ * - Try to commit changes to the database. If nothing changed, then
+ * ovsdb_idl_txn_commit() returns TXN_UNCHANGED, otherwise some other
+ * value.
+ *
+ * - instant_stats_run() is called late in the run loop, after anything that
+ * might change any of the instant stats.
+ *
+ * We use these two facts together to avoid waking the process up every
+ * INSTANT_INTERVAL_MSEC whether there is any change or not.
+ */
+
+/* Minimum interval between writing updates to the instant stats to the
+ * database. */
+#define INSTANT_INTERVAL_MSEC 100
+
+/* Current instant stats database transaction, NULL if there is no ongoing
+ * transaction. */
+static struct ovsdb_idl_txn *instant_txn;
+
+/* Next time (in msec on monotonic clock) at which we will update the instant
+ * stats. */
+static long long int instant_next_txn = LLONG_MIN;
+
+/* True if the run loop has run since we last saw that the instant stats were
+ * unchanged, that is, this is true if we need to wake up at 'instant_next_txn'
+ * to refresh the instant stats. */
+static bool instant_stats_could_have_changed;
static void
-refresh_instant_stats(void)
+instant_stats_run(void)
{
- static struct ovsdb_idl_txn *txn = NULL;
+ enum ovsdb_idl_txn_status status;
+
+ instant_stats_could_have_changed = true;
- if (!txn) {
+ if (!instant_txn) {
struct bridge *br;
- txn = ovsdb_idl_txn_create(idl);
+ if (time_msec() < instant_next_txn) {
+ return;
+ }
+ instant_next_txn = time_msec() + INSTANT_INTERVAL_MSEC;
+ instant_txn = ovsdb_idl_txn_create(idl);
HMAP_FOR_EACH (br, node, &all_bridges) {
struct iface *iface;
struct port *port;
}
}
- if (ovsdb_idl_txn_commit(txn) != TXN_INCOMPLETE) {
- ovsdb_idl_txn_destroy(txn);
- txn = NULL;
+ status = ovsdb_idl_txn_commit(instant_txn);
+ if (status != TXN_INCOMPLETE) {
+ ovsdb_idl_txn_destroy(instant_txn);
+ instant_txn = NULL;
+ }
+ if (status == TXN_UNCHANGED) {
+ instant_stats_could_have_changed = false;
}
}
+static void
+instant_stats_wait(void)
+{
+ if (instant_txn) {
+ ovsdb_idl_txn_wait(instant_txn);
+ } else if (instant_stats_could_have_changed) {
+ poll_timer_wait_until(instant_next_txn);
+ }
+}
+\f
/* Performs periodic activity required by bridges that needs to be done with
* the least possible latency.
*
}
run_system_stats();
- refresh_instant_stats();
+ instant_stats_run();
}
void
}
system_stats_wait();
+ instant_stats_wait();
}
/* Adds some memory usage statistics for bridges into 'usage', for use with
ovsrec_interface_set_duplex(if_cfg, NULL);
ovsrec_interface_set_link_speed(if_cfg, NULL, 0);
ovsrec_interface_set_link_state(if_cfg, NULL);
+ ovsrec_interface_set_mac_in_use(if_cfg, NULL);
ovsrec_interface_set_mtu(if_cfg, NULL, 0);
ovsrec_interface_set_cfm_fault(if_cfg, NULL, 0);
ovsrec_interface_set_cfm_fault_status(if_cfg, NULL, 0);
.so ofproto/ofproto-unixctl.man
.so lib/vlog-unixctl.man
.so lib/memory-unixctl.man
+.so lib/coverage-unixctl.man
.so lib/stress-unixctl.man
.
.SH "LIMITS"
{"name": "Open_vSwitch",
- "version": "7.0.0",
- "cksum": "3537583872 17299",
+ "version": "7.1.0",
+ "cksum": "2234055133 17444",
"tables": {
"Open_vSwitch": {
"columns": {
"ingress_policing_burst": {
"type": {"key": {"type": "integer",
"minInteger": 0}}},
+ "mac_in_use": {
+ "type": {"key": {"type": "string"},
+ "min": 0, "max": 1},
+ "ephemeral": true},
"mac": {
"type": {"key": {"type": "string"},
"min": 0, "max": 1}},
on a host.
</column>
+ <column name="mac_in_use">
+ The MAC address in use by this interface.
+ </column>
+
<column name="mac">
<p>Ethernet address to set for this interface. If unset then the
default MAC address is used:</p>
</p>
<column name="options" key="remote_ip">
- <p>
- Required. The tunnel endpoint. Unicast and multicast endpoints are
- both supported.
- </p>
-
- <p>
- When a multicast endpoint is specified, a routing table lookup occurs
- only when the tunnel is created. Following a routing change, delete
- and then re-create the tunnel to force a new routing table lookup.
- </p>
+ Required. The tunnel endpoint. Only unicast endpoints are supported.
</column>
<column name="options" key="local_ip">
Optional. The destination IP that received packets must match.
- Default is to match all addresses. Must be omitted when <ref
- column="options" key="remote_ip"/> is a multicast address.
+ Default is to match all addresses.
</column>
<column name="options" key="in_key">
two different hypervisors. That is, <code>active</code> means that
this <ref column="external_ids" key="iface-id"/> is the active
instance within a single hypervisor, not in a broader scope.
+ There is one exception: some hypervisors support ``migration'' from a
+ given hypervisor to itself (most often for test purposes). During
+ such a ``migration,'' two instances of a single <ref
+ column="external_ids" key="iface-id"/> might both be briefly marked
+ <code>active</code> on a single hypervisor.
</p>
</column>
the following scripts, which are described below:
* ovs-bugtool-cfm-show
- * ovs-bugtool-lcap-show
+ * ovs-bugtool-lacp-show
* ovs-bugtool-ovsdb-dump
* ovs-bugtool-tc-class-show
* ovs-bugtool-bond-show
+ * ovs-bugtool-ovs-ofctl-show
+ * ovs-bugtool-ovs-ofctl-dump-flows
+ * ovs-bugtool-ovs-appctl-dpif
+ * ovs-bugtool-coverage-show
+ * ovs-bugtool-memory-show
+ * ovs-bugtool-vsctl-show
system-configuration/openvswitch.xml
Script to dump tc class configuration for all network interfaces.
+ ovs-bugtool-ovs-ofctl-show
+
+ Script to dump information about flow tables and ports of each bridge.
+
+ ovs-bugtool-ovs-ofctl-dump-flows
+
+ Script to dump openflow flows of each bridge.
+
+ ovs-bugtool-ovs-appctl-dpif
+
+ Script to collect a summary of configured datapaths and datapath flows.
+
+ ovs-bugtool-coverage-show
+
+ Script to count the number of times particular events occur during
+ ovs-vswitchd's runtime.
+
+ ovs-bugtool-memory-show
+
+ Script to show some basic statistics about ovs-vswitchd's memory usage.
+
+ ovs-bugtool-vsctl-show
+
+ Script to show a brief overview of the database contents.
+
ovs-bugtool-daemons-ver
Script to dump version information for all Open vSwitch daemons.
}
start () {
- set $ovs_ctl ${1-start}
+ set ovs_ctl ${1-start}
set "$@" --system-id="$INSTALLATION_UUID"
set "$@" --system-type="$PRODUCT_BRAND"
set "$@" --system-version="$PRODUCT_VERSION-$BUILD_NUMBER"
start_ovs_xapi_sync
- $ovs_ctl --protocol=gre enable-protocol
+ ovs_ctl --protocol=gre enable-protocol
touch /var/lock/subsys/openvswitch
}
}
stop () {
- $ovs_ctl stop
+ ovs_ctl stop
stop_daemon ovs-xapi-sync
rm -f /var/lock/subsys/openvswitch
}
fi
}
-ovs_ctl=/usr/share/openvswitch/scripts/ovs-ctl
case $1 in
start)
start
fi
;;
status)
- $ovs_ctl status && daemon_status ovs-xapi-sync
+ ovs_ctl status && daemon_status ovs-xapi-sync
;;
version)
- $ovs_ctl version
+ ovs_ctl version
;;
force-reload-kmod)
force_reload_kmod
# ovs-vswitchd configuration that are managed in the xapi database when
# integrated with Citrix management tools.
-# Copyright (C) 2009, 2010, 2011, 2012 Nicira, Inc.
+# Copyright (C) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"--", "set-manager", 'ssl:' + controller + ':6632'])
def vswitchCfgQuery(action_args):
- cmd = [vsctl, "--timeout=5", "-vconsole:off"] + action_args
+ cmd = [vsctl, "-vconsole:off"] + action_args
output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
if len(output) == 0 or output[0] == None:
output = ""
# Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
-# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
+# Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
def vswitchCfgQuery(action_args):
cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(),
- '--timeout=5', '-vconsole:off'] + action_args
+ '-vconsole:off'] + action_args
output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
if len(output) == 0 or output[0] == None:
output = ""
+# Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc.
# Copyright (c) 2007-2011 Citrix Systems Inc.
-# Copyright (c) 2009,2010,2011,2012 Nicira, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@staticmethod
def Get(action):
try:
- arg = [vsctl, "--timeout=30", "-vconsole:off"] + action.split()
+ arg = [vsctl, "-vconsole:off"] + action.split()
output = ShellPipe(arg).Stdout()
except StandardError, e:
XSLogError("config retrieval error: " + str(e))