This patch provides two methods to return packets to the kernel
for handling as though OVS never touched them.
Firstly, we provide a bridge knob to the implicit learning switch that
is used on the 'normal' action to instead pass packets back to the
kernel.
Secondly, we provide a new flow action 'back_to_kernel' that explicitly
asks for packets to be sent back to the kernel.
To support this, we extend the datapath on kernels since 2.6.39 to be
able to pass packets back by providing a special output port number
('OVSP_NORMAL') which causes the hook to return RX_HANDLER_PASS. We
alter ovs-dpctl to be able to parse and display these flows.
We add a flag to the userspace bridge so that it is in either OVS mode
or kernel mode. This flag is inspected when a OPFF_NORMAL action is
encountered and, if true, the datapath is instructed to use the port
'OVSP_NORMAL'.
We extend the OVSDB schema to store this flag and extend ovs-vsctl to
manipulate this flag:
ovs-vsctl set-port-normal-mode br0 kernel
vs.
ovs-vsctl set-port-normal-mode br0 ovs
Finally, we add the 'back_to_kernel' action to OpenFlow which triggers a
datapath output action using 'OVSP_NORMAL'.
Signed-off-by: Chris Luke <chris_l...@cable.comcast.com>
---
Revision 1:
- Alter kernel hook to be able to return packets to the kernel.
- Add tweak to vswitch to use this feature instead of a learning
switch when it encounters OFPP_NORMAL.
Revision 2: (supercedes patch 2606)
- Refactor the path taken through the datapath to remove one skb clone
and lots of value returns.
- Add capability to insert a packet back to the kernel input queue.
- Add openflow action to send a packet back_to_kernel.
- Update tests/docs.
---
FAQ | 58 +++++++++++++++++++++++-----
datapath/actions.c | 56 +++++++++++++++++++++++++--
datapath/datapath.c | 23 ++++++++++--
datapath/datapath.h | 9 ++++-
datapath/flow_netlink.c | 4 +-
datapath/vport-netdev.c | 83 +++++++++++++++++++++++++++++++++++------
datapath/vport.c | 22 ++++++++++-
datapath/vport.h | 4 +-
include/linux/openvswitch.h | 3 ++
include/openflow/nicira-ext.h | 1 +
lib/odp-util.c | 21 ++++++++++-
lib/odp-util.h | 5 ++-
lib/ofp-actions.c | 32 ++++++++++++++++
lib/ofp-actions.h | 8 ++++
lib/ofp-parse.c | 4 ++
lib/ofp-util.def | 1 +
ofproto/ofproto-dpif-xlate.c | 37 ++++++++++++++++++
ofproto/ofproto-dpif-xlate.h | 5 +++
ofproto/ofproto-dpif.c | 8 ++++
ofproto/ofproto-dpif.h | 2 +
ofproto/ofproto-provider.h | 1 +
ofproto/ofproto.c | 9 +++++
ofproto/ofproto.h | 7 ++++
tests/odp.at | 1 +
tests/ofproto-macros.at | 2 +-
tests/ovs-vsctl.at | 2 +
utilities/ovs-ofctl.8.in | 7 ++++
utilities/ovs-vsctl.8.in | 26 +++++++++++++
utilities/ovs-vsctl.c | 65 +++++++++++++++++++++++++++++++-
vswitchd/bridge.c | 8 ++++
vswitchd/vswitch.ovsschema | 11 ++++--
vswitchd/vswitch.xml | 10 +++++
32 files changed, 493 insertions(+), 42 deletions(-)
diff --git a/FAQ b/FAQ
index 1edcd94..88f5397 100644
--- a/FAQ
+++ b/FAQ
@@ -509,9 +509,9 @@ Q: I created a bridge and added my Ethernet port to it,
using commands
and as soon as I ran the "add-port" command I lost all connectivity
through eth0. Help!
-A: A physical Ethernet device that is part of an Open vSwitch bridge
- should not have an IP address. If one does, then that IP address
- will not be fully functional.
+A: In the default Open vSwitch model, a physical Ethernet device that
+ is part of an Open vSwitch bridge should not have an IP address.
+ If one does, then that IP address will not be fully functional.
You can restore functionality by moving the IP address to an Open
vSwitch "internal" device, such as the network device named after
@@ -534,12 +534,52 @@ A: A physical Ethernet device that is part of an Open
vSwitch bridge
(e.g. br0). You might still need to manually clear the IP address
from the physical interface (e.g. with "ifconfig eth0 0.0.0.0").
- There is no compelling reason why Open vSwitch must work this way.
- However, this is the way that the Linux kernel bridge module has
- always worked, so it's a model that those accustomed to Linux
- bridging are already used to. Also, the model that most people
- expect is not implementable without kernel changes on all the
- versions of Linux that Open vSwitch supports.
+ This was the way that the Linux kernel bridge module always worked,
+ so it's a model that those accustomed to Linux bridging are already
+ used to.
+
+ Alternatively, Linux kernels since 2.6.39 allow packets that were
+ delivered to a module to be passed back to the kernel as though the
+ datapath never saw it; Open vSwitch provides methods to use this
+ in two ways:
+
+ - By disabling the implicit learning switch on the 'normal'
+ output action (also called OFPP_NORMAL) and instead handing
+ packets back to the kernel.
+
+ - By installing a flow that explicitly tells Open vSwitch to
+ hand packets back to the kernel. This is an Open vSwitch
+ extension to OpenFlow.
+
+ To configure a bridge to no longer act as an implicit learning
+ switch with the 'normal' action you need set the 'port normal' mode
+ to 'kernel'. You would then install a flow with 'action=normal' and
+ Open vSwitch will, instead of using its learning switch, simply pass
+ the matching packets back to the kernel for normal Linux processing.
+ For example:
+
+ ovs-vsctl set-port-normal-mode br0 kernel
+ # LLDP
+ ovs-ofctl add-flow br0 dl_type=0x88cc,action=normal
+ # ARP
+ ovs-ofctl add-flow br0 arp,action=normal
+ # ICMP
+ ovs-ofctl add-flow br0 ip,nw_proto=1,action=normal
+
+ To install a flow explicitly directing packets back to the kernel,
+ use the Open vSwitch specific 'action=back_to_kernel', for example:
+
+ # LLDP
+ ovs-ofctl add-flow br0 dl_type=0x88cc,action=back_to_kernel
+ # ARP
+ ovs-ofctl add-flow br0 arp,action=back_to_kernel
+ # ICMP
+ ovs-ofctl add-flow br0 ip,nw_proto=1,action=back_to_kernel
+
+ With either method, it's worth mentioning that it will have an impact
+ on IP addresses configured on the bridge internal port; you must
+ consider carefully what packets you want the kernel to process on the
+ kernel native port and which you want it to handle on Open vSwitch ports.
By the way, this issue is not specific to physical Ethernet
devices. It applies to all network devices except Open vswitch
diff --git a/datapath/actions.c b/datapath/actions.c
index 30ea1d2..6190d31 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -405,6 +405,34 @@ static int do_output(struct datapath *dp, struct sk_buff
*skb, int out_port)
return 0;
}
+static int do_insert(struct datapath *dp, struct sk_buff *skb)
+{
+ struct vport *vport;
+ int error = 0;
+
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ if (likely(OVS_CB(skb)->pkt_from_kernel)) {
+ /* Since we got this packet from the kernel
+ * we can simply set a flag to return it to
+ * the kernel. */
+ OVS_CB(skb)->return_pkt_to_kernel = true;
+ } else {
+ /* We got this packet from userspace so we
+ * need to insert this into the network input
+ * queue. */
+ vport = ovs_vport_rcu(dp, OVS_CB(skb)->flow->key.phy.in_port);
+ if (unlikely(!vport)) {
+ kfree_skb(skb);
+ return -ENODEV;
+ }
+
+ error = ovs_vport_insert(vport, skb_clone(skb, GFP_ATOMIC));
+ }
+ return error;
+}
+
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
const struct nlattr *attr)
{
@@ -514,7 +542,7 @@ static int do_execute_actions(struct datapath *dp, struct
sk_buff *skb,
* case is just a single output action, so that doing a clone and
* then freeing the original skbuff is wasteful. So the following code
* is slightly obscure just to avoid that. */
- int prev_port = -1;
+ u32 prev_port = OVSP_NONE;
const struct nlattr *a;
int rem;
@@ -522,14 +550,30 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
a = nla_next(a, &rem)) {
int err = 0;
- if (prev_port != -1) {
+ if (prev_port != OVSP_NONE) {
do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
- prev_port = -1;
+ prev_port = OVSP_NONE;
}
switch (nla_type(a)) {
case OVS_ACTION_ATTR_OUTPUT:
prev_port = nla_get_u32(a);
+ if(unlikely(prev_port > DP_MAX_PORTS)) {
+ switch(prev_port) {
+ case OVSP_NORMAL:
+ do_insert(dp, skb);
+ /* If we need to return the packet to
the kernel,
+ * keep the skb for it. */
+ if (OVS_CB(skb)->return_pkt_to_kernel)
+ keep_skb = true;
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+ prev_port = OVSP_NONE;
+ }
break;
case OVS_ACTION_ATTR_USERSPACE:
@@ -552,6 +596,10 @@ static int do_execute_actions(struct datapath *dp, struct
sk_buff *skb,
case OVS_ACTION_ATTR_SAMPLE:
err = sample(dp, skb, a);
+ /* If a sampled output action has us use OVSP_NORMAL,
+ * we need to keep the skb around. */
+ if (OVS_CB(skb)->return_pkt_to_kernel)
+ keep_skb = true;
break;
}
@@ -561,7 +609,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
}
}
- if (prev_port != -1) {
+ if (prev_port != OVSP_NONE) {
if (keep_skb)
skb = skb_clone(skb, GFP_ATOMIC);
diff --git a/datapath/datapath.c b/datapath/datapath.c
index b42fd8b..1e3369e 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -215,7 +215,7 @@ void ovs_dp_detach_port(struct vport *p)
}
/* Must be called with rcu_read_lock. */
-void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
+int ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
{
struct datapath *dp = p->dp;
struct sw_flow *flow;
@@ -231,7 +231,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct
sk_buff *skb)
error = ovs_flow_extract(skb, p->port_no, &key);
if (unlikely(error)) {
kfree_skb(skb);
- return;
+ return error;
}
/* Look up flow. */
@@ -253,7 +253,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct
sk_buff *skb)
OVS_CB(skb)->pkt_key = &key;
ovs_flow_stats_update(OVS_CB(skb)->flow, skb);
- ovs_execute_actions(dp, skb);
+ error = ovs_execute_actions(dp, skb);
stats_counter = &stats->n_hit;
out:
@@ -262,6 +262,8 @@ out:
(*stats_counter)++;
stats->n_mask_hit += n_mask_hit;
u64_stats_update_end(&stats->sync);
+
+ return error;
}
static struct genl_family dp_packet_genl_family = {
@@ -544,6 +546,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb,
struct genl_info *info)
OVS_CB(packet)->pkt_key = &flow->key;
packet->priority = flow->key.phy.priority;
packet->mark = flow->key.phy.skb_mark;
+ OVS_CB(packet)->pkt_from_kernel = false;
rcu_read_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -551,6 +554,20 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb,
struct genl_info *info)
if (!dp)
goto err_unlock;
+ /* Work out the dev of the original input port */
+ if (flow->key.phy.in_port != DP_MAX_PORTS && flow->key.phy.in_port !=
OVSP_LOCAL) {
+ struct vport *vport;
+ vport = ovs_vport_rcu(dp, flow->key.phy.in_port);
+ if (vport != NULL)
+ packet->dev = dev_get_by_name(sock_net(skb->sk),
vport->ops->get_name(vport));
+ if (packet->dev != NULL)
+ packet->protocol = eth_type_trans(packet, packet->dev);
+ }
+ /* Failing that, use the loopback device */
+ if (packet->dev == NULL) {
+ packet->dev = dev_get_by_name(sock_net(skb->sk), "lo");
+ }
+
local_bh_disable();
err = ovs_execute_actions(dp, packet);
local_bh_enable();
diff --git a/datapath/datapath.h b/datapath/datapath.h
index b3ae7cd..c012d2a 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -100,11 +100,18 @@ struct datapath {
* @pkt_key: The flow information extracted from the packet. Must be nonnull.
* @tun_key: Key for the tunnel that encapsulated this packet. NULL if the
* packet is not being tunneled.
+ * @pkt_from_kernel: True if this packet was handed to us by the kernel, false
+ * if it came from userspace.
+ * @return_pkt_to_kernel: True if the action of a matching flow wants us to
+ * give this packet back to the kernel. Only relevant if it came from the
+ * kernel.
*/
struct ovs_skb_cb {
struct sw_flow *flow;
struct sw_flow_key *pkt_key;
struct ovs_key_ipv4_tunnel *tun_key;
+ bool pkt_from_kernel;
+ bool return_pkt_to_kernel;
};
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
@@ -186,7 +193,7 @@ static inline struct vport *ovs_vport_ovsl(const struct datapath *dp, int port_n
extern struct notifier_block ovs_dp_device_notifier;
extern struct genl_multicast_group ovs_dp_vport_multicast_group;
-void ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
+int ovs_dp_process_received_packet(struct vport *, struct sk_buff *);
void ovs_dp_detach_port(struct vport *);
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
const struct dp_upcall_info *);
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 9b26528..d2c2ce0 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -1520,6 +1520,7 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
const struct ovs_action_push_vlan *vlan;
int type = nla_type(a);
bool skip_copy;
+ u32 port;
if (type > OVS_ACTION_ATTR_MAX ||
(action_lens[type] != nla_len(a) &&
@@ -1538,7 +1539,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
break;
case OVS_ACTION_ATTR_OUTPUT:
- if (nla_get_u32(a) >= DP_MAX_PORTS)
+ port = nla_get_u32(a);
+ if (port >= DP_MAX_PORTS && port != OVSP_NORMAL)
return -EINVAL;
break;
diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c
index c15923b..16dfc08 100644
--- a/datapath/vport-netdev.c
+++ b/datapath/vport-netdev.c
@@ -34,7 +34,7 @@
#include "vport-internal_dev.h"
#include "vport-netdev.h"
-static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
+static bool netdev_port_receive(struct vport *vport, struct sk_buff *skb);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
/* Called with rcu_read_lock and bottom-halves disabled. */
@@ -48,7 +48,10 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff
**pskb)
vport = ovs_netdev_get_vport(skb->dev);
- netdev_port_receive(vport, skb);
+ if (netdev_port_receive(vport, skb)) {
+ /* Tell the kernel we didn't want it. */
+ return RX_HANDLER_PASS;
+ }
return RX_HANDLER_CONSUMED;
}
@@ -64,7 +67,10 @@ static struct sk_buff *netdev_frame_hook(struct sk_buff *skb)
vport = ovs_netdev_get_vport(skb->dev);
- netdev_port_receive(vport, skb);
+ if (netdev_port_receive(vport, skb)) {
+ /* Tell the kernel we didn't want it. */
+ return skb;
+ }
return NULL;
}
@@ -189,31 +195,51 @@ const char *ovs_netdev_get_name(const struct vport *vport)
return netdev_vport->dev->name;
}
-/* Must be called with rcu_read_lock. */
-static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
+/* Must be called with rcu_read_lock.
+ * Returns true if we want the hook to return the packet to the kernel.
+ */
+static bool netdev_port_receive(struct vport *vport, struct sk_buff *skb)
{
+ int error;
+
if (unlikely(!vport))
goto error;
if (unlikely(skb_warn_if_lro(skb)))
goto error;
- /* Make our own copy of the packet. Otherwise we will mangle the
- * packet for anyone who came before us (e.g. tcpdump via AF_PACKET).
- * (No one comes after us, since we tell handle_bridge() that we took
- * the packet.) */
+ /* Make a clone of the skb if someone else has a reference to it so
+ * nothing we do with it interferes with anyone higher up the chain.
+ */
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
- return;
+ return false;
skb_push(skb, ETH_HLEN);
ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
- ovs_vport_receive(vport, skb, NULL);
- return;
+ OVS_CB(skb)->pkt_from_kernel = true;
+ OVS_CB(skb)->return_pkt_to_kernel = false;
+
+ error = ovs_vport_receive(vport, skb, NULL);
+ if (unlikely(error)) {
+ /* If we encountered an error, then the skb
+ * has been freed and we cannot look at it
+ * safely anymore. */
+ return false;
+ }
+
+ if (OVS_CB(skb)->return_pkt_to_kernel) {
+ /* Clean up the skb */
+ skb->protocol = eth_type_trans(skb, skb->dev);
+ return true;
+ }
+
+ return false;
error:
kfree_skb(skb);
+ return false;
}
static unsigned int packet_length(const struct sk_buff *skb)
@@ -250,6 +276,38 @@ drop:
return 0;
}
+/* Must be called with rcu_read_lock. */
+static int netdev_insert(struct sk_buff *skb)
+{
+ int len, ret;
+
+ if (unlikely(skb == NULL || skb->dev == NULL))
+ return -EINVAL;
+
+ skb_reset_network_header(skb);
+ len = skb->len;
+
+ /* This stops it looping if it somehow
+ * doesn't match a flow on re-entry */
+ skb->pkt_type = PACKET_LOOPBACK;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+ ret = netif_rx_ni(skb);
+#else
+ /* unsupported kernel version */
+ kfree_skb(skb);
+ ret = -EINVAL;
+#endif
+
+ if (likely(ret == NET_RX_SUCCESS)) {
+ return len;
+ } else if(ret == NET_RX_DROP) {
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
/* Returns null if this device is not attached to a datapath. */
struct vport *ovs_netdev_get_vport(struct net_device *dev)
{
@@ -278,6 +336,7 @@ const struct vport_ops ovs_netdev_vport_ops = {
.destroy = netdev_destroy,
.get_name = ovs_netdev_get_name,
.send = netdev_send,
+ .insert = netdev_insert,
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) && \
diff --git a/datapath/vport.c b/datapath/vport.c
index 7f12acc..14c2d66 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -358,8 +358,9 @@ int ovs_vport_get_options(const struct vport *vport, struct
sk_buff *skb)
* Must be called with rcu_read_lock. The packet cannot be shared and
* skb->data should point to the Ethernet header. The caller must have already
* called compute_ip_summed() to initialize the checksumming fields.
+ * Returns 0 on success, or -errno otherwise.
*/
-void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
+int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
struct ovs_key_ipv4_tunnel *tun_key)
{
struct pcpu_tstats *stats;
@@ -371,7 +372,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff
*skb,
u64_stats_update_end(&stats->syncp);
OVS_CB(skb)->tun_key = tun_key;
- ovs_dp_process_received_packet(vport, skb);
+ return ovs_dp_process_received_packet(vport, skb);
}
/**
@@ -406,6 +407,23 @@ int ovs_vport_send(struct vport *vport, struct sk_buff
*skb)
}
/**
+ * ovs_vport_insert - send a packet to the input queue
+ *
+ * @vport: vport from which to insert the packet
+ * @skb: skb to send
+ *
+ * Sends the given packet and returns the length of data sent. Either ovs
+ * lock or rcu_read_lock must be held. Returns 0 if the packet was
+ * dropped and -EINVAL for any errors.
+ */
+int ovs_vport_insert(struct vport *vport, struct sk_buff *skb)
+{
+ if (likely(vport->ops->insert != NULL))
+ return vport->ops->insert(skb);
+ return -EINVAL;
+}
+
+/**
* ovs_vport_record_error - indicate device error to generic stats layer
*
* @vport: vport that encountered the error
diff --git a/datapath/vport.h b/datapath/vport.h
index 2cf2b18..34303dd 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -51,6 +51,7 @@ int ovs_vport_set_options(struct vport *, struct nlattr
*options);
int ovs_vport_get_options(const struct vport *, struct sk_buff *);
int ovs_vport_send(struct vport *, struct sk_buff *);
+int ovs_vport_insert(struct vport *, struct sk_buff *);
/* The following definitions are for implementers of vport devices: */
@@ -146,6 +147,7 @@ struct vport_ops {
const char *(*get_name)(const struct vport *);
int (*send)(struct vport *, struct sk_buff *);
+ int (*insert)(struct sk_buff *);
};
enum vport_err_type {
@@ -191,7 +193,7 @@ static inline struct vport *vport_from_priv(const void
*priv)
return (struct vport *)(priv - ALIGN(sizeof(struct vport),
VPORT_ALIGN));
}
-void ovs_vport_receive(struct vport *, struct sk_buff *,
+int ovs_vport_receive(struct vport *, struct sk_buff *,
struct ovs_key_ipv4_tunnel *);
/* List of statically compiled vport implementations. Don't forget to also
diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h
index 5137c2f..179d529 100644
--- a/include/linux/openvswitch.h
+++ b/include/linux/openvswitch.h
@@ -139,7 +139,10 @@ struct ovs_vport_stats {
#define OVS_DP_F_UNALIGNED (1 << 0)
/* Fixed logical ports. */
+#define OVSP_MAX (4294967295U)
#define OVSP_LOCAL ((__u32)0)
+#define OVSP_NONE ((__u32)OVSP_MAX)
+#define OVSP_NORMAL ((__u32)OVSP_MAX-1)
/* Packet transfer. */
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 22939f4..aadab31 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -314,6 +314,7 @@ enum nx_action_subtype {
NXAST_SAMPLE, /* struct nx_action_sample */
NXAST_SET_MPLS_LABEL, /* struct nx_action_ttl */
NXAST_SET_MPLS_TC, /* struct nx_action_ttl */
+ NXAST_BACK_TO_KERNEL, /* struct nx_action_header */
};
/* Header for Nicira-defined actions. */
diff --git a/lib/odp-util.c b/lib/odp-util.c
index f44c7d4..fabf50f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -385,8 +385,17 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
}
switch (type) {
- case OVS_ACTION_ATTR_OUTPUT:
- ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ case OVS_ACTION_ATTR_OUTPUT: {
+ uint32_t port = nl_attr_get_u32(a);
+ switch (port) {
+ case OVSP_NORMAL:
+ ds_put_format(ds, "output(normal)");
+ break;
+ default:
+ ds_put_format(ds, "%"PRIu32, port);
+ break;
+ }
+ }
break;
case OVS_ACTION_ATTR_USERSPACE:
format_odp_userspace_action(ds, a);
@@ -476,6 +485,14 @@ parse_odp_action(const char *s, const struct simap
*port_names,
}
}
+ {
+ int len = strcspn(s, delimiters);
+ if (strncmp(s, "output(normal)", len) == 0) {
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, OVSP_NORMAL);
+ return len;
+ }
+ }
+
if (port_names) {
int len = strcspn(s, delimiters);
struct simap_node *node;
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 821b2c4..79b0327 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -65,8 +65,9 @@ enum slow_path_reason {
const char *slow_path_reason_to_explanation(enum slow_path_reason);
-#define ODPP_LOCAL ODP_PORT_C(OVSP_LOCAL)
-#define ODPP_NONE ODP_PORT_C(UINT32_MAX)
+#define ODPP_LOCAL ODP_PORT_C(OVSP_LOCAL)
+#define ODPP_NONE ODP_PORT_C(OVSP_NONE)
+#define ODPP_NORMAL ODP_PORT_C(OVSP_NORMAL)
void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
size_t actions_len);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index df7aebd..72705b3 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -503,6 +503,10 @@ ofpact_from_nxast(const union ofp_action *a, enum
ofputil_action_code code,
case OFPUTIL_NXAST_SAMPLE:
error = sample_from_openflow(&a->sample, out);
break;
+
+ case OFPUTIL_NXAST_BACK_TO_KERNEL:
+ ofpact_put_BACK_TO_KERNEL(out);
+ break;
}
return error;
@@ -1335,6 +1339,7 @@ ofpact_is_set_action(const struct ofpact *a)
case OFPACT_STRIP_VLAN:
case OFPACT_WRITE_ACTIONS:
case OFPACT_WRITE_METADATA:
+ case OFPACT_BACK_TO_KERNEL:
return false;
default:
OVS_NOT_REACHED();
@@ -1394,6 +1399,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
case OFPACT_SAMPLE:
case OFPACT_STACK_POP:
case OFPACT_STACK_PUSH:
+ case OFPACT_BACK_TO_KERNEL:
/* The action set may only include actions and thus
* may not include any instructions */
@@ -1632,6 +1638,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type
type)
case OFPACT_NOTE:
case OFPACT_EXIT:
case OFPACT_SAMPLE:
+ case OFPACT_BACK_TO_KERNEL:
default:
return OVSINST_OFPIT11_APPLY_ACTIONS;
}
@@ -2118,6 +2125,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols,
struct ofpact *a,
case OFPACT_GROUP:
return 0;
+ case OFPACT_BACK_TO_KERNEL:
+ return 0;
+
default:
OVS_NOT_REACHED();
}
@@ -2351,6 +2361,16 @@ ofpact_sample_to_nxast(const struct ofpact_sample *os,
}
static void
+ofpact_back_to_kernel_to_nxast(const struct ofpact_back_to_kernel *oc,
+ struct ofpbuf *out)
+{
+ struct nx_action_header *nac;
+ nac = ofputil_put_NXAST_BACK_TO_KERNEL(out);
+ (void)nac;
+ (void)oc;
+}
+
+static void
ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
{
switch (a->type) {
@@ -2460,6 +2480,10 @@ ofpact_to_nxast(const struct ofpact *a, struct ofpbuf
*out)
ofpact_sample_to_nxast(ofpact_get_SAMPLE(a), out);
break;
+ case OFPACT_BACK_TO_KERNEL:
+ ofpact_back_to_kernel_to_nxast(ofpact_get_BACK_TO_KERNEL(a), out);
+ break;
+
case OFPACT_GROUP:
case OFPACT_OUTPUT:
case OFPACT_ENQUEUE:
@@ -2616,6 +2640,7 @@ ofpact_to_openflow10(const struct ofpact *a, struct
ofpbuf *out)
case OFPACT_PUSH_MPLS:
case OFPACT_POP_MPLS:
case OFPACT_SAMPLE:
+ case OFPACT_BACK_TO_KERNEL:
ofpact_to_nxast(a, out);
break;
}
@@ -2809,6 +2834,7 @@ ofpact_to_openflow11(const struct ofpact *a, struct
ofpbuf *out)
case OFPACT_NOTE:
case OFPACT_EXIT:
case OFPACT_SAMPLE:
+ case OFPACT_BACK_TO_KERNEL:
ofpact_to_nxast(a, out);
break;
}
@@ -3137,6 +3163,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact,
ofp_port_t port)
case OFPACT_GOTO_TABLE:
case OFPACT_METER:
case OFPACT_GROUP:
+ case OFPACT_BACK_TO_KERNEL:
default:
return false;
}
@@ -3561,6 +3588,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
ds_put_format(s, "group:%"PRIu32,
ofpact_get_GROUP(a)->group_id);
break;
+
+ case OFPACT_BACK_TO_KERNEL:
+ ds_put_cstr(s, "back_to_kernel");
+ break;
+
}
}
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 00cba6a..801959a 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -57,6 +57,7 @@
DEFINE_OFPACT(ENQUEUE, ofpact_enqueue, ofpact) \
DEFINE_OFPACT(OUTPUT_REG, ofpact_output_reg, ofpact) \
DEFINE_OFPACT(BUNDLE, ofpact_bundle, slaves) \
+ DEFINE_OFPACT(BACK_TO_KERNEL, ofpact_back_to_kernel, ofpact) \
\
/* Header changes. */ \
DEFINE_OFPACT(SET_FIELD, ofpact_set_field, ofpact) \
@@ -258,6 +259,13 @@ struct ofpact_bundle {
ofp_port_t slaves[];
};
+/* OFPACT_BACK_TO_KERNEL
+ *
+ * Used for NXAST_BACK_TO_KERNEL. */
+struct ofpact_back_to_kernel {
+ struct ofpact ofpact;
+};
+
/* OFPACT_SET_VLAN_VID.
*
* We keep track if vlan was present at action validation time to avoid a
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 251adfa..d4bcd2c 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -905,6 +905,10 @@ parse_named_action(enum ofputil_action_code code,
case OFPUTIL_NXAST_SAMPLE:
error = parse_sample(ofpacts, arg);
break;
+
+ case OFPUTIL_NXAST_BACK_TO_KERNEL:
+ ofpact_put_BACK_TO_KERNEL(ofpacts);
+ break;
}
if (error) {
diff --git a/lib/ofp-util.def b/lib/ofp-util.def
index fae2bf2..951875f 100644
--- a/lib/ofp-util.def
+++ b/lib/ofp-util.def
@@ -77,6 +77,7 @@ 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")
NXAST_ACTION(NXAST_SAMPLE, nx_action_sample, 0, "sample")
+NXAST_ACTION(NXAST_BACK_TO_KERNEL, nx_action_header, 0,
"back_to_kernel")
#undef OFPAT10_ACTION
#undef OFPAT11_ACTION
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 848c778..d87fcef 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1425,6 +1425,13 @@ xlate_normal(struct xlate_ctx *ctx)
return;
}
+ /* Are we using the OVS or the kernel 'normal' mode? */
+ if(ctx->xin->normal_uses_kernel) {
+ ctx->xout->nf_output_iface = NF_OUT_MULTI;
+ compose_output_action(ctx, OFPP_NORMAL);
+ return;
+ }
+
/* Learn source MAC. */
if (ctx->xin->may_learn) {
update_learning_table(ctx->xbridge, flow, wc, vlan, in_xbundle);
@@ -1689,6 +1696,23 @@ compose_output_action__(struct xlate_ctx *ctx,
ofp_port_t ofp_port,
* before traversing a patch port. */
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23);
+ /* If we get this far and the output port is OFPP_NORMAL then
+ * we want to direct the packet back to the kernel. This can
+ * probably be done more gracefully, but for now just send the
+ * message to the datapath. */
+ if (ofp_port == OFPP_NORMAL) {
+ ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
+ &ctx->xout->odp_actions,
+ &ctx->xout->wc,
+ &ctx->mpls_depth_delta);
+ nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
+ ODPP_NORMAL);
+ ctx->sflow_odp_port = ODPP_NORMAL;
+ ctx->sflow_n_outputs++;
+ ctx->xout->nf_output_iface = ofp_port;
+ return;
+ }
+
if (!xport) {
xlate_report(ctx, "Nonexistent output port");
return;
@@ -2573,6 +2597,13 @@ xlate_action_set(struct xlate_ctx *ctx)
}
static void
+xlate_back_to_kernel_action(struct xlate_ctx *ctx)
+{
+ compose_output_action(ctx, OFPP_NORMAL);
+ ctx->xout->nf_output_iface = NF_OUT_MULTI;
+}
+
+static void
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct xlate_ctx *ctx)
{
@@ -2865,6 +2896,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t
ofpacts_len,
case OFPACT_SAMPLE:
xlate_sample_action(ctx, ofpact_get_SAMPLE(a));
break;
+
+ case OFPACT_BACK_TO_KERNEL:
+ xlate_back_to_kernel_action(ctx);
+ break;
}
}
}
@@ -2874,6 +2909,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif
*ofproto,
const struct flow *flow, struct rule_dpif *rule,
uint16_t tcp_flags, const struct ofpbuf *packet)
{
+ struct ofproto *ofproto_ = ofproto_dpif_uncast(ofproto);
xin->ofproto = ofproto;
xin->flow = *flow;
xin->packet = packet;
@@ -2886,6 +2922,7 @@ xlate_in_init(struct xlate_in *xin, struct ofproto_dpif
*ofproto,
xin->report_hook = NULL;
xin->resubmit_stats = NULL;
xin->skip_wildcards = false;
+ xin->normal_uses_kernel = (ofproto_->port_normal_mode ==
OFPROTO_PORT_NORMAL_MODE_KERNEL);
}
void
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 68076ca..cd7cd97 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -73,6 +73,11 @@ struct xlate_in {
* not if we are just revalidating. */
bool may_learn;
+ /* The model OFPP_NORMAL is treated with. If true then we use Kernel
+ * processing. If false then we use the OVS learning switch model.
+ */
+ bool normal_uses_kernel;
+
/* If the caller of xlate_actions() doesn't need the flow_wildcards
* contained in struct xlate_out. 'skip_wildcards' can be set to true
* disabling the expensive wildcard computation. When true, 'wc' in struct
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 52759b5..0e09692 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -314,6 +314,14 @@ ofproto_dpif_cast(const struct ofproto *ofproto)
return CONTAINER_OF(ofproto, struct ofproto_dpif, up);
}
+/* Extract the pointer to an ofproto from an ofproto-dpif. */
+struct ofproto *
+ofproto_dpif_uncast(struct ofproto_dpif *ofproto)
+{
+ ovs_assert(ofproto->up.ofproto_class == &ofproto_dpif_class);
+ return &(ofproto->up);
+}
+
static struct ofport_dpif *get_ofp_port(const struct ofproto_dpif *ofproto,
ofp_port_t ofp_port);
static void ofproto_trace(struct ofproto_dpif *, const struct flow *,
diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h
index 51cb38f..8d0da1b 100644
--- a/ofproto/ofproto-dpif.h
+++ b/ofproto/ofproto-dpif.h
@@ -62,6 +62,8 @@ struct OVS_LOCKABLE group_dpif;
* Ofproto-dpif-xlate is responsible for translating translating OpenFlow
* actions into datapath actions. */
+struct ofproto *ofproto_dpif_uncast(struct ofproto_dpif *ofproto);
+
void rule_dpif_lookup(struct ofproto_dpif *, const struct flow *,
struct flow_wildcards *, struct rule_dpif **rule);
diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
index cc318ee..c2c505d 100644
--- a/ofproto/ofproto-provider.h
+++ b/ofproto/ofproto-provider.h
@@ -71,6 +71,7 @@ struct ofproto {
uint64_t datapath_id; /* Datapath ID. */
bool forward_bpdu; /* Option to allow forwarding of BPDU frames
* when NORMAL action is invoked. */
+ enum ofproto_port_normal_mode port_normal_mode; /* OVS or Kernel
handles OFPP_NORMAL. */
char *mfr_desc; /* Manufacturer (NULL for default)b. */
char *hw_desc; /* Hardware (NULL for default). */
char *sw_desc; /* Software version (NULL for default). */
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 676a6cb..db59981 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -705,6 +705,15 @@ ofproto_set_flow_miss_model(unsigned model)
flow_miss_model = model;
}
+/* Chooses between OVS learning switch or native kernel based
+ * behavior for the OFPP_NORMAL action.
+ */
+void
+ofproto_set_port_normal_mode(struct ofproto *p, enum ofproto_port_normal_mode
port_normal_mode)
+{
+ p->port_normal_mode = port_normal_mode;
+}
+
/* If forward_bpdu is true, the NORMAL action will forward frames with
* reserved (e.g. STP) destination Ethernet addresses. if forward_bpdu is
false,
* the NORMAL action will drop these frames. */
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 3034d32..677c980 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -134,6 +134,12 @@ enum ofproto_band {
OFPROTO_OUT_OF_BAND /* Out-of-band connection to controller. */
};
+/* Behavior for OFPP_NORMAL */
+enum ofproto_port_normal_mode {
+ OFPROTO_PORT_NORMAL_MODE_OVS, /* OVS' learning switch default */
+ OFPROTO_PORT_NORMAL_MODE_KERNEL /* Pass packet back to the kernel */
+};
+
struct ofproto_controller {
char *target; /* e.g. "tcp:127.0.0.1" */
int max_backoff; /* Maximum reconnection backoff, in seconds. */
@@ -244,6 +250,7 @@ void ofproto_set_extra_in_band_remotes(struct ofproto *,
void ofproto_set_in_band_queue(struct ofproto *, int queue_id);
void ofproto_set_flow_limit(unsigned limit);
void ofproto_set_flow_miss_model(unsigned model);
+void ofproto_set_port_normal_mode(struct ofproto *, enum
ofproto_port_normal_mode port_normal_mode);
void ofproto_set_forward_bpdu(struct ofproto *, bool forward_bpdu);
void ofproto_set_mac_table_config(struct ofproto *, unsigned idle_time,
size_t max_entries);
diff --git a/tests/odp.at b/tests/odp.at
index b505345..c7ea1ad 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -243,6 +243,7 @@ pop_vlan
sample(sample=9.7%,actions(1,2,3,push_vlan(vid=1,pcp=2)))
set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(df,csum,key)))
set(tunnel(tun_id=0xabcdef1234567890,src=1.1.1.1,dst=2.2.2.2,tos=0x0,ttl=64,flags(key)))
+output(normal)
])
AT_CHECK_UNQUOTED([test-odp parse-actions < actions.txt], [0],
[`cat actions.txt`
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index 3bcffc2..1be379b 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -83,7 +83,7 @@ m4_define([OVS_VSWITCHD_START],
/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
dnl Add bridges, ports, etc.
- AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy
other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00
protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]] fail-mode=secure --
$1 m4_if([$2], [], [], [| ${PERL} $srcdir/uuidfilt.pl])], [0], [$2])
+ AT_CHECK([ovs-vsctl -- add-br br0 -- set bridge br0 datapath-type=dummy
other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00
protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13]] fail-mode=secure
port-normal-mode=ovs -- $1 m4_if([$2], [], [], [| ${PERL}
$srcdir/uuidfilt.pl])], [0], [$2])
])
m4_divert_push([PREPARE_TESTS])
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index 851e4d8..91adf2e 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -643,6 +643,7 @@ mirrors : []
name : "br0"
netflow : []
other_config : {}
+port_normal_mode : []
ports : []
protocols : []
sflow : []
@@ -1135,6 +1136,7 @@ mirrors : []
name : "br0"
netflow : []
other_config : {}
+port_normal_mode : []
ports : []
protocols : []
sflow : []
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 9a8fd33..af56015 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1671,6 +1671,13 @@ configuring sample collector sets.
.IP
This action was added in Open vSwitch 1.10.90.
.
+.IP "\fBback_to_kernel\fR"
+Causes packets that match to be returned to the system kernel as though
+Open vSwitch never saw them. If this is not supported by the running
+kernel this is effectively a non-operation.
+.IP
+This action was added in Open vSwitch 2.1.x.
+.
.IP "\fBexit\fR"
This action causes Open vSwitch to immediately halt execution of
further actions. Those actions which have already been executed are
diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in
index 2223d04..1fb26e4 100644
--- a/utilities/ovs-vsctl.8.in
+++ b/utilities/ovs-vsctl.8.in
@@ -390,6 +390,32 @@ Deletes the configured failure mode.
.IP "\fBset\-fail\-mode\fR \fIbridge\fR \fBstandalone\fR|\fBsecure\fR"
Sets the configured failure mode.
.
+.SS "Port Normal Mode"
+.PP
+Flow actions can include output to a port called \fBnormal\fR. By default
+Open vSwitch implements a learning layer 2 switch for packets that are
+output using it. Bridges can be switched to another mode which, on Linux
+kernels since 2.6.39, will return packets that use the \fBnormal\fR
+action to the kernel as though unseen by Open vSwitch.
+.PP
+Why is this useful? This allows standard Linux packet processing to
+take over, particularly handling of local IP addresses but also including
+IP routing and firewalling.
+.PP
+This makes it possible to, for example, run
+routing procotol software directly attached to the external ports for
+low-level route distribution and a flow-based layer on top of this. This
+would be an example of hybrid mode with OpenFlow.
+.
+.IP "\fBget\-port\-normal\-mode\fR \fIbridge\fR"
+Prints the configured port normal mode.
+.
+.IP "\fBdel\-port\-normal\-mode\fR \fIbridge\fR"
+Deletes the configured port normal mode.
+.
+.IP "\fBset\-port\-normal\-mode\fR \fIbridge\fR \fBovs\fR|\fBkernel\fR"
+Sets the configured port normal mode.
+.
.SS "Manager Connectivity"
.
These commands manipulate the \fBmanager_options\fR column in the
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index 528b40c..4520238 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -653,6 +653,11 @@ Controller commands:\n\
del-fail-mode BRIDGE delete the fail-mode for BRIDGE\n\
set-fail-mode BRIDGE MODE set the fail-mode for BRIDGE to MODE\n\
\n\
+Port normal commands:\n\
+ get-port-normal-mode BRIDGE print the port-normal-mode for BRIDGE\n\
+ del-port-normal-mode BRIDGE delete the port-normal-mode for BRIDGE\n\
+ set-port-normal-mode BRIDGE MODE set the port-normal-mode for BRIDGE to
MODE\n\
+\n\
Manager commands:\n\
get-manager print the managers\n\
del-manager delete the managers\n\
@@ -1002,6 +1007,7 @@ pre_get_info(struct vsctl_context *ctx)
ovsdb_idl_add_column(ctx->idl, &ovsrec_port_col_interfaces);
ovsdb_idl_add_column(ctx->idl, &ovsrec_interface_col_name);
+ ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_port_normal_mode);
}
static void
@@ -1265,7 +1271,7 @@ cmd_init(struct vsctl_context *ctx OVS_UNUSED)
struct cmd_show_table {
const struct ovsdb_idl_table_class *table;
const struct ovsdb_idl_column *name_column;
- const struct ovsdb_idl_column *columns[3];
+ const struct ovsdb_idl_column *columns[4];
bool recurse;
};
@@ -1281,6 +1287,7 @@ static struct cmd_show_table cmd_show_tables[] = {
&ovsrec_bridge_col_name,
{&ovsrec_bridge_col_controller,
&ovsrec_bridge_col_fail_mode,
+ &ovsrec_bridge_col_port_normal_mode,
&ovsrec_bridge_col_ports},
false},
@@ -1463,6 +1470,7 @@ pre_cmd_emer_reset(struct vsctl_context *ctx)
&ovsrec_interface_col_ingress_policing_rate);
ovsdb_idl_add_column(ctx->idl,
&ovsrec_interface_col_ingress_policing_burst);
+ ovsdb_idl_add_column(ctx->idl, &ovsrec_bridge_col_port_normal_mode);
}
static void
@@ -1495,6 +1503,7 @@ cmd_emer_reset(struct vsctl_context *ctx)
ovsrec_bridge_set_sflow(br, NULL);
ovsrec_bridge_set_ipfix(br, NULL);
ovsrec_bridge_set_flood_vlans(br, NULL, 0);
+ ovsrec_bridge_set_port_normal_mode(br, NULL);
/* We only want to save the "hwaddr" key from other_config. */
hwaddr = smap_get(&br->other_config, "hwaddr");
@@ -2301,6 +2310,55 @@ cmd_set_fail_mode(struct vsctl_context *ctx)
}
static void
+cmd_get_port_normal_mode(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+ const char *port_normal_mode;
+
+ vsctl_context_populate_cache(ctx);
+ br = find_bridge(ctx, ctx->argv[1], true);
+
+ if (br->parent) {
+ br = br->parent;
+ }
+ ovsrec_bridge_verify_port_normal_mode(br->br_cfg);
+
+ port_normal_mode = br->br_cfg->port_normal_mode;
+ if (port_normal_mode && strlen(port_normal_mode)) {
+ ds_put_format(&ctx->output, "%s\n", port_normal_mode);
+ }
+}
+
+static void
+cmd_del_port_normal_mode(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+
+ vsctl_context_populate_cache(ctx);
+
+ br = find_real_bridge(ctx, ctx->argv[1], true);
+
+ ovsrec_bridge_set_port_normal_mode(br->br_cfg, NULL);
+}
+
+static void
+cmd_set_port_normal_mode(struct vsctl_context *ctx)
+{
+ struct vsctl_bridge *br;
+ const char *port_normal_mode = ctx->argv[2];
+
+ vsctl_context_populate_cache(ctx);
+
+ br = find_real_bridge(ctx, ctx->argv[1], true);
+
+ if (strcmp(port_normal_mode, "ovs") && strcmp(port_normal_mode, "kernel"))
{
+ vsctl_fatal("port-normal-mode must be \"ovs\" or \"kernel\"");
+ }
+
+ ovsrec_bridge_set_port_normal_mode(br->br_cfg, port_normal_mode);
+}
+
+static void
verify_managers(const struct ovsrec_open_vswitch *ovs)
{
size_t i;
@@ -4195,6 +4253,11 @@ static const struct vsctl_command_syntax all_commands[]
= {
{"del-fail-mode", 1, 1, pre_get_info, cmd_del_fail_mode, NULL, "", RW},
{"set-fail-mode", 2, 2, pre_get_info, cmd_set_fail_mode, NULL, "", RW},
+ /* Port normal commands. */
+ {"get-port-normal-mode", 1, 1, pre_get_info, cmd_get_port_normal_mode, NULL,
"", RO},
+ {"del-port-normal-mode", 1, 1, pre_get_info, cmd_del_port_normal_mode, NULL,
"", RW},
+ {"set-port-normal-mode", 2, 2, pre_get_info, cmd_set_port_normal_mode, NULL,
"", RW},
+
/* Manager commands. */
{"get-manager", 0, 0, pre_manager, cmd_get_manager, NULL, "", RO},
{"del-manager", 0, 0, pre_manager, cmd_del_manager, NULL, "", RW},
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 6311ff3..b55ae5d 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -2918,6 +2918,7 @@ bridge_configure_remotes(struct bridge *br,
size_t n_controllers;
enum ofproto_fail_mode fail_mode;
+ enum ofproto_port_normal_mode port_normal_mode;
struct ofproto_controller *ocs;
size_t n_ocs;
@@ -3013,6 +3014,13 @@ bridge_configure_remotes(struct bridge *br,
: OFPROTO_FAIL_SECURE;
ofproto_set_fail_mode(br->ofproto, fail_mode);
+ /* Set the port-normal-mode. */
+ port_normal_mode = br->cfg->port_normal_mode
+ && !strcmp(br->cfg->port_normal_mode, "kernel")
+ ? OFPROTO_PORT_NORMAL_MODE_KERNEL
+ : OFPROTO_PORT_NORMAL_MODE_OVS;
+ ofproto_set_port_normal_mode(br->ofproto, port_normal_mode);
+
/* Configure OpenFlow controller connection snooping. */
if (!ofproto_has_snoops(br->ofproto)) {
struct sset snoops;
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 9eb21ed..1c01068 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
{"name": "Open_vSwitch",
- "version": "7.4.0",
- "cksum": "951746691 20389",
+ "version": "7.4.1",
+ "cksum": "1796841126 20564",
"tables": {
"Open_vSwitch": {
"columns": {
@@ -107,7 +107,12 @@
"maxInteger": 254},
"value": {"type": "uuid",
"refTable": "Flow_Table"},
- "min": 0, "max": "unlimited"}}},
+ "min": 0, "max": "unlimited"}},
+ "port_normal_mode": {
+ "type": {"key": {"type": "string",
+ "enum": ["set", ["ovs",
+ "kernel"]]},
+ "min": 0, "max": 1}}},
"indexes": [["name"]]},
"Port": {
"columns": {
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 5fd82fc..a831135 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -591,6 +591,16 @@
connection with a controller. A default value of
<code>OpenFlow10</code> will be used if this column is empty.
</column>
+
+ <column name="port_normal_mode">
+ Behavior when handling a flow whose destination action port is
+ OFPP_NORMAL. Default Open vSwitch behavior, or when this value
+ is <code>ovs</code>, is to act like a learning layer 2 switch.
+ When this value is <code>kernel</code> then matching flows are
+ handed back to the kernel as though Open vSwitch never saw them,
+ allowing normal layer 3 processing by the kernel. This latter
+ behavior might be described as Hybrid mode.
+ </column>
</group>
<group title="Spanning Tree Configuration">
--
1.7.9.5
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev