2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2016-01-18 22:52:48 -08:00
|
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2012-06-21 10:42:20 -07:00
|
|
|
|
#include <arpa/inet.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "odp-util.h"
|
2011-01-23 18:44:44 -08:00
|
|
|
|
#include <errno.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <inttypes.h>
|
2011-11-11 15:22:56 -08:00
|
|
|
|
#include <math.h>
|
2011-11-01 13:25:49 +01:00
|
|
|
|
#include <netinet/in.h>
|
2011-02-01 22:54:11 -08:00
|
|
|
|
#include <netinet/icmp6.h>
|
2015-12-04 12:36:47 -02:00
|
|
|
|
#include <netinet/ip6.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
#include "byte-order.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "coverage.h"
|
2013-12-30 15:58:58 -08:00
|
|
|
|
#include "dpif.h"
|
2016-03-03 10:20:46 -08:00
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "flow.h"
|
2010-12-10 10:40:58 -08:00
|
|
|
|
#include "netlink.h"
|
2016-03-25 14:10:24 -07:00
|
|
|
|
#include "openvswitch/ofpbuf.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "packets.h"
|
2012-05-22 10:32:02 -07:00
|
|
|
|
#include "simap.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "timeval.h"
|
tunnel: Geneve TLV handling support for OpenFlow.
The current support for Geneve in OVS is exactly equivalent to VXLAN:
it is possible to set and match on the VNI but not on any options
contained in the header. This patch enables the use of options.
The goal for Geneve support is not to add support for any particular option
but to allow end users or controllers to specify what they would like to
match. That is, the full range of Geneve's capabilities should be exposed
without modifying the code (the one exception being options that require
per-packet computation in the fast path).
The main issue with supporting Geneve options is how to integrate the
fields into the existing OpenFlow pipeline. All existing operations
are referred to by their NXM/OXM field name - matches, action generation,
arithmetic operations (i.e. tranfer to a register). However, the Geneve
option space is exactly the same as the OXM space, so a direct mapping
is not feasible. Instead, we create a pool of 64 NXMs that are then
dynamically mapped on Geneve option TLVs using OpenFlow. Once mapped,
these fields become first-class citizens in the OpenFlow pipeline.
An example of how to use Geneve options:
ovs-ofctl add-geneve-map br0 {class=0xffff,type=0,len=4}->tun_metadata0
ovs-ofctl add-flow br0 in_port=LOCAL,actions=set_field:0xffffffff->tun_metadata0,1
This will add a 4 bytes option (filled will all 1's) to all packets
coming from the LOCAL port and then send then out to port 1.
A limitation of this patch is that although the option table is specified
for a particular switch over OpenFlow, it is currently global to all
switches. This will be addressed in a future patch.
Based on work originally done by Madhu Challa. Ben Pfaff also significantly
improved the comments.
Signed-off-by: Madhu Challa <challa@noironetworks.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-04-30 18:09:57 -07:00
|
|
|
|
#include "tun-metadata.h"
|
2014-11-11 11:53:47 -08:00
|
|
|
|
#include "unaligned.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "util.h"
|
2015-05-29 17:08:45 -07:00
|
|
|
|
#include "uuid.h"
|
2014-12-15 14:10:38 +01:00
|
|
|
|
#include "openvswitch/vlog.h"
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(odp_util);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
/* The interface between userspace and kernel uses an "OVS_*" prefix.
|
|
|
|
|
* Since this is fairly non-specific for the OVS userspace components,
|
|
|
|
|
* "ODP_*" (Open vSwitch Datapath) is used as the prefix for
|
|
|
|
|
* interactions with the datapath.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
/* The set of characters that may separate one action or one key attribute
|
|
|
|
|
* from another. */
|
|
|
|
|
static const char *delimiters = ", \t\r\n";
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
static const char *delimiters_end = ", \t\r\n)";
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
struct attr_len_tbl {
|
|
|
|
|
int len;
|
|
|
|
|
const struct attr_len_tbl *next;
|
|
|
|
|
int next_max;
|
|
|
|
|
};
|
|
|
|
|
#define ATTR_LEN_INVALID -1
|
|
|
|
|
#define ATTR_LEN_VARIABLE -2
|
|
|
|
|
#define ATTR_LEN_NESTED -3
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static int parse_odp_key_mask_attr(const char *, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *, struct ofpbuf *);
|
|
|
|
|
static void format_odp_key_attr(const struct nlattr *a,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct nlattr *ma,
|
|
|
|
|
const struct hmap *portno_names, struct ds *ds,
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool verbose);
|
2011-11-07 09:14:46 -08:00
|
|
|
|
|
2015-06-22 14:23:37 -07:00
|
|
|
|
struct geneve_scan {
|
|
|
|
|
struct geneve_opt d[63];
|
|
|
|
|
int len;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int scan_geneve(const char *s, struct geneve_scan *key,
|
|
|
|
|
struct geneve_scan *mask);
|
|
|
|
|
static void format_geneve_opts(const struct geneve_opt *opt,
|
|
|
|
|
const struct geneve_opt *mask, int opts_len,
|
|
|
|
|
struct ds *, bool verbose);
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[],
|
|
|
|
|
int max, struct ofpbuf *,
|
|
|
|
|
const struct nlattr *key);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
static void format_u128(struct ds *ds, const ovs_u128 *value,
|
|
|
|
|
const ovs_u128 *mask, bool verbose);
|
|
|
|
|
static int scan_u128(const char *s, ovs_u128 *value, ovs_u128 *mask);
|
|
|
|
|
|
2017-01-31 19:23:27 -08:00
|
|
|
|
static int parse_odp_action(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *actions);
|
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
/* Returns one the following for the action with the given OVS_ACTION_ATTR_*
|
|
|
|
|
* 'type':
|
|
|
|
|
*
|
|
|
|
|
* - For an action whose argument has a fixed length, returned that
|
|
|
|
|
* nonnegative length in bytes.
|
|
|
|
|
*
|
2015-05-20 11:57:35 -07:00
|
|
|
|
* - For an action with a variable-length argument, returns ATTR_LEN_VARIABLE.
|
2011-10-12 16:24:54 -07:00
|
|
|
|
*
|
2015-05-20 11:57:35 -07:00
|
|
|
|
* - For an invalid 'type', returns ATTR_LEN_INVALID. */
|
2011-10-21 14:38:54 -07:00
|
|
|
|
static int
|
2010-12-10 10:40:58 -08:00
|
|
|
|
odp_action_len(uint16_t type)
|
|
|
|
|
{
|
2011-08-18 10:35:40 -07:00
|
|
|
|
if (type > OVS_ACTION_ATTR_MAX) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-05 09:59:51 -07:00
|
|
|
|
switch ((enum ovs_action_attr) type) {
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
|
ofp-actions: Add truncate action.
The patch adds a new action to support packet truncation. The new action
is formatted as 'output(port=n,max_len=m)', as output to port n, with
packet size being MIN(original_size, m).
One use case is to enable port mirroring to send smaller packets to the
destination port so that only useful packet information is mirrored/copied,
saving some performance overhead of copying entire packet payload. Example
use case is below as well as shown in the testcases:
- Output to port 1 with max_len 100 bytes.
- The output packet size on port 1 will be MIN(original_packet_size, 100).
# ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)'
- The scope of max_len is limited to output action itself. The following
packet size of output:1 and output:2 will be intact.
# ovs-ofctl add-flow br0 \
'actions=output(port=1,max_len=100),output:1,output:2'
- The Datapath actions shows:
# Datapath actions: trunc(100),1,1,2
Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/140037134
Signed-off-by: William Tu <u9012063@gmail.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
2016-06-24 07:42:30 -07:00
|
|
|
|
case OVS_ACTION_ATTR_TRUNC: return sizeof(struct ovs_action_trunc);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
|
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN: return 0;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
|
|
|
|
|
case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
|
2014-04-08 18:42:39 -07:00
|
|
|
|
case OVS_ACTION_ATTR_RECIRC: return sizeof(uint32_t);
|
|
|
|
|
case OVS_ACTION_ATTR_HASH: return sizeof(struct ovs_action_hash);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET: return ATTR_LEN_VARIABLE;
|
|
|
|
|
case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE;
|
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE;
|
dpif-netdev: Add clone action
Add support for userspace datapath clone action. The clone action
provides an action envelope to enclose an action list.
For example, with actions A, B, C and D, and an action list:
A, clone(B, C), D
The clone action will ensure that:
- D will see the same packet, and any meta states, such as flow, as
action B.
- D will be executed regardless whether B, or C drops a packet. They
can only drop a clone.
- When B drops a packet, clone will skip all remaining actions
within the clone envelope. This feature is useful when we add
meter action later: The meter action can be implemented as a
simple action without its own envolop (unlike the sample action).
When necessary, the flow translation layer can enclose a meter action
in clone.
The clone action is very similar with the OpenFlow clone action.
This is by design to simplify vswitchd flow translation logic.
Without datapath clone, vswitchd simulate the effect by inserting
datapath actions to "undo" clone actions. The above flow will be
translated into A, B, C, -C, -B, D.
However, there are two issues:
- The resulting datapath action list may be longer without using
clone.
- Some actions, such as NAT may not be possible to reverse.
This patch implements clone() simply with packet copy. The performance
can be improved with later patches, for example, to delay or avoid
packet copy if possible. It seems datapath should have enough context
to carry out such optimization without the userspace context.
Signed-off-by: Andy Zhou <azhou@ovn.org>
Acked-by: Jarno Rajahalme <jarno@ovn.org>
2017-01-10 18:13:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
2015-05-20 11:57:35 -07:00
|
|
|
|
return ATTR_LEN_INVALID;
|
2010-12-10 10:40:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
return ATTR_LEN_INVALID;
|
2010-12-10 10:40:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-04-15 15:40:21 -07:00
|
|
|
|
/* Returns a string form of 'attr'. The return value is either a statically
|
|
|
|
|
* allocated constant string or the 'bufsize'-byte buffer 'namebuf'. 'bufsize'
|
|
|
|
|
* should be at least OVS_KEY_ATTR_BUFSIZE. */
|
|
|
|
|
enum { OVS_KEY_ATTR_BUFSIZE = 3 + INT_STRLEN(unsigned int) + 1 };
|
2011-11-07 13:13:36 -08:00
|
|
|
|
static const char *
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
|
2011-11-07 13:13:36 -08:00
|
|
|
|
{
|
|
|
|
|
switch (attr) {
|
|
|
|
|
case OVS_KEY_ATTR_UNSPEC: return "unspec";
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_ENCAP: return "encap";
|
2012-11-29 14:36:49 -08:00
|
|
|
|
case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
|
2012-11-13 19:19:36 +02:00
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_STATE: return "ct_state";
|
|
|
|
|
case OVS_KEY_ATTR_CT_ZONE: return "ct_zone";
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_MARK: return "ct_mark";
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_LABELS: return "ct_label";
|
2013-01-18 18:10:59 -08:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: return "tunnel";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_IN_PORT: return "in_port";
|
|
|
|
|
case OVS_KEY_ATTR_ETHERNET: return "eth";
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_VLAN: return "vlan";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE: return "eth_type";
|
|
|
|
|
case OVS_KEY_ATTR_IPV4: return "ipv4";
|
|
|
|
|
case OVS_KEY_ATTR_IPV6: return "ipv6";
|
|
|
|
|
case OVS_KEY_ATTR_TCP: return "tcp";
|
2013-10-28 13:54:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_UDP: return "udp";
|
2013-08-22 20:24:44 +12:00
|
|
|
|
case OVS_KEY_ATTR_SCTP: return "sctp";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_ICMP: return "icmp";
|
|
|
|
|
case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
|
|
|
|
|
case OVS_KEY_ATTR_ARP: return "arp";
|
|
|
|
|
case OVS_KEY_ATTR_ND: return "nd";
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_KEY_ATTR_MPLS: return "mpls";
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
|
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
|
|
|
|
default:
|
2013-04-15 15:40:21 -07:00
|
|
|
|
snprintf(namebuf, bufsize, "key%u", (unsigned int) attr);
|
|
|
|
|
return namebuf;
|
2011-11-07 13:13:36 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
static void
|
|
|
|
|
format_generic_odp_action(struct ds *ds, const struct nlattr *a)
|
|
|
|
|
{
|
2010-12-23 14:21:01 -08:00
|
|
|
|
size_t len = nl_attr_get_size(a);
|
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
ds_put_format(ds, "action%"PRId16, nl_attr_type(a));
|
2010-12-23 14:21:01 -08:00
|
|
|
|
if (len) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
const uint8_t *unspec;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
unspec = nl_attr_get(a);
|
2010-12-23 14:21:01 -08:00
|
|
|
|
for (i = 0; i < len; i++) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
ds_put_char(ds, i ? ' ': '(');
|
|
|
|
|
ds_put_format(ds, "%02x", unspec[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-28 10:43:07 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
static const struct nl_policy ovs_sample_policy[] = {
|
2014-03-19 17:34:55 -07:00
|
|
|
|
[OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 },
|
|
|
|
|
[OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED }
|
2011-09-28 10:43:07 -07:00
|
|
|
|
};
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)];
|
|
|
|
|
double percentage;
|
|
|
|
|
const struct nlattr *nla_acts;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "sample");
|
|
|
|
|
|
2011-10-04 12:58:25 -07:00
|
|
|
|
if (!nl_parse_nested(attr, ovs_sample_policy, a, ARRAY_SIZE(a))) {
|
2011-09-28 10:43:07 -07:00
|
|
|
|
ds_put_cstr(ds, "(error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
percentage = (100.0 * nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY])) /
|
|
|
|
|
UINT32_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "(sample=%.1f%%,", percentage);
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "actions(");
|
|
|
|
|
nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]);
|
|
|
|
|
len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]);
|
|
|
|
|
format_odp_actions(ds, nla_acts, len);
|
|
|
|
|
ds_put_format(ds, "))");
|
|
|
|
|
}
|
|
|
|
|
|
dpif-netdev: Add clone action
Add support for userspace datapath clone action. The clone action
provides an action envelope to enclose an action list.
For example, with actions A, B, C and D, and an action list:
A, clone(B, C), D
The clone action will ensure that:
- D will see the same packet, and any meta states, such as flow, as
action B.
- D will be executed regardless whether B, or C drops a packet. They
can only drop a clone.
- When B drops a packet, clone will skip all remaining actions
within the clone envelope. This feature is useful when we add
meter action later: The meter action can be implemented as a
simple action without its own envolop (unlike the sample action).
When necessary, the flow translation layer can enclose a meter action
in clone.
The clone action is very similar with the OpenFlow clone action.
This is by design to simplify vswitchd flow translation logic.
Without datapath clone, vswitchd simulate the effect by inserting
datapath actions to "undo" clone actions. The above flow will be
translated into A, B, C, -C, -B, D.
However, there are two issues:
- The resulting datapath action list may be longer without using
clone.
- Some actions, such as NAT may not be possible to reverse.
This patch implements clone() simply with packet copy. The performance
can be improved with later patches, for example, to delay or avoid
packet copy if possible. It seems datapath should have enough context
to carry out such optimization without the userspace context.
Signed-off-by: Andy Zhou <azhou@ovn.org>
Acked-by: Jarno Rajahalme <jarno@ovn.org>
2017-01-10 18:13:47 -08:00
|
|
|
|
static void
|
|
|
|
|
format_odp_clone_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
const struct nlattr *nla_acts = nl_attr_get(attr);
|
|
|
|
|
int len = nl_attr_get_size(attr);
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "clone");
|
|
|
|
|
ds_put_format(ds, "(");
|
|
|
|
|
format_odp_actions(ds, nla_acts, len);
|
|
|
|
|
ds_put_format(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:52:36 -07:00
|
|
|
|
static const char *
|
2013-09-20 12:54:51 -07:00
|
|
|
|
slow_path_reason_to_string(uint32_t reason)
|
2012-05-04 14:52:36 -07:00
|
|
|
|
{
|
2013-09-20 12:54:51 -07:00
|
|
|
|
switch ((enum slow_path_reason) reason) {
|
|
|
|
|
#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return STRING;
|
|
|
|
|
SLOW_PATH_REASONS
|
|
|
|
|
#undef SPR
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
2013-09-20 12:54:51 -07:00
|
|
|
|
|
|
|
|
|
return NULL;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-20 12:54:51 -07:00
|
|
|
|
const char *
|
|
|
|
|
slow_path_reason_to_explanation(enum slow_path_reason reason)
|
2013-05-28 11:43:43 -07:00
|
|
|
|
{
|
2013-09-20 12:54:51 -07:00
|
|
|
|
switch (reason) {
|
|
|
|
|
#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return EXPLANATION;
|
|
|
|
|
SLOW_PATH_REASONS
|
|
|
|
|
#undef SPR
|
2013-05-28 11:43:43 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-20 12:54:51 -07:00
|
|
|
|
return "<unknown>";
|
2013-05-28 11:43:43 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-14 21:10:22 -08:00
|
|
|
|
static int
|
2015-07-11 20:48:29 -07:00
|
|
|
|
parse_odp_flags(const char *s, const char *(*bit_to_string)(uint32_t),
|
|
|
|
|
uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
|
2012-11-14 21:10:22 -08:00
|
|
|
|
{
|
2015-07-11 20:48:29 -07:00
|
|
|
|
return parse_flags(s, bit_to_string, ')', NULL, NULL,
|
|
|
|
|
res_flags, allowed, res_mask);
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
static const struct nl_policy ovs_userspace_policy[] = {
|
2014-03-19 17:34:55 -07:00
|
|
|
|
[OVS_USERSPACE_ATTR_PID] = { .type = NL_A_U32 },
|
|
|
|
|
[OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_UNSPEC,
|
|
|
|
|
.optional = true },
|
2014-08-17 20:19:36 -07:00
|
|
|
|
[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32,
|
|
|
|
|
.optional = true },
|
2015-07-17 21:37:02 -07:00
|
|
|
|
[OVS_USERSPACE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC,
|
|
|
|
|
.optional = true },
|
2011-10-12 16:24:54 -07:00
|
|
|
|
};
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
|
2013-02-15 16:48:32 -08:00
|
|
|
|
const struct nlattr *userdata_attr;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
const struct nlattr *tunnel_out_port_attr;
|
2011-10-12 16:24:54 -07:00
|
|
|
|
|
|
|
|
|
if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) {
|
|
|
|
|
ds_put_cstr(ds, "userspace(error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "userspace(pid=%"PRIu32,
|
|
|
|
|
nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
|
|
|
|
|
|
2013-02-15 16:48:32 -08:00
|
|
|
|
userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
|
2013-04-22 10:01:14 -07:00
|
|
|
|
|
|
|
|
|
if (userdata_attr) {
|
|
|
|
|
const uint8_t *userdata = nl_attr_get(userdata_attr);
|
|
|
|
|
size_t userdata_len = nl_attr_get_size(userdata_attr);
|
|
|
|
|
bool userdata_unspec = true;
|
2012-05-04 14:56:40 -07:00
|
|
|
|
union user_action_cookie cookie;
|
2011-10-12 16:24:54 -07:00
|
|
|
|
|
2013-04-22 10:01:14 -07:00
|
|
|
|
if (userdata_len >= sizeof cookie.type
|
|
|
|
|
&& userdata_len <= sizeof cookie) {
|
|
|
|
|
|
|
|
|
|
memset(&cookie, 0, sizeof cookie);
|
|
|
|
|
memcpy(&cookie, userdata, userdata_len);
|
|
|
|
|
|
|
|
|
|
userdata_unspec = false;
|
|
|
|
|
|
|
|
|
|
if (userdata_len == sizeof cookie.sflow
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_SFLOW) {
|
|
|
|
|
ds_put_format(ds, ",sFlow("
|
|
|
|
|
"vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
|
|
|
|
|
vlan_tci_to_vid(cookie.sflow.vlan_tci),
|
|
|
|
|
vlan_tci_to_pcp(cookie.sflow.vlan_tci),
|
|
|
|
|
cookie.sflow.output);
|
|
|
|
|
} else if (userdata_len == sizeof cookie.slow_path
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
|
2013-09-20 12:54:51 -07:00
|
|
|
|
ds_put_cstr(ds, ",slow_path(");
|
|
|
|
|
format_flags(ds, slow_path_reason_to_string,
|
|
|
|
|
cookie.slow_path.reason, ',');
|
|
|
|
|
ds_put_format(ds, ")");
|
2013-04-22 10:01:14 -07:00
|
|
|
|
} else if (userdata_len == sizeof cookie.flow_sample
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
|
|
|
|
|
ds_put_format(ds, ",flow_sample(probability=%"PRIu16
|
|
|
|
|
",collector_set_id=%"PRIu32
|
|
|
|
|
",obs_domain_id=%"PRIu32
|
ipfix: Support tunnel information for Flow IPFIX.
Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
1) Create a new record in Flow_Sample_Collector_Set table:
'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
2) Add IPFIX configuration which is referred by corresponding
row in Flow_Sample_Collector_Set table:
'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
cache_active_timeout=60 cache_max_flows=13'
3) Add sample action to the flows:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,
obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added and with NXAST_SAMPLE2 action
in this patch, the step 3 should be configured like below:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample action.
When flow sample action upcall happens, tunnel information will be retrieved from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.
This patch mainly do three tasks:
1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
tunnel information. NXAST_SAMPLE2 action has a new added field
'sampling_port'.
2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
exporting tunnel information.
3) If 'sampling_port' is equal to output port and output port is a tunnel
port, the translation of OpenFlow "sample" action should first emit
set(tunnel(...)), then the sample action itself. It makes sure the
egress tunnel information can be sampled.
4) Add a test of flow-based IPFIX for tunnel set.
How to test flow-based IPFIX:
1) Setup a test environment with two Linux host with Docker supported
2) Create a Docker container and a GRE tunnel port on each host
3) Use ovs-docker to add the container on the bridge
4) Listen on port 4739 on the collector machine and use wireshark to filter
'cflow' packets.
5) Configure flow-based IPFIX:
- 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
- 'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
other_config:enable-tunnel-sampling=true'
- 'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
Note: The in-port is container port. The output port and sampling_port
are both open flow port and the output port is a GRE tunnel port.
6) Ping from the container whose host enabled flow-based IPFIX.
7) Get the IPFIX template pakcets and IPFIX information packets.
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-14 16:53:34 +08:00
|
|
|
|
",obs_point_id=%"PRIu32
|
2016-11-23 23:15:19 -08:00
|
|
|
|
",output_port=%"PRIu32,
|
2013-04-22 10:01:14 -07:00
|
|
|
|
cookie.flow_sample.probability,
|
|
|
|
|
cookie.flow_sample.collector_set_id,
|
|
|
|
|
cookie.flow_sample.obs_domain_id,
|
ipfix: Support tunnel information for Flow IPFIX.
Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
1) Create a new record in Flow_Sample_Collector_Set table:
'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
2) Add IPFIX configuration which is referred by corresponding
row in Flow_Sample_Collector_Set table:
'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
cache_active_timeout=60 cache_max_flows=13'
3) Add sample action to the flows:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,
obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added and with NXAST_SAMPLE2 action
in this patch, the step 3 should be configured like below:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample action.
When flow sample action upcall happens, tunnel information will be retrieved from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.
This patch mainly do three tasks:
1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
tunnel information. NXAST_SAMPLE2 action has a new added field
'sampling_port'.
2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
exporting tunnel information.
3) If 'sampling_port' is equal to output port and output port is a tunnel
port, the translation of OpenFlow "sample" action should first emit
set(tunnel(...)), then the sample action itself. It makes sure the
egress tunnel information can be sampled.
4) Add a test of flow-based IPFIX for tunnel set.
How to test flow-based IPFIX:
1) Setup a test environment with two Linux host with Docker supported
2) Create a Docker container and a GRE tunnel port on each host
3) Use ovs-docker to add the container on the bridge
4) Listen on port 4739 on the collector machine and use wireshark to filter
'cflow' packets.
5) Configure flow-based IPFIX:
- 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
- 'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
other_config:enable-tunnel-sampling=true'
- 'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
Note: The in-port is container port. The output port and sampling_port
are both open flow port and the output port is a GRE tunnel port.
6) Ping from the container whose host enabled flow-based IPFIX.
7) Get the IPFIX template pakcets and IPFIX information packets.
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-14 16:53:34 +08:00
|
|
|
|
cookie.flow_sample.obs_point_id,
|
|
|
|
|
cookie.flow_sample.output_odp_port);
|
2016-11-23 23:15:19 -08:00
|
|
|
|
if (cookie.flow_sample.direction == NX_ACTION_SAMPLE_INGRESS) {
|
|
|
|
|
ds_put_cstr(ds, ",ingress");
|
|
|
|
|
} else if (cookie.flow_sample.direction == NX_ACTION_SAMPLE_EGRESS) {
|
|
|
|
|
ds_put_cstr(ds, ",egress");
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2013-11-11 13:32:23 -08:00
|
|
|
|
} else if (userdata_len >= sizeof cookie.ipfix
|
2013-04-22 10:01:14 -07:00
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_IPFIX) {
|
2014-08-17 20:19:36 -07:00
|
|
|
|
ds_put_format(ds, ",ipfix(output_port=%"PRIu32")",
|
|
|
|
|
cookie.ipfix.output_odp_port);
|
2013-04-22 10:01:14 -07:00
|
|
|
|
} else {
|
|
|
|
|
userdata_unspec = true;
|
|
|
|
|
}
|
2011-10-12 16:24:54 -07:00
|
|
|
|
}
|
2013-02-15 16:48:32 -08:00
|
|
|
|
|
2013-04-22 10:01:14 -07:00
|
|
|
|
if (userdata_unspec) {
|
|
|
|
|
size_t i;
|
|
|
|
|
ds_put_format(ds, ",userdata(");
|
|
|
|
|
for (i = 0; i < userdata_len; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", userdata[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2013-02-15 16:48:32 -08:00
|
|
|
|
}
|
2011-10-12 16:24:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
|
if (a[OVS_USERSPACE_ATTR_ACTIONS]) {
|
|
|
|
|
ds_put_cstr(ds, ",actions");
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT];
|
|
|
|
|
if (tunnel_out_port_attr) {
|
|
|
|
|
ds_put_format(ds, ",tunnel_out_port=%"PRIu32,
|
|
|
|
|
nl_attr_get_u32(tunnel_out_port_attr));
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 17:19:41 -08:00
|
|
|
|
static void
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(struct ds *ds, ovs_be16 tci, ovs_be16 mask, bool verbose)
|
2011-11-14 17:19:41 -08:00
|
|
|
|
{
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (verbose || vlan_tci_to_vid(tci) || vlan_tci_to_vid(mask)) {
|
|
|
|
|
ds_put_format(ds, "vid=%"PRIu16, vlan_tci_to_vid(tci));
|
|
|
|
|
if (vlan_tci_to_vid(mask) != VLAN_VID_MASK) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/0x%"PRIx16, vlan_tci_to_vid(mask));
|
|
|
|
|
};
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
if (verbose || vlan_tci_to_pcp(tci) || vlan_tci_to_pcp(mask)) {
|
|
|
|
|
ds_put_format(ds, "pcp=%d", vlan_tci_to_pcp(tci));
|
|
|
|
|
if (vlan_tci_to_pcp(mask) != (VLAN_PCP_MASK >> VLAN_PCP_SHIFT)) {
|
|
|
|
|
ds_put_format(ds, "/0x%x", vlan_tci_to_pcp(mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
if (!(tci & htons(VLAN_CFI))) {
|
|
|
|
|
ds_put_cstr(ds, "cfi=0");
|
|
|
|
|
ds_put_char(ds, ',');
|
2011-11-14 17:19:41 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
ds_chomp(ds, ',');
|
2011-11-14 17:19:41 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 16:22:07 +09:00
|
|
|
|
static void
|
|
|
|
|
format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
|
|
|
|
|
mpls_lse_to_label(mpls_lse),
|
|
|
|
|
mpls_lse_to_tc(mpls_lse),
|
|
|
|
|
mpls_lse_to_ttl(mpls_lse),
|
|
|
|
|
mpls_lse_to_bos(mpls_lse));
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static void
|
|
|
|
|
format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
const struct ovs_key_mpls *mpls_mask, int n)
|
2013-06-19 07:15:10 +00:00
|
|
|
|
{
|
2016-02-24 16:10:42 -08:00
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
|
ovs_be32 key = mpls_key[i].mpls_lse;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (mpls_mask == NULL) {
|
|
|
|
|
format_mpls_lse(ds, key);
|
|
|
|
|
} else {
|
2016-02-24 16:10:42 -08:00
|
|
|
|
ovs_be32 mask = mpls_mask[i].mpls_lse;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
|
|
|
|
|
mpls_lse_to_label(key), mpls_lse_to_label(mask),
|
|
|
|
|
mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
|
|
|
|
|
mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
|
|
|
|
|
mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
|
|
|
|
|
}
|
2016-02-24 16:10:42 -08:00
|
|
|
|
ds_put_char(ds, ',');
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2016-02-24 16:10:42 -08:00
|
|
|
|
ds_chomp(ds, ',');
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 15:36:03 -08:00
|
|
|
|
static void
|
2014-04-08 18:42:39 -07:00
|
|
|
|
format_odp_recirc_action(struct ds *ds, uint32_t recirc_id)
|
2014-03-04 15:36:03 -08:00
|
|
|
|
{
|
2015-05-20 13:46:01 -07:00
|
|
|
|
ds_put_format(ds, "recirc(%#"PRIx32")", recirc_id);
|
2014-04-08 18:42:39 -07:00
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-04-08 18:42:39 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_hash_action(struct ds *ds, const struct ovs_action_hash *hash_act)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "hash(");
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-04-08 18:42:39 -07:00
|
|
|
|
if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
|
2014-04-17 20:06:58 -07:00
|
|
|
|
ds_put_format(ds, "hash_l4(%"PRIu32")", hash_act->hash_basis);
|
2014-04-08 18:42:39 -07:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "Unknown hash algorithm(%"PRIu32")",
|
|
|
|
|
hash_act->hash_alg);
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-03-26 12:07:02 -07:00
|
|
|
|
static const void *
|
2015-12-04 12:36:47 -02:00
|
|
|
|
format_udp_tnl_push_header(struct ds *ds, const struct udp_header *udp)
|
2015-03-26 12:07:02 -07:00
|
|
|
|
{
|
2015-03-26 14:27:19 -07:00
|
|
|
|
ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16",csum=0x%"PRIx16"),",
|
|
|
|
|
ntohs(udp->udp_src), ntohs(udp->udp_dst),
|
|
|
|
|
ntohs(udp->udp_csum));
|
2015-03-26 12:07:02 -07:00
|
|
|
|
|
|
|
|
|
return udp + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
static void
|
|
|
|
|
format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
const struct eth_header *eth;
|
|
|
|
|
const void *l3;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
const void *l4;
|
|
|
|
|
const struct udp_header *udp;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
eth = (const struct eth_header *)data->header;
|
|
|
|
|
|
|
|
|
|
l3 = eth + 1;
|
|
|
|
|
|
|
|
|
|
/* Ethernet */
|
|
|
|
|
ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
|
|
|
|
|
data->header_len, data->tnl_type);
|
|
|
|
|
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
|
|
|
|
|
ds_put_format(ds, ",src=");
|
|
|
|
|
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
|
|
|
|
|
ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
|
|
|
|
|
|
2015-12-04 12:36:47 -02:00
|
|
|
|
if (eth->eth_type == htons(ETH_TYPE_IP)) {
|
|
|
|
|
/* IPv4 */
|
|
|
|
|
const struct ip_header *ip;
|
|
|
|
|
ip = (const struct ip_header *) l3;
|
|
|
|
|
ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
|
|
|
|
|
",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
|
|
|
|
|
IP_ARGS(get_16aligned_be32(&ip->ip_src)),
|
|
|
|
|
IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
|
|
|
|
|
ip->ip_proto, ip->ip_tos,
|
|
|
|
|
ip->ip_ttl,
|
2016-02-01 11:31:54 -08:00
|
|
|
|
ntohs(ip->ip_frag_off));
|
2015-12-04 12:36:47 -02:00
|
|
|
|
l4 = (ip + 1);
|
|
|
|
|
} else {
|
|
|
|
|
const struct ip6_hdr *ip6;
|
|
|
|
|
ip6 = (const struct ip6_hdr *) l3;
|
|
|
|
|
ds_put_format(ds, "ipv6(src=");
|
|
|
|
|
ipv6_format_addr(&ip6->ip6_src, ds);
|
|
|
|
|
ds_put_format(ds, ",dst=");
|
|
|
|
|
ipv6_format_addr(&ip6->ip6_dst, ds);
|
|
|
|
|
ds_put_format(ds, ",label=%i,proto=%"PRIu8",tclass=0x%"PRIx8
|
|
|
|
|
",hlimit=%"PRIu8"),",
|
|
|
|
|
ntohl(ip6->ip6_flow) & IPV6_LABEL_MASK, ip6->ip6_nxt,
|
|
|
|
|
(ntohl(ip6->ip6_flow) >> 20) & 0xff, ip6->ip6_hlim);
|
|
|
|
|
l4 = (ip6 + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
udp = (const struct udp_header *) l4;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
|
|
|
|
|
const struct vxlanhdr *vxh;
|
|
|
|
|
|
2015-12-04 12:36:47 -02:00
|
|
|
|
vxh = format_udp_tnl_push_header(ds, udp);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
|
|
|
|
|
ntohl(get_16aligned_be32(&vxh->vx_flags)),
|
2015-03-27 17:51:35 -07:00
|
|
|
|
ntohl(get_16aligned_be32(&vxh->vx_vni)) >> 8);
|
2015-03-26 13:51:06 -07:00
|
|
|
|
} else if (data->tnl_type == OVS_VPORT_TYPE_GENEVE) {
|
|
|
|
|
const struct genevehdr *gnh;
|
|
|
|
|
|
2015-12-04 12:36:47 -02:00
|
|
|
|
gnh = format_udp_tnl_push_header(ds, udp);
|
2015-03-26 13:51:06 -07:00
|
|
|
|
|
2015-06-22 14:23:37 -07:00
|
|
|
|
ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
|
2015-03-26 13:51:06 -07:00
|
|
|
|
gnh->oam ? "oam," : "",
|
2015-06-22 14:23:37 -07:00
|
|
|
|
gnh->critical ? "crit," : "",
|
2015-03-26 13:51:06 -07:00
|
|
|
|
ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
|
2015-06-22 14:23:37 -07:00
|
|
|
|
|
|
|
|
|
if (gnh->opt_len) {
|
|
|
|
|
ds_put_cstr(ds, ",options(");
|
|
|
|
|
format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
|
|
|
|
|
ds, false);
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_char(ds, ')');
|
2014-11-11 11:53:47 -08:00
|
|
|
|
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
|
|
|
|
|
const struct gre_base_hdr *greh;
|
|
|
|
|
ovs_16aligned_be32 *options;
|
|
|
|
|
|
|
|
|
|
greh = (const struct gre_base_hdr *) l4;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
|
2015-04-10 11:08:10 -03:00
|
|
|
|
ntohs(greh->flags), ntohs(greh->protocol));
|
2014-11-11 11:53:47 -08:00
|
|
|
|
options = (ovs_16aligned_be32 *)(greh + 1);
|
|
|
|
|
if (greh->flags & htons(GRE_CSUM)) {
|
2015-03-26 17:09:38 -07:00
|
|
|
|
ds_put_format(ds, ",csum=0x%"PRIx16, ntohs(*((ovs_be16 *)options)));
|
2014-11-11 11:53:47 -08:00
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_KEY)) {
|
|
|
|
|
ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_SEQ)) {
|
|
|
|
|
ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_tnl *data;
|
|
|
|
|
|
|
|
|
|
data = (struct ovs_action_push_tnl *) nl_attr_get(attr);
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "tnl_push(tnl_port(%"PRIu32"),", data->tnl_port);
|
|
|
|
|
format_odp_tnl_push_header(ds, data);
|
|
|
|
|
ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-24 15:47:56 -08:00
|
|
|
|
static const struct nl_policy ovs_nat_policy[] = {
|
|
|
|
|
[OVS_NAT_ATTR_SRC] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_DST] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_IP_MIN] = { .type = NL_A_UNSPEC, .optional = true,
|
|
|
|
|
.min_len = sizeof(struct in_addr),
|
|
|
|
|
.max_len = sizeof(struct in6_addr)},
|
|
|
|
|
[OVS_NAT_ATTR_IP_MAX] = { .type = NL_A_UNSPEC, .optional = true,
|
|
|
|
|
.min_len = sizeof(struct in_addr),
|
|
|
|
|
.max_len = sizeof(struct in6_addr)},
|
|
|
|
|
[OVS_NAT_ATTR_PROTO_MIN] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_PROTO_MAX] = { .type = NL_A_U16, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_PERSISTENT] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_PROTO_HASH] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
[OVS_NAT_ATTR_PROTO_RANDOM] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_odp_ct_nat(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_nat_policy)];
|
|
|
|
|
size_t addr_len;
|
|
|
|
|
ovs_be32 ip_min, ip_max;
|
|
|
|
|
struct in6_addr ip6_min, ip6_max;
|
|
|
|
|
uint16_t proto_min, proto_max;
|
|
|
|
|
|
|
|
|
|
if (!nl_parse_nested(attr, ovs_nat_policy, a, ARRAY_SIZE(a))) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: nl_parse_nested() failed.)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* If no type, then nothing else either. */
|
|
|
|
|
if (!(a[OVS_NAT_ATTR_SRC] || a[OVS_NAT_ATTR_DST])
|
|
|
|
|
&& (a[OVS_NAT_ATTR_IP_MIN] || a[OVS_NAT_ATTR_IP_MAX]
|
|
|
|
|
|| a[OVS_NAT_ATTR_PROTO_MIN] || a[OVS_NAT_ATTR_PROTO_MAX]
|
|
|
|
|
|| a[OVS_NAT_ATTR_PERSISTENT] || a[OVS_NAT_ATTR_PROTO_HASH]
|
|
|
|
|
|| a[OVS_NAT_ATTR_PROTO_RANDOM])) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: options allowed only with \"src\" or \"dst\")");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* Both SNAT & DNAT may not be specified. */
|
|
|
|
|
if (a[OVS_NAT_ATTR_SRC] && a[OVS_NAT_ATTR_DST]) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: Only one of \"src\" or \"dst\" may be present.)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* proto may not appear without ip. */
|
|
|
|
|
if (!a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_PROTO_MIN]) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: proto but no IP.)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* MAX may not appear without MIN. */
|
|
|
|
|
if ((!a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_IP_MAX])
|
|
|
|
|
|| (!a[OVS_NAT_ATTR_PROTO_MIN] && a[OVS_NAT_ATTR_PROTO_MAX])) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: range max without min.)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* Address sizes must match. */
|
|
|
|
|
if ((a[OVS_NAT_ATTR_IP_MIN]
|
|
|
|
|
&& (nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) != sizeof(ovs_be32) &&
|
|
|
|
|
nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) != sizeof(struct in6_addr)))
|
|
|
|
|
|| (a[OVS_NAT_ATTR_IP_MIN] && a[OVS_NAT_ATTR_IP_MAX]
|
|
|
|
|
&& (nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN])
|
|
|
|
|
!= nl_attr_get_size(a[OVS_NAT_ATTR_IP_MAX])))) {
|
|
|
|
|
ds_put_cstr(ds, "nat(error: IP address sizes do not match)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr_len = a[OVS_NAT_ATTR_IP_MIN]
|
|
|
|
|
? nl_attr_get_size(a[OVS_NAT_ATTR_IP_MIN]) : 0;
|
|
|
|
|
ip_min = addr_len == sizeof(ovs_be32) && a[OVS_NAT_ATTR_IP_MIN]
|
|
|
|
|
? nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MIN]) : 0;
|
|
|
|
|
ip_max = addr_len == sizeof(ovs_be32) && a[OVS_NAT_ATTR_IP_MAX]
|
|
|
|
|
? nl_attr_get_be32(a[OVS_NAT_ATTR_IP_MAX]) : 0;
|
|
|
|
|
if (addr_len == sizeof ip6_min) {
|
|
|
|
|
ip6_min = a[OVS_NAT_ATTR_IP_MIN]
|
|
|
|
|
? *(struct in6_addr *)nl_attr_get(a[OVS_NAT_ATTR_IP_MIN])
|
|
|
|
|
: in6addr_any;
|
|
|
|
|
ip6_max = a[OVS_NAT_ATTR_IP_MAX]
|
|
|
|
|
? *(struct in6_addr *)nl_attr_get(a[OVS_NAT_ATTR_IP_MAX])
|
|
|
|
|
: in6addr_any;
|
|
|
|
|
}
|
|
|
|
|
proto_min = a[OVS_NAT_ATTR_PROTO_MIN]
|
|
|
|
|
? nl_attr_get_u16(a[OVS_NAT_ATTR_PROTO_MIN]) : 0;
|
|
|
|
|
proto_max = a[OVS_NAT_ATTR_PROTO_MAX]
|
|
|
|
|
? nl_attr_get_u16(a[OVS_NAT_ATTR_PROTO_MAX]) : 0;
|
|
|
|
|
|
|
|
|
|
if ((addr_len == sizeof(ovs_be32)
|
|
|
|
|
&& ip_max && ntohl(ip_min) > ntohl(ip_max))
|
|
|
|
|
|| (addr_len == sizeof(struct in6_addr)
|
|
|
|
|
&& !ipv6_mask_is_any(&ip6_max)
|
|
|
|
|
&& memcmp(&ip6_min, &ip6_max, sizeof ip6_min) > 0)
|
|
|
|
|
|| (proto_max && proto_min > proto_max)) {
|
|
|
|
|
ds_put_cstr(ds, "nat(range error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "nat");
|
|
|
|
|
if (a[OVS_NAT_ATTR_SRC] || a[OVS_NAT_ATTR_DST]) {
|
|
|
|
|
ds_put_char(ds, '(');
|
|
|
|
|
if (a[OVS_NAT_ATTR_SRC]) {
|
|
|
|
|
ds_put_cstr(ds, "src");
|
|
|
|
|
} else if (a[OVS_NAT_ATTR_DST]) {
|
|
|
|
|
ds_put_cstr(ds, "dst");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (addr_len > 0) {
|
|
|
|
|
ds_put_cstr(ds, "=");
|
|
|
|
|
|
|
|
|
|
if (addr_len == sizeof ip_min) {
|
|
|
|
|
ds_put_format(ds, IP_FMT, IP_ARGS(ip_min));
|
|
|
|
|
|
|
|
|
|
if (ip_max && ip_max != ip_min) {
|
|
|
|
|
ds_put_format(ds, "-"IP_FMT, IP_ARGS(ip_max));
|
|
|
|
|
}
|
|
|
|
|
} else if (addr_len == sizeof ip6_min) {
|
|
|
|
|
ipv6_format_addr_bracket(&ip6_min, ds, proto_min);
|
|
|
|
|
|
|
|
|
|
if (!ipv6_mask_is_any(&ip6_max) &&
|
|
|
|
|
memcmp(&ip6_max, &ip6_min, sizeof ip6_max) != 0) {
|
|
|
|
|
ds_put_char(ds, '-');
|
|
|
|
|
ipv6_format_addr_bracket(&ip6_max, ds, proto_min);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (proto_min) {
|
|
|
|
|
ds_put_format(ds, ":%"PRIu16, proto_min);
|
|
|
|
|
|
|
|
|
|
if (proto_max && proto_max != proto_min) {
|
|
|
|
|
ds_put_format(ds, "-%"PRIu16, proto_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
if (a[OVS_NAT_ATTR_PERSISTENT]) {
|
|
|
|
|
ds_put_cstr(ds, "persistent,");
|
|
|
|
|
}
|
|
|
|
|
if (a[OVS_NAT_ATTR_PROTO_HASH]) {
|
|
|
|
|
ds_put_cstr(ds, "hash,");
|
|
|
|
|
}
|
|
|
|
|
if (a[OVS_NAT_ATTR_PROTO_RANDOM]) {
|
|
|
|
|
ds_put_cstr(ds, "random,");
|
|
|
|
|
}
|
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static const struct nl_policy ovs_conntrack_policy[] = {
|
|
|
|
|
[OVS_CT_ATTR_COMMIT] = { .type = NL_A_FLAG, .optional = true, },
|
|
|
|
|
[OVS_CT_ATTR_ZONE] = { .type = NL_A_U16, .optional = true, },
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
[OVS_CT_ATTR_MARK] = { .type = NL_A_UNSPEC, .optional = true,
|
|
|
|
|
.min_len = sizeof(uint32_t) * 2 },
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
[OVS_CT_ATTR_LABELS] = { .type = NL_A_UNSPEC, .optional = true,
|
|
|
|
|
.min_len = sizeof(struct ovs_key_ct_labels) * 2 },
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
[OVS_CT_ATTR_HELPER] = { .type = NL_A_STRING, .optional = true,
|
|
|
|
|
.min_len = 1, .max_len = 16 },
|
2015-11-24 15:47:56 -08:00
|
|
|
|
[OVS_CT_ATTR_NAT] = { .type = NL_A_UNSPEC, .optional = true },
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_conntrack_policy)];
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
const ovs_u128 *label;
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
const uint32_t *mark;
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
const char *helper;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
uint16_t zone;
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
bool commit;
|
2015-11-24 15:47:56 -08:00
|
|
|
|
const struct nlattr *nat;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
|
|
|
|
if (!nl_parse_nested(attr, ovs_conntrack_policy, a, ARRAY_SIZE(a))) {
|
|
|
|
|
ds_put_cstr(ds, "ct(error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commit = a[OVS_CT_ATTR_COMMIT] ? true : false;
|
|
|
|
|
zone = a[OVS_CT_ATTR_ZONE] ? nl_attr_get_u16(a[OVS_CT_ATTR_ZONE]) : 0;
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
mark = a[OVS_CT_ATTR_MARK] ? nl_attr_get(a[OVS_CT_ATTR_MARK]) : NULL;
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
label = a[OVS_CT_ATTR_LABELS] ? nl_attr_get(a[OVS_CT_ATTR_LABELS]): NULL;
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
helper = a[OVS_CT_ATTR_HELPER] ? nl_attr_get(a[OVS_CT_ATTR_HELPER]) : NULL;
|
2015-11-24 15:47:56 -08:00
|
|
|
|
nat = a[OVS_CT_ATTR_NAT];
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "ct");
|
2015-11-24 15:47:56 -08:00
|
|
|
|
if (commit || zone || mark || label || helper || nat) {
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
ds_put_cstr(ds, "(");
|
|
|
|
|
if (commit) {
|
|
|
|
|
ds_put_format(ds, "commit,");
|
|
|
|
|
}
|
|
|
|
|
if (zone) {
|
|
|
|
|
ds_put_format(ds, "zone=%"PRIu16",", zone);
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (mark) {
|
|
|
|
|
ds_put_format(ds, "mark=%#"PRIx32"/%#"PRIx32",", *mark,
|
|
|
|
|
*(mark + 1));
|
|
|
|
|
}
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
if (label) {
|
|
|
|
|
ds_put_format(ds, "label=");
|
|
|
|
|
format_u128(ds, label, label + 1, true);
|
2015-10-23 16:35:17 -07:00
|
|
|
|
ds_put_char(ds, ',');
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
}
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
if (helper) {
|
|
|
|
|
ds_put_format(ds, "helper=%s,", helper);
|
|
|
|
|
}
|
2015-11-24 15:47:56 -08:00
|
|
|
|
if (nat) {
|
|
|
|
|
format_odp_ct_nat(ds, nat);
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
ds_put_cstr(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 14:38:54 -07:00
|
|
|
|
static void
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_odp_action(struct ds *ds, const struct nlattr *a)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-10-12 16:24:54 -07:00
|
|
|
|
int expected_len;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
enum ovs_action_attr type = nl_attr_type(a);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
size_t size;
|
2010-12-10 10:40:58 -08:00
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
expected_len = odp_action_len(nl_attr_type(a));
|
2015-05-20 11:57:35 -07:00
|
|
|
|
if (expected_len != ATTR_LEN_VARIABLE &&
|
|
|
|
|
nl_attr_get_size(a) != expected_len) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ds_put_format(ds, "bad length %"PRIuSIZE", expected %d for: ",
|
2011-10-12 16:24:54 -07:00
|
|
|
|
nl_attr_get_size(a), expected_len);
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_generic_odp_action(ds, a);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 14:38:54 -07:00
|
|
|
|
switch (type) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_ACTION_ATTR_OUTPUT:
|
2013-06-14 17:09:34 +03:00
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
ofp-actions: Add truncate action.
The patch adds a new action to support packet truncation. The new action
is formatted as 'output(port=n,max_len=m)', as output to port n, with
packet size being MIN(original_size, m).
One use case is to enable port mirroring to send smaller packets to the
destination port so that only useful packet information is mirrored/copied,
saving some performance overhead of copying entire packet payload. Example
use case is below as well as shown in the testcases:
- Output to port 1 with max_len 100 bytes.
- The output packet size on port 1 will be MIN(original_packet_size, 100).
# ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)'
- The scope of max_len is limited to output action itself. The following
packet size of output:1 and output:2 will be intact.
# ovs-ofctl add-flow br0 \
'actions=output(port=1,max_len=100),output:1,output:2'
- The Datapath actions shows:
# Datapath actions: trunc(100),1,1,2
Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/140037134
Signed-off-by: William Tu <u9012063@gmail.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
2016-06-24 07:42:30 -07:00
|
|
|
|
case OVS_ACTION_ATTR_TRUNC: {
|
|
|
|
|
const struct ovs_action_trunc *trunc =
|
|
|
|
|
nl_attr_get_unspec(a, sizeof *trunc);
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "trunc(%"PRIu32")", trunc->max_len);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_POP:
|
|
|
|
|
ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
|
|
|
|
|
break;
|
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_PUSH:
|
|
|
|
|
format_odp_tnl_push_action(ds, a);
|
|
|
|
|
break;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE:
|
2011-10-12 16:24:54 -07:00
|
|
|
|
format_odp_userspace_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_ACTION_ATTR_RECIRC:
|
2014-04-08 18:42:39 -07:00
|
|
|
|
format_odp_recirc_action(ds, nl_attr_get_u32(a));
|
|
|
|
|
break;
|
|
|
|
|
case OVS_ACTION_ATTR_HASH:
|
|
|
|
|
format_odp_hash_action(ds, nl_attr_get(a));
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET_MASKED:
|
|
|
|
|
a = nl_attr_get(a);
|
|
|
|
|
size = nl_attr_get_size(a) / 2;
|
|
|
|
|
ds_put_cstr(ds, "set(");
|
|
|
|
|
|
|
|
|
|
/* Masked set action not supported for tunnel key, which is bigger. */
|
|
|
|
|
if (size <= sizeof(struct ovs_key_ipv6)) {
|
|
|
|
|
struct nlattr attr[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
|
|
|
|
|
sizeof(struct nlattr))];
|
|
|
|
|
struct nlattr mask[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
|
|
|
|
|
sizeof(struct nlattr))];
|
|
|
|
|
|
|
|
|
|
mask->nla_type = attr->nla_type = nl_attr_type(a);
|
|
|
|
|
mask->nla_len = attr->nla_len = NLA_HDRLEN + size;
|
|
|
|
|
memcpy(attr + 1, (char *)(a + 1), size);
|
|
|
|
|
memcpy(mask + 1, (char *)(a + 1) + size, size);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_odp_key_attr(attr, mask, NULL, ds, false);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
} else {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_odp_key_attr(a, NULL, NULL, ds, false);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(ds, ")");
|
|
|
|
|
break;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET:
|
|
|
|
|
ds_put_cstr(ds, "set(");
|
2013-09-23 22:58:46 -07:00
|
|
|
|
format_odp_key_attr(nl_attr_get(a), NULL, NULL, ds, true);
|
2011-10-21 14:38:54 -07:00
|
|
|
|
ds_put_cstr(ds, ")");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2015-12-01 15:03:02 +09:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN: {
|
|
|
|
|
const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
ds_put_cstr(ds, "push_vlan(");
|
|
|
|
|
if (vlan->vlan_tpid != htons(ETH_TYPE_VLAN)) {
|
|
|
|
|
ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(vlan->vlan_tpid));
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(ds, vlan->vlan_tci, OVS_BE16_MAX, false);
|
2011-11-14 17:19:41 -08:00
|
|
|
|
ds_put_char(ds, ')');
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2015-12-01 15:03:02 +09:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN:
|
|
|
|
|
ds_put_cstr(ds, "pop_vlan");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_MPLS: {
|
|
|
|
|
const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
|
|
|
|
|
ds_put_cstr(ds, "push_mpls(");
|
|
|
|
|
format_mpls_lse(ds, mpls->mpls_lse);
|
|
|
|
|
ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OVS_ACTION_ATTR_POP_MPLS: {
|
|
|
|
|
ovs_be16 ethertype = nl_attr_get_be16(a);
|
|
|
|
|
ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-28 10:43:07 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
|
|
|
format_odp_sample_action(ds, a);
|
|
|
|
|
break;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
case OVS_ACTION_ATTR_CT:
|
|
|
|
|
format_odp_conntrack_action(ds, a);
|
|
|
|
|
break;
|
dpif-netdev: Add clone action
Add support for userspace datapath clone action. The clone action
provides an action envelope to enclose an action list.
For example, with actions A, B, C and D, and an action list:
A, clone(B, C), D
The clone action will ensure that:
- D will see the same packet, and any meta states, such as flow, as
action B.
- D will be executed regardless whether B, or C drops a packet. They
can only drop a clone.
- When B drops a packet, clone will skip all remaining actions
within the clone envelope. This feature is useful when we add
meter action later: The meter action can be implemented as a
simple action without its own envolop (unlike the sample action).
When necessary, the flow translation layer can enclose a meter action
in clone.
The clone action is very similar with the OpenFlow clone action.
This is by design to simplify vswitchd flow translation logic.
Without datapath clone, vswitchd simulate the effect by inserting
datapath actions to "undo" clone actions. The above flow will be
translated into A, B, C, -C, -B, D.
However, there are two issues:
- The resulting datapath action list may be longer without using
clone.
- Some actions, such as NAT may not be possible to reverse.
This patch implements clone() simply with packet copy. The performance
can be improved with later patches, for example, to delay or avoid
packet copy if possible. It seems datapath should have enough context
to carry out such optimization without the userspace context.
Signed-off-by: Andy Zhou <azhou@ovn.org>
Acked-by: Jarno Rajahalme <jarno@ovn.org>
2017-01-10 18:13:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_CLONE:
|
|
|
|
|
format_odp_clone_action(ds, a);
|
|
|
|
|
break;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
default:
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_generic_odp_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_odp_actions(struct ds *ds, const struct nlattr *actions,
|
2010-12-11 22:51:31 -08:00
|
|
|
|
size_t actions_len)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-12-10 10:40:58 -08:00
|
|
|
|
if (actions_len) {
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
|
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
|
|
|
|
|
if (a != actions) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
format_odp_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-12-10 10:40:58 -08:00
|
|
|
|
if (left) {
|
2012-01-26 16:21:50 -08:00
|
|
|
|
int i;
|
|
|
|
|
|
2011-01-11 15:35:56 -08:00
|
|
|
|
if (left == actions_len) {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2012-01-26 16:21:50 -08:00
|
|
|
|
ds_put_format(ds, ",***%u leftover bytes*** (", left);
|
|
|
|
|
for (i = 0; i < left; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2010-12-10 10:40:58 -08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(ds, "drop");
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
/* Separate out parse_odp_userspace_action() function. */
|
2011-11-11 15:22:56 -08:00
|
|
|
|
static int
|
2014-08-17 20:19:36 -07:00
|
|
|
|
parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
|
2011-11-11 15:22:56 -08:00
|
|
|
|
{
|
2014-08-17 20:19:36 -07:00
|
|
|
|
uint32_t pid;
|
|
|
|
|
union user_action_cookie cookie;
|
|
|
|
|
struct ofpbuf buf;
|
|
|
|
|
odp_port_t tunnel_out_port;
|
|
|
|
|
int n = -1;
|
|
|
|
|
void *user_data = NULL;
|
|
|
|
|
size_t user_data_size = 0;
|
2015-07-17 21:37:02 -07:00
|
|
|
|
bool include_actions = false;
|
2016-01-04 16:18:41 -08:00
|
|
|
|
int res;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
|
|
|
|
|
if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) {
|
|
|
|
|
return -EINVAL;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-04 16:18:41 -08:00
|
|
|
|
ofpbuf_init(&buf, 16);
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
{
|
2013-11-09 15:44:23 -08:00
|
|
|
|
uint32_t output;
|
|
|
|
|
uint32_t probability;
|
|
|
|
|
uint32_t collector_set_id;
|
|
|
|
|
uint32_t obs_domain_id;
|
|
|
|
|
uint32_t obs_point_id;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
int vid, pcp;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
int n1 = -1;
|
|
|
|
|
if (ovs_scan(&s[n], ",sFlow(vid=%i,"
|
|
|
|
|
"pcp=%i,output=%"SCNi32")%n",
|
|
|
|
|
&vid, &pcp, &output, &n1)) {
|
2011-11-11 15:22:56 -08:00
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
tci = vid | (pcp << VLAN_PCP_SHIFT);
|
|
|
|
|
if (tci) {
|
|
|
|
|
tci |= VLAN_CFI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cookie.type = USER_ACTION_COOKIE_SFLOW;
|
2012-05-04 14:56:40 -07:00
|
|
|
|
cookie.sflow.vlan_tci = htons(tci);
|
|
|
|
|
cookie.sflow.output = output;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.sflow;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
} else if (ovs_scan(&s[n], ",slow_path(%n",
|
2014-08-17 20:19:36 -07:00
|
|
|
|
&n1)) {
|
|
|
|
|
n += n1;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
|
|
|
|
|
cookie.slow_path.unused = 0;
|
2013-09-20 12:54:51 -07:00
|
|
|
|
cookie.slow_path.reason = 0;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
|
2015-07-11 20:48:29 -07:00
|
|
|
|
res = parse_odp_flags(&s[n], slow_path_reason_to_string,
|
|
|
|
|
&cookie.slow_path.reason,
|
|
|
|
|
SLOW_PATH_REASON_MASK, NULL);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (res < 0 || s[n + res] != ')') {
|
2016-01-04 16:18:41 -08:00
|
|
|
|
goto out;
|
2013-09-20 12:54:51 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
n += res + 1;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.slow_path;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",flow_sample(probability=%"SCNi32","
|
2013-11-09 15:44:23 -08:00
|
|
|
|
"collector_set_id=%"SCNi32","
|
|
|
|
|
"obs_domain_id=%"SCNi32","
|
ipfix: Support tunnel information for Flow IPFIX.
Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
1) Create a new record in Flow_Sample_Collector_Set table:
'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
2) Add IPFIX configuration which is referred by corresponding
row in Flow_Sample_Collector_Set table:
'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
cache_active_timeout=60 cache_max_flows=13'
3) Add sample action to the flows:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,
obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added and with NXAST_SAMPLE2 action
in this patch, the step 3 should be configured like below:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample action.
When flow sample action upcall happens, tunnel information will be retrieved from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.
This patch mainly do three tasks:
1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
tunnel information. NXAST_SAMPLE2 action has a new added field
'sampling_port'.
2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
exporting tunnel information.
3) If 'sampling_port' is equal to output port and output port is a tunnel
port, the translation of OpenFlow "sample" action should first emit
set(tunnel(...)), then the sample action itself. It makes sure the
egress tunnel information can be sampled.
4) Add a test of flow-based IPFIX for tunnel set.
How to test flow-based IPFIX:
1) Setup a test environment with two Linux host with Docker supported
2) Create a Docker container and a GRE tunnel port on each host
3) Use ovs-docker to add the container on the bridge
4) Listen on port 4739 on the collector machine and use wireshark to filter
'cflow' packets.
5) Configure flow-based IPFIX:
- 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
- 'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
other_config:enable-tunnel-sampling=true'
- 'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
Note: The in-port is container port. The output port and sampling_port
are both open flow port and the output port is a GRE tunnel port.
6) Ping from the container whose host enabled flow-based IPFIX.
7) Get the IPFIX template pakcets and IPFIX information packets.
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-14 16:53:34 +08:00
|
|
|
|
"obs_point_id=%"SCNi32","
|
2016-11-23 23:15:19 -08:00
|
|
|
|
"output_port=%"SCNi32"%n",
|
2014-08-17 20:19:36 -07:00
|
|
|
|
&probability, &collector_set_id,
|
ipfix: Support tunnel information for Flow IPFIX.
Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
1) Create a new record in Flow_Sample_Collector_Set table:
'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
2) Add IPFIX configuration which is referred by corresponding
row in Flow_Sample_Collector_Set table:
'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
cache_active_timeout=60 cache_max_flows=13'
3) Add sample action to the flows:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,
obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added and with NXAST_SAMPLE2 action
in this patch, the step 3 should be configured like below:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample action.
When flow sample action upcall happens, tunnel information will be retrieved from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.
This patch mainly do three tasks:
1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
tunnel information. NXAST_SAMPLE2 action has a new added field
'sampling_port'.
2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
exporting tunnel information.
3) If 'sampling_port' is equal to output port and output port is a tunnel
port, the translation of OpenFlow "sample" action should first emit
set(tunnel(...)), then the sample action itself. It makes sure the
egress tunnel information can be sampled.
4) Add a test of flow-based IPFIX for tunnel set.
How to test flow-based IPFIX:
1) Setup a test environment with two Linux host with Docker supported
2) Create a Docker container and a GRE tunnel port on each host
3) Use ovs-docker to add the container on the bridge
4) Listen on port 4739 on the collector machine and use wireshark to filter
'cflow' packets.
5) Configure flow-based IPFIX:
- 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
- 'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
other_config:enable-tunnel-sampling=true'
- 'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
Note: The in-port is container port. The output port and sampling_port
are both open flow port and the output port is a GRE tunnel port.
6) Ping from the container whose host enabled flow-based IPFIX.
7) Get the IPFIX template pakcets and IPFIX information packets.
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-14 16:53:34 +08:00
|
|
|
|
&obs_domain_id, &obs_point_id,
|
|
|
|
|
&output, &n1)) {
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2013-04-22 10:01:14 -07:00
|
|
|
|
|
|
|
|
|
cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
|
|
|
|
|
cookie.flow_sample.probability = probability;
|
|
|
|
|
cookie.flow_sample.collector_set_id = collector_set_id;
|
|
|
|
|
cookie.flow_sample.obs_domain_id = obs_domain_id;
|
|
|
|
|
cookie.flow_sample.obs_point_id = obs_point_id;
|
ipfix: Support tunnel information for Flow IPFIX.
Add support to export tunnel information for flow-based IPFIX.
The original steps to configure flow level IPFIX:
1) Create a new record in Flow_Sample_Collector_Set table:
'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
2) Add IPFIX configuration which is referred by corresponding
row in Flow_Sample_Collector_Set table:
'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX
targets=\"IP:4739\" obs_domain_id=123 obs_point_id=456
cache_active_timeout=60 cache_max_flows=13'
3) Add sample action to the flows:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,
obs_domain_id=123,obs_point_id=456')',output:3'
NXAST_SAMPLE action was used in step 3. In order to support exporting tunnel
information, the NXAST_SAMPLE2 action was added and with NXAST_SAMPLE2 action
in this patch, the step 3 should be configured like below:
'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
'sampling_port' can be equal to ingress port or one of egress ports. If sampling
port is equal to output port and the output port is a tunnel port,
OVS_USERSPACE_ATTR_EGRESS_TUN_PORT will be set in the datapath flow sample action.
When flow sample action upcall happens, tunnel information will be retrieved from
the datapath and then IPFIX can export egress tunnel port information. If
samping_port=65535 (OFPP_NONE), flow-based IPFIX will keep the same behavior
as before.
This patch mainly do three tasks:
1) Add a new flow sample action NXAST_SAMPLE2 to support exporting
tunnel information. NXAST_SAMPLE2 action has a new added field
'sampling_port'.
2) Use 'other_configure: enable-tunnel-sampling' to enable or disable
exporting tunnel information.
3) If 'sampling_port' is equal to output port and output port is a tunnel
port, the translation of OpenFlow "sample" action should first emit
set(tunnel(...)), then the sample action itself. It makes sure the
egress tunnel information can be sampled.
4) Add a test of flow-based IPFIX for tunnel set.
How to test flow-based IPFIX:
1) Setup a test environment with two Linux host with Docker supported
2) Create a Docker container and a GRE tunnel port on each host
3) Use ovs-docker to add the container on the bridge
4) Listen on port 4739 on the collector machine and use wireshark to filter
'cflow' packets.
5) Configure flow-based IPFIX:
- 'ovs-vsctl -- create Flow_Sample_Collector_Set id=1 bridge="Bridge UUID"'
- 'ovs-vsctl -- set Flow_Sample_Collector_Set
"Flow_Sample_Collector_Set UUID" ipfix=@i -- --id=@i create IPFIX \
targets=\"IP:4739\" cache_active_timeout=60 cache_max_flows=13 \
other_config:enable-tunnel-sampling=true'
- 'ovs-ofctl add-flow mybridge in_port=1,
actions=sample'('probability=65535,collector_set_id=1,obs_domain_id=123,
obs_point_id=456,sampling_port=3')',output:3'
Note: The in-port is container port. The output port and sampling_port
are both open flow port and the output port is a GRE tunnel port.
6) Ping from the container whose host enabled flow-based IPFIX.
7) Get the IPFIX template pakcets and IPFIX information packets.
Signed-off-by: Benli Ye <daniely@vmware.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-14 16:53:34 +08:00
|
|
|
|
cookie.flow_sample.output_odp_port = u32_to_odp(output);
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.flow_sample;
|
2016-11-23 23:15:19 -08:00
|
|
|
|
|
|
|
|
|
if (ovs_scan(&s[n], ",ingress%n", &n1)) {
|
|
|
|
|
cookie.flow_sample.direction = NX_ACTION_SAMPLE_INGRESS;
|
|
|
|
|
n += n1;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",egress%n", &n1)) {
|
|
|
|
|
cookie.flow_sample.direction = NX_ACTION_SAMPLE_EGRESS;
|
|
|
|
|
n += n1;
|
|
|
|
|
} else {
|
|
|
|
|
cookie.flow_sample.direction = NX_ACTION_SAMPLE_DEFAULT;
|
|
|
|
|
}
|
|
|
|
|
if (s[n] != ')') {
|
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
n++;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
} else if (ovs_scan(&s[n], ",ipfix(output_port=%"SCNi32")%n",
|
|
|
|
|
&output, &n1) ) {
|
|
|
|
|
n += n1;
|
2013-04-22 10:01:14 -07:00
|
|
|
|
cookie.type = USER_ACTION_COOKIE_IPFIX;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
cookie.ipfix.output_odp_port = u32_to_odp(output);
|
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.ipfix;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",userdata(%n",
|
|
|
|
|
&n1)) {
|
2013-02-15 16:48:32 -08:00
|
|
|
|
char *end;
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2013-02-15 16:48:32 -08:00
|
|
|
|
end = ofpbuf_put_hex(&buf, &s[n], NULL);
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (end[0] != ')') {
|
2016-01-04 16:18:41 -08:00
|
|
|
|
res = -EINVAL;
|
|
|
|
|
goto out;
|
2013-02-15 16:48:32 -08:00
|
|
|
|
}
|
2015-03-02 17:29:44 -08:00
|
|
|
|
user_data = buf.data;
|
|
|
|
|
user_data_size = buf.size;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n = (end + 1) - s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-17 21:37:02 -07:00
|
|
|
|
{
|
|
|
|
|
int n1 = -1;
|
|
|
|
|
if (ovs_scan(&s[n], ",actions%n", &n1)) {
|
|
|
|
|
n += n1;
|
|
|
|
|
include_actions = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
{
|
|
|
|
|
int n1 = -1;
|
|
|
|
|
if (ovs_scan(&s[n], ",tunnel_out_port=%"SCNi32")%n",
|
|
|
|
|
&tunnel_out_port, &n1)) {
|
2015-07-17 21:37:02 -07:00
|
|
|
|
odp_put_userspace_action(pid, user_data, user_data_size,
|
|
|
|
|
tunnel_out_port, include_actions, actions);
|
2016-01-04 16:18:41 -08:00
|
|
|
|
res = n + n1;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
} else if (s[n] == ')') {
|
2015-07-17 21:37:02 -07:00
|
|
|
|
odp_put_userspace_action(pid, user_data, user_data_size,
|
|
|
|
|
ODPP_NONE, include_actions, actions);
|
2016-01-04 16:18:41 -08:00
|
|
|
|
res = n + 1;
|
|
|
|
|
} else {
|
|
|
|
|
res = -EINVAL;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-04 16:18:41 -08:00
|
|
|
|
out:
|
|
|
|
|
ofpbuf_uninit(&buf);
|
|
|
|
|
return res;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
static int
|
|
|
|
|
ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
struct eth_header *eth;
|
|
|
|
|
struct ip_header *ip;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
struct ovs_16aligned_ip6_hdr *ip6;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
struct udp_header *udp;
|
|
|
|
|
struct gre_base_hdr *greh;
|
2015-04-10 11:08:10 -03:00
|
|
|
|
uint16_t gre_proto, gre_flags, dl_type, udp_src, udp_dst, csum;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
ovs_be32 sip, dip;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
uint32_t tnl_type = 0, header_len = 0, ip_len = 0;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
void *l3, *l4;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
eth = (struct eth_header *) data->header;
|
|
|
|
|
l3 = (data->header + sizeof *eth);
|
|
|
|
|
ip = (struct ip_header *) l3;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
ip6 = (struct ovs_16aligned_ip6_hdr *) l3;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
|
2015-11-24 15:47:56 -08:00
|
|
|
|
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
|
|
|
|
&data->header_len,
|
|
|
|
|
&data->tnl_type,
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
2014-11-11 11:53:47 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
|
2015-11-24 15:47:56 -08:00
|
|
|
|
ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
|
2014-11-11 11:53:47 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
eth->eth_type = htons(dl_type);
|
|
|
|
|
|
2015-12-04 12:36:47 -02:00
|
|
|
|
if (eth->eth_type == htons(ETH_TYPE_IP)) {
|
|
|
|
|
/* IPv4 */
|
2016-02-01 11:31:54 -08:00
|
|
|
|
uint16_t ip_frag_off;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
|
|
|
|
|
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
|
|
|
|
IP_SCAN_ARGS(&sip),
|
|
|
|
|
IP_SCAN_ARGS(&dip),
|
|
|
|
|
&ip->ip_proto, &ip->ip_tos,
|
2016-02-01 11:31:54 -08:00
|
|
|
|
&ip->ip_ttl, &ip_frag_off)) {
|
2015-12-04 12:36:47 -02:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(&ip->ip_src, sip);
|
|
|
|
|
put_16aligned_be32(&ip->ip_dst, dip);
|
2016-02-01 11:31:54 -08:00
|
|
|
|
ip->ip_frag_off = htons(ip_frag_off);
|
2015-12-04 12:36:47 -02:00
|
|
|
|
ip_len = sizeof *ip;
|
|
|
|
|
} else {
|
|
|
|
|
char sip6_s[IPV6_SCAN_LEN + 1];
|
|
|
|
|
char dip6_s[IPV6_SCAN_LEN + 1];
|
|
|
|
|
struct in6_addr sip6, dip6;
|
|
|
|
|
uint8_t tclass;
|
|
|
|
|
uint32_t label;
|
|
|
|
|
if (!ovs_scan_len(s, &n, "ipv6(src="IPV6_SCAN_FMT",dst="IPV6_SCAN_FMT
|
|
|
|
|
",label=%i,proto=%"SCNi8",tclass=0x%"SCNx8
|
|
|
|
|
",hlimit=%"SCNi8"),",
|
|
|
|
|
sip6_s, dip6_s, &label, &ip6->ip6_nxt,
|
|
|
|
|
&tclass, &ip6->ip6_hlim)
|
|
|
|
|
|| (label & ~IPV6_LABEL_MASK) != 0
|
|
|
|
|
|| inet_pton(AF_INET6, sip6_s, &sip6) != 1
|
|
|
|
|
|| inet_pton(AF_INET6, dip6_s, &dip6) != 1) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(&ip6->ip6_flow, htonl(6 << 28) |
|
|
|
|
|
htonl(tclass << 20) | htonl(label));
|
|
|
|
|
memcpy(&ip6->ip6_src, &sip6, sizeof(ip6->ip6_src));
|
|
|
|
|
memcpy(&ip6->ip6_dst, &dip6, sizeof(ip6->ip6_dst));
|
|
|
|
|
ip_len = sizeof *ip6;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tunnel header */
|
2015-12-04 12:36:47 -02:00
|
|
|
|
l4 = ((uint8_t *) l3 + ip_len);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
udp = (struct udp_header *) l4;
|
|
|
|
|
greh = (struct gre_base_hdr *) l4;
|
2015-03-26 14:27:19 -07:00
|
|
|
|
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),",
|
2015-11-24 15:47:56 -08:00
|
|
|
|
&udp_src, &udp_dst, &csum)) {
|
2015-03-26 13:51:06 -07:00
|
|
|
|
uint32_t vx_flags, vni;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
udp->udp_src = htons(udp_src);
|
|
|
|
|
udp->udp_dst = htons(udp_dst);
|
|
|
|
|
udp->udp_len = 0;
|
2015-03-26 14:27:19 -07:00
|
|
|
|
udp->udp_csum = htons(csum);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
2015-03-26 12:07:02 -07:00
|
|
|
|
if (ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
|
2015-11-24 15:47:56 -08:00
|
|
|
|
&vx_flags, &vni)) {
|
2015-03-26 12:07:02 -07:00
|
|
|
|
struct vxlanhdr *vxh = (struct vxlanhdr *) (udp + 1);
|
|
|
|
|
|
|
|
|
|
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
|
2015-03-26 13:51:06 -07:00
|
|
|
|
put_16aligned_be32(&vxh->vx_vni, htonl(vni << 8));
|
2015-03-26 12:07:02 -07:00
|
|
|
|
tnl_type = OVS_VPORT_TYPE_VXLAN;
|
2015-12-04 12:36:47 -02:00
|
|
|
|
header_len = sizeof *eth + ip_len +
|
2015-03-26 12:07:02 -07:00
|
|
|
|
sizeof *udp + sizeof *vxh;
|
2015-03-26 13:51:06 -07:00
|
|
|
|
} else if (ovs_scan_len(s, &n, "geneve(")) {
|
|
|
|
|
struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
|
|
|
|
|
|
2015-04-07 18:55:54 -07:00
|
|
|
|
memset(gnh, 0, sizeof *gnh);
|
2015-12-04 12:36:47 -02:00
|
|
|
|
header_len = sizeof *eth + ip_len +
|
2015-06-22 14:23:37 -07:00
|
|
|
|
sizeof *udp + sizeof *gnh;
|
|
|
|
|
|
2015-03-26 13:51:06 -07:00
|
|
|
|
if (ovs_scan_len(s, &n, "oam,")) {
|
|
|
|
|
gnh->oam = 1;
|
|
|
|
|
}
|
2015-06-22 14:23:37 -07:00
|
|
|
|
if (ovs_scan_len(s, &n, "crit,")) {
|
|
|
|
|
gnh->critical = 1;
|
|
|
|
|
}
|
|
|
|
|
if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
|
2015-03-26 13:51:06 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2015-06-22 14:23:37 -07:00
|
|
|
|
if (ovs_scan_len(s, &n, ",options(")) {
|
|
|
|
|
struct geneve_scan options;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
memset(&options, 0, sizeof options);
|
|
|
|
|
len = scan_geneve(s + n, &options, NULL);
|
|
|
|
|
if (!len) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(gnh->options, options.d, options.len);
|
|
|
|
|
gnh->opt_len = options.len / 4;
|
|
|
|
|
header_len += options.len;
|
|
|
|
|
|
|
|
|
|
n += len;
|
|
|
|
|
}
|
|
|
|
|
if (!ovs_scan_len(s, &n, "))")) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-26 13:51:06 -07:00
|
|
|
|
gnh->proto_type = htons(ETH_TYPE_TEB);
|
|
|
|
|
put_16aligned_be32(&gnh->vni, htonl(vni << 8));
|
|
|
|
|
tnl_type = OVS_VPORT_TYPE_GENEVE;
|
2015-03-26 12:07:02 -07:00
|
|
|
|
} else {
|
2014-11-11 11:53:47 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
|
2015-11-24 15:47:56 -08:00
|
|
|
|
&gre_flags, &gre_proto)){
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
tnl_type = OVS_VPORT_TYPE_GRE;
|
2015-04-10 11:08:10 -03:00
|
|
|
|
greh->flags = htons(gre_flags);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
greh->protocol = htons(gre_proto);
|
|
|
|
|
ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
|
|
|
|
|
|
|
|
|
|
if (greh->flags & htons(GRE_CSUM)) {
|
2015-03-26 17:09:38 -07:00
|
|
|
|
if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx16, &csum)) {
|
2014-11-11 11:53:47 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2015-03-26 17:09:38 -07:00
|
|
|
|
|
|
|
|
|
memset(options, 0, sizeof *options);
|
|
|
|
|
*((ovs_be16 *)options) = htons(csum);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_KEY)) {
|
|
|
|
|
uint32_t key;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
put_16aligned_be32(options, htonl(key));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_SEQ)) {
|
|
|
|
|
uint32_t seq;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(options, htonl(seq));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "))")) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-04 12:36:47 -02:00
|
|
|
|
header_len = sizeof *eth + ip_len +
|
2014-11-11 11:53:47 -08:00
|
|
|
|
((uint8_t *) options - (uint8_t *) greh);
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check tunnel meta data. */
|
|
|
|
|
if (data->tnl_type != tnl_type) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (data->header_len != header_len) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Out port */
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-24 15:47:56 -08:00
|
|
|
|
struct ct_nat_params {
|
|
|
|
|
bool snat;
|
|
|
|
|
bool dnat;
|
|
|
|
|
size_t addr_len;
|
|
|
|
|
union {
|
|
|
|
|
ovs_be32 ip;
|
|
|
|
|
struct in6_addr ip6;
|
|
|
|
|
} addr_min;
|
|
|
|
|
union {
|
|
|
|
|
ovs_be32 ip;
|
|
|
|
|
struct in6_addr ip6;
|
|
|
|
|
} addr_max;
|
|
|
|
|
uint16_t proto_min;
|
|
|
|
|
uint16_t proto_max;
|
|
|
|
|
bool persistent;
|
|
|
|
|
bool proto_hash;
|
|
|
|
|
bool proto_random;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
scan_ct_nat_range(const char *s, int *n, struct ct_nat_params *p)
|
|
|
|
|
{
|
|
|
|
|
if (ovs_scan_len(s, n, "=")) {
|
|
|
|
|
char ipv6_s[IPV6_SCAN_LEN + 1];
|
|
|
|
|
struct in6_addr ipv6;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan_len(s, n, IP_SCAN_FMT, IP_SCAN_ARGS(&p->addr_min.ip))) {
|
|
|
|
|
p->addr_len = sizeof p->addr_min.ip;
|
|
|
|
|
if (ovs_scan_len(s, n, "-")) {
|
|
|
|
|
if (!ovs_scan_len(s, n, IP_SCAN_FMT,
|
|
|
|
|
IP_SCAN_ARGS(&p->addr_max.ip))) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if ((ovs_scan_len(s, n, IPV6_SCAN_FMT, ipv6_s)
|
|
|
|
|
|| ovs_scan_len(s, n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) {
|
|
|
|
|
p->addr_len = sizeof p->addr_min.ip6;
|
|
|
|
|
p->addr_min.ip6 = ipv6;
|
|
|
|
|
if (ovs_scan_len(s, n, "-")) {
|
|
|
|
|
if ((ovs_scan_len(s, n, IPV6_SCAN_FMT, ipv6_s)
|
|
|
|
|
|| ovs_scan_len(s, n, "["IPV6_SCAN_FMT"]", ipv6_s))
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, &ipv6) == 1) {
|
|
|
|
|
p->addr_max.ip6 = ipv6;
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan_len(s, n, ":%"SCNu16, &p->proto_min)) {
|
|
|
|
|
if (ovs_scan_len(s, n, "-")) {
|
|
|
|
|
if (!ovs_scan_len(s, n, "%"SCNu16, &p->proto_max)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
scan_ct_nat(const char *s, struct ct_nat_params *p)
|
|
|
|
|
{
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan_len(s, &n, "nat")) {
|
|
|
|
|
memset(p, 0, sizeof *p);
|
|
|
|
|
|
|
|
|
|
if (ovs_scan_len(s, &n, "(")) {
|
|
|
|
|
char *end;
|
|
|
|
|
int end_n;
|
|
|
|
|
|
|
|
|
|
end = strchr(s + n, ')');
|
|
|
|
|
if (!end) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
end_n = end - s;
|
|
|
|
|
|
|
|
|
|
while (n < end_n) {
|
|
|
|
|
n += strspn(s + n, delimiters);
|
|
|
|
|
if (ovs_scan_len(s, &n, "src")) {
|
|
|
|
|
int err = scan_ct_nat_range(s, &n, p);
|
|
|
|
|
if (err) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
p->snat = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan_len(s, &n, "dst")) {
|
|
|
|
|
int err = scan_ct_nat_range(s, &n, p);
|
|
|
|
|
if (err) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
p->dnat = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan_len(s, &n, "persistent")) {
|
|
|
|
|
p->persistent = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan_len(s, &n, "hash")) {
|
|
|
|
|
p->proto_hash = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan_len(s, &n, "random")) {
|
|
|
|
|
p->proto_random = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (p->snat && p->dnat) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if ((p->addr_len != 0 &&
|
|
|
|
|
memcmp(&p->addr_max, &in6addr_any, p->addr_len) &&
|
|
|
|
|
memcmp(&p->addr_max, &p->addr_min, p->addr_len) < 0) ||
|
|
|
|
|
(p->proto_max && p->proto_max < p->proto_min)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (p->proto_hash && p->proto_random) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
nl_msg_put_ct_nat(struct ct_nat_params *p, struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
size_t start = nl_msg_start_nested(actions, OVS_CT_ATTR_NAT);
|
|
|
|
|
|
|
|
|
|
if (p->snat) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_SRC);
|
|
|
|
|
} else if (p->dnat) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_DST);
|
|
|
|
|
} else {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (p->addr_len != 0) {
|
|
|
|
|
nl_msg_put_unspec(actions, OVS_NAT_ATTR_IP_MIN, &p->addr_min,
|
|
|
|
|
p->addr_len);
|
|
|
|
|
if (memcmp(&p->addr_max, &p->addr_min, p->addr_len) > 0) {
|
|
|
|
|
nl_msg_put_unspec(actions, OVS_NAT_ATTR_IP_MAX, &p->addr_max,
|
|
|
|
|
p->addr_len);
|
|
|
|
|
}
|
|
|
|
|
if (p->proto_min) {
|
|
|
|
|
nl_msg_put_u16(actions, OVS_NAT_ATTR_PROTO_MIN, p->proto_min);
|
|
|
|
|
if (p->proto_max && p->proto_max > p->proto_min) {
|
|
|
|
|
nl_msg_put_u16(actions, OVS_NAT_ATTR_PROTO_MAX, p->proto_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (p->persistent) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PERSISTENT);
|
|
|
|
|
}
|
|
|
|
|
if (p->proto_hash) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PROTO_HASH);
|
|
|
|
|
}
|
|
|
|
|
if (p->proto_random) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_NAT_ATTR_PROTO_RANDOM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
nl_msg_end_nested(actions, start);
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static int
|
|
|
|
|
parse_conntrack_action(const char *s_, struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
const char *s = s_;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "ct")) {
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
const char *helper = NULL;
|
|
|
|
|
size_t helper_len = 0;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
bool commit = false;
|
|
|
|
|
uint16_t zone = 0;
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
struct {
|
|
|
|
|
uint32_t value;
|
|
|
|
|
uint32_t mask;
|
|
|
|
|
} ct_mark = { 0, 0 };
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
struct {
|
|
|
|
|
ovs_u128 value;
|
|
|
|
|
ovs_u128 mask;
|
|
|
|
|
} ct_label;
|
2015-11-24 15:47:56 -08:00
|
|
|
|
struct ct_nat_params nat_params;
|
|
|
|
|
bool have_nat = false;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
size_t start;
|
|
|
|
|
char *end;
|
|
|
|
|
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
memset(&ct_label, 0, sizeof(ct_label));
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
s += 2;
|
|
|
|
|
if (ovs_scan(s, "(")) {
|
|
|
|
|
s++;
|
2015-11-24 15:47:56 -08:00
|
|
|
|
find_end:
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
end = strchr(s, ')');
|
|
|
|
|
if (!end) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (s != end) {
|
2015-11-24 15:47:56 -08:00
|
|
|
|
int n;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
|
|
|
|
s += strspn(s, delimiters);
|
|
|
|
|
if (ovs_scan(s, "commit%n", &n)) {
|
|
|
|
|
commit = true;
|
|
|
|
|
s += n;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ovs_scan(s, "zone=%"SCNu16"%n", &zone, &n)) {
|
|
|
|
|
s += n;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (ovs_scan(s, "mark=%"SCNx32"%n", &ct_mark.value, &n)) {
|
|
|
|
|
s += n;
|
|
|
|
|
n = -1;
|
|
|
|
|
if (ovs_scan(s, "/%"SCNx32"%n", &ct_mark.mask, &n)) {
|
|
|
|
|
s += n;
|
|
|
|
|
} else {
|
|
|
|
|
ct_mark.mask = UINT32_MAX;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
if (ovs_scan(s, "label=%n", &n)) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
s += n;
|
|
|
|
|
retval = scan_u128(s, &ct_label.value, &ct_label.mask);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
if (ovs_scan(s, "helper=%n", &n)) {
|
|
|
|
|
s += n;
|
|
|
|
|
helper_len = strcspn(s, delimiters_end);
|
|
|
|
|
if (!helper_len || helper_len > 15) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
helper = s;
|
|
|
|
|
s += helper_len;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
2015-11-24 15:47:56 -08:00
|
|
|
|
n = scan_ct_nat(s, &nat_params);
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
s += n;
|
|
|
|
|
have_nat = true;
|
|
|
|
|
|
|
|
|
|
/* end points to the end of the nested, nat action.
|
|
|
|
|
* find the real end. */
|
|
|
|
|
goto find_end;
|
|
|
|
|
}
|
|
|
|
|
/* Nothing matched. */
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
start = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CT);
|
|
|
|
|
if (commit) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_CT_ATTR_COMMIT);
|
|
|
|
|
}
|
|
|
|
|
if (zone) {
|
|
|
|
|
nl_msg_put_u16(actions, OVS_CT_ATTR_ZONE, zone);
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (ct_mark.mask) {
|
|
|
|
|
nl_msg_put_unspec(actions, OVS_CT_ATTR_MARK, &ct_mark,
|
|
|
|
|
sizeof(ct_mark));
|
|
|
|
|
}
|
2016-05-03 18:20:51 -07:00
|
|
|
|
if (!ovs_u128_is_zero(ct_label.mask)) {
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
nl_msg_put_unspec(actions, OVS_CT_ATTR_LABELS, &ct_label,
|
|
|
|
|
sizeof ct_label);
|
|
|
|
|
}
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
|
if (helper) {
|
|
|
|
|
nl_msg_put_string__(actions, OVS_CT_ATTR_HELPER, helper,
|
|
|
|
|
helper_len);
|
|
|
|
|
}
|
2015-11-24 15:47:56 -08:00
|
|
|
|
if (have_nat) {
|
|
|
|
|
nl_msg_put_ct_nat(&nat_params, actions);
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
nl_msg_end_nested(actions, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s - s_;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 19:23:27 -08:00
|
|
|
|
static int
|
|
|
|
|
parse_action_list(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
n += strspn(s + n, delimiters);
|
|
|
|
|
if (s[n] == ')') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
retval = parse_odp_action(s + n, port_names, actions);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
n += retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
static int
|
|
|
|
|
parse_odp_action(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
uint32_t port;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
|
|
|
|
|
return n;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
ofp-actions: Add truncate action.
The patch adds a new action to support packet truncation. The new action
is formatted as 'output(port=n,max_len=m)', as output to port n, with
packet size being MIN(original_size, m).
One use case is to enable port mirroring to send smaller packets to the
destination port so that only useful packet information is mirrored/copied,
saving some performance overhead of copying entire packet payload. Example
use case is below as well as shown in the testcases:
- Output to port 1 with max_len 100 bytes.
- The output packet size on port 1 will be MIN(original_packet_size, 100).
# ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)'
- The scope of max_len is limited to output action itself. The following
packet size of output:1 and output:2 will be intact.
# ovs-ofctl add-flow br0 \
'actions=output(port=1,max_len=100),output:1,output:2'
- The Datapath actions shows:
# Datapath actions: trunc(100),1,1,2
Tested-at: https://travis-ci.org/williamtu/ovs-travis/builds/140037134
Signed-off-by: William Tu <u9012063@gmail.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
2016-06-24 07:42:30 -07:00
|
|
|
|
{
|
|
|
|
|
uint32_t max_len;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "trunc(%"SCNi32")%n", &max_len, &n)) {
|
|
|
|
|
struct ovs_action_trunc *trunc;
|
|
|
|
|
|
|
|
|
|
trunc = nl_msg_put_unspec_uninit(actions,
|
|
|
|
|
OVS_ACTION_ATTR_TRUNC, sizeof *trunc);
|
|
|
|
|
trunc->max_len = max_len;
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (port_names) {
|
|
|
|
|
int len = strcspn(s, delimiters);
|
|
|
|
|
struct simap_node *node;
|
|
|
|
|
|
|
|
|
|
node = simap_find_len(port_names, s, len);
|
|
|
|
|
if (node) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-03 15:04:15 -07:00
|
|
|
|
{
|
|
|
|
|
uint32_t recirc_id;
|
|
|
|
|
int n = -1;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "recirc(%"PRIu32")%n", &recirc_id, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_RECIRC, recirc_id);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (!strncmp(s, "userspace(", 10)) {
|
|
|
|
|
return parse_odp_userspace_action(s, actions);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
if (!strncmp(s, "set(", 4)) {
|
|
|
|
|
size_t start_ofs;
|
|
|
|
|
int retval;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
struct nlattr mask[128 / sizeof(struct nlattr)];
|
|
|
|
|
struct ofpbuf maskbuf;
|
|
|
|
|
struct nlattr *nested, *key;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
/* 'mask' is big enough to hold any key. */
|
|
|
|
|
ofpbuf_use_stack(&maskbuf, mask, sizeof mask);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
|
|
|
|
start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
retval = parse_odp_key_mask_attr(s + 4, port_names, actions, &maskbuf);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
if (s[retval + 4] != ')') {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2014-09-05 15:44:19 -07:00
|
|
|
|
|
|
|
|
|
nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested);
|
|
|
|
|
key = nested + 1;
|
|
|
|
|
|
|
|
|
|
size = nl_attr_get_size(mask);
|
|
|
|
|
if (size == nl_attr_get_size(key)) {
|
|
|
|
|
/* Change to masked set action if not fully masked. */
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (!is_all_ones(mask + 1, size)) {
|
2014-09-05 15:44:19 -07:00
|
|
|
|
key->nla_len += size;
|
|
|
|
|
ofpbuf_put(actions, mask + 1, size);
|
|
|
|
|
/* 'actions' may have been reallocated by ofpbuf_put(). */
|
|
|
|
|
nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested);
|
|
|
|
|
nested->nla_type = OVS_ACTION_ATTR_SET_MASKED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
nl_msg_end_nested(actions, start_ofs);
|
|
|
|
|
return retval + 5;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 10:24:05 -08:00
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_vlan push;
|
|
|
|
|
int tpid = ETH_TYPE_VLAN;
|
|
|
|
|
int vid, pcp;
|
|
|
|
|
int cfi = 1;
|
|
|
|
|
int n = -1;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2013-11-09 15:44:23 -08:00
|
|
|
|
if (ovs_scan(s, "push_vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(vid=%i,pcp=%i,cfi=%i)%n",
|
|
|
|
|
&vid, &pcp, &cfi, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i)%n",
|
|
|
|
|
&tpid, &vid, &pcp, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i,cfi=%i)%n",
|
|
|
|
|
&tpid, &vid, &pcp, &cfi, &n)) {
|
2011-11-17 10:24:05 -08:00
|
|
|
|
push.vlan_tpid = htons(tpid);
|
|
|
|
|
push.vlan_tci = htons((vid << VLAN_VID_SHIFT)
|
|
|
|
|
| (pcp << VLAN_PCP_SHIFT)
|
|
|
|
|
| (cfi ? VLAN_CFI : 0));
|
|
|
|
|
nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_VLAN,
|
|
|
|
|
&push, sizeof push);
|
|
|
|
|
|
|
|
|
|
return n;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 10:24:05 -08:00
|
|
|
|
if (!strncmp(s, "pop_vlan", 8)) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_VLAN);
|
|
|
|
|
return 8;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
double percentage;
|
|
|
|
|
int n = -1;
|
|
|
|
|
|
2013-11-09 15:44:23 -08:00
|
|
|
|
if (ovs_scan(s, "sample(sample=%lf%%,actions(%n", &percentage, &n)
|
|
|
|
|
&& percentage >= 0. && percentage <= 100.0) {
|
2011-11-11 15:22:56 -08:00
|
|
|
|
size_t sample_ofs, actions_ofs;
|
|
|
|
|
double probability;
|
|
|
|
|
|
|
|
|
|
probability = floor(UINT32_MAX * (percentage / 100.0) + .5);
|
|
|
|
|
sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE);
|
|
|
|
|
nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY,
|
|
|
|
|
(probability <= 0 ? 0
|
|
|
|
|
: probability >= UINT32_MAX ? UINT32_MAX
|
|
|
|
|
: probability));
|
|
|
|
|
|
|
|
|
|
actions_ofs = nl_msg_start_nested(actions,
|
|
|
|
|
OVS_SAMPLE_ATTR_ACTIONS);
|
2017-01-31 19:23:27 -08:00
|
|
|
|
int retval = parse_action_list(s + n, port_names, actions);
|
|
|
|
|
if (retval < 0)
|
|
|
|
|
return retval;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2017-01-31 19:23:27 -08:00
|
|
|
|
n += retval;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
nl_msg_end_nested(actions, actions_ofs);
|
|
|
|
|
nl_msg_end_nested(actions, sample_ofs);
|
|
|
|
|
|
|
|
|
|
return s[n + 1] == ')' ? n + 2 : -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-31 19:23:27 -08:00
|
|
|
|
{
|
|
|
|
|
if (!strncmp(s, "clone(", 6)) {
|
|
|
|
|
size_t actions_ofs;
|
|
|
|
|
int n = 6;
|
|
|
|
|
|
|
|
|
|
actions_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_CLONE);
|
|
|
|
|
int retval = parse_action_list(s + n, port_names, actions);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
n += retval;
|
|
|
|
|
nl_msg_end_nested(actions, actions_ofs);
|
|
|
|
|
return n + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t port;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "tnl_pop(%"SCNi32")%n", &port, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_TUNNEL_POP, port);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
retval = parse_conntrack_action(s, actions);
|
|
|
|
|
if (retval) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_tnl data;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
n = ovs_parse_tnl_push(s, &data);
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
odp_put_tnl_push_action(actions, &data);
|
|
|
|
|
return n;
|
|
|
|
|
} else if (n < 0) {
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-11 15:22:56 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parses the string representation of datapath actions, in the format output
|
|
|
|
|
* by format_odp_action(). Returns 0 if successful, otherwise a positive errno
|
|
|
|
|
* value. On success, the ODP actions are appended to 'actions' as a series of
|
|
|
|
|
* Netlink attributes. On failure, no data is appended to 'actions'. Either
|
|
|
|
|
* way, 'actions''s data might be reallocated. */
|
|
|
|
|
int
|
2012-05-22 10:32:02 -07:00
|
|
|
|
odp_actions_from_string(const char *s, const struct simap *port_names,
|
2011-11-11 15:22:56 -08:00
|
|
|
|
struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
size_t old_size;
|
|
|
|
|
|
|
|
|
|
if (!strcasecmp(s, "drop")) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 17:29:44 -08:00
|
|
|
|
old_size = actions->size;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
s += strspn(s, delimiters);
|
|
|
|
|
if (!*s) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = parse_odp_action(s, port_names, actions);
|
|
|
|
|
if (retval < 0 || !strchr(delimiters, s[retval])) {
|
2015-03-02 17:29:44 -08:00
|
|
|
|
actions->size = old_size;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
return -retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-10-11 13:31:35 -07:00
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
static const struct attr_len_tbl ovs_vxlan_ext_attr_lens[OVS_VXLAN_EXT_MAX + 1] = {
|
|
|
|
|
[OVS_VXLAN_EXT_GBP] = { .len = 4 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_ID] = { .len = 8 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = { .len = 4 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_IPV4_DST] = { .len = 4 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_TOS] = { .len = 1 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_TTL] = { .len = 1 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = { .len = 0 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_CSUM] = { .len = 0 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_TP_SRC] = { .len = 2 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_TP_DST] = { .len = 2 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_OAM] = { .len = 0 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = { .len = ATTR_LEN_VARIABLE },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = ATTR_LEN_NESTED,
|
|
|
|
|
.next = ovs_vxlan_ext_attr_lens ,
|
|
|
|
|
.next_max = OVS_VXLAN_EXT_MAX},
|
2015-11-25 11:31:11 -02:00
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 },
|
|
|
|
|
[OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 },
|
2015-05-20 11:57:35 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
|
|
|
|
|
[OVS_KEY_ATTR_ENCAP] = { .len = ATTR_LEN_NESTED },
|
|
|
|
|
[OVS_KEY_ATTR_PRIORITY] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_SKB_MARK] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_DP_HASH] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_RECIRC_ID] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_TUNNEL] = { .len = ATTR_LEN_NESTED,
|
|
|
|
|
.next = ovs_tun_key_attr_lens,
|
|
|
|
|
.next_max = OVS_TUNNEL_KEY_ATTR_MAX },
|
|
|
|
|
[OVS_KEY_ATTR_IN_PORT] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_ETHERNET] = { .len = sizeof(struct ovs_key_ethernet) },
|
|
|
|
|
[OVS_KEY_ATTR_VLAN] = { .len = 2 },
|
|
|
|
|
[OVS_KEY_ATTR_ETHERTYPE] = { .len = 2 },
|
|
|
|
|
[OVS_KEY_ATTR_MPLS] = { .len = ATTR_LEN_VARIABLE },
|
|
|
|
|
[OVS_KEY_ATTR_IPV4] = { .len = sizeof(struct ovs_key_ipv4) },
|
|
|
|
|
[OVS_KEY_ATTR_IPV6] = { .len = sizeof(struct ovs_key_ipv6) },
|
|
|
|
|
[OVS_KEY_ATTR_TCP] = { .len = sizeof(struct ovs_key_tcp) },
|
|
|
|
|
[OVS_KEY_ATTR_TCP_FLAGS] = { .len = 2 },
|
|
|
|
|
[OVS_KEY_ATTR_UDP] = { .len = sizeof(struct ovs_key_udp) },
|
|
|
|
|
[OVS_KEY_ATTR_SCTP] = { .len = sizeof(struct ovs_key_sctp) },
|
|
|
|
|
[OVS_KEY_ATTR_ICMP] = { .len = sizeof(struct ovs_key_icmp) },
|
|
|
|
|
[OVS_KEY_ATTR_ICMPV6] = { .len = sizeof(struct ovs_key_icmpv6) },
|
|
|
|
|
[OVS_KEY_ATTR_ARP] = { .len = sizeof(struct ovs_key_arp) },
|
|
|
|
|
[OVS_KEY_ATTR_ND] = { .len = sizeof(struct ovs_key_nd) },
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
[OVS_KEY_ATTR_CT_STATE] = { .len = 4 },
|
|
|
|
|
[OVS_KEY_ATTR_CT_ZONE] = { .len = 2 },
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
[OVS_KEY_ATTR_CT_MARK] = { .len = 4 },
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
[OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct ovs_key_ct_labels) },
|
2015-05-20 11:57:35 -07:00
|
|
|
|
};
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
/* Returns the correct length of the payload for a flow key attribute of the
|
2015-05-20 11:57:35 -07:00
|
|
|
|
* specified 'type', ATTR_LEN_INVALID if 'type' is unknown, ATTR_LEN_VARIABLE
|
|
|
|
|
* if the attribute's payload is variable length, or ATTR_LEN_NESTED if the
|
|
|
|
|
* payload is a nested type. */
|
2011-01-23 18:44:44 -08:00
|
|
|
|
static int
|
2015-05-20 11:57:35 -07:00
|
|
|
|
odp_key_attr_len(const struct attr_len_tbl tbl[], int max_len, uint16_t type)
|
2011-01-23 18:44:44 -08:00
|
|
|
|
{
|
2015-05-20 11:57:35 -07:00
|
|
|
|
if (type > max_len) {
|
|
|
|
|
return ATTR_LEN_INVALID;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
return tbl[type].len;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_generic_odp_key(const struct nlattr *a, struct ds *ds)
|
|
|
|
|
{
|
|
|
|
|
size_t len = nl_attr_get_size(a);
|
|
|
|
|
if (len) {
|
|
|
|
|
const uint8_t *unspec;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
unspec = nl_attr_get(a);
|
|
|
|
|
for (i = 0; i < len; i++) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (i) {
|
|
|
|
|
ds_put_char(ds, ' ');
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
ds_put_format(ds, "%02x", unspec[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static const char *
|
|
|
|
|
ovs_frag_type_to_string(enum ovs_frag_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_FRAG_TYPE_NONE:
|
|
|
|
|
return "no";
|
|
|
|
|
case OVS_FRAG_TYPE_FIRST:
|
|
|
|
|
return "first";
|
|
|
|
|
case OVS_FRAG_TYPE_LATER:
|
|
|
|
|
return "later";
|
|
|
|
|
case __OVS_FRAG_TYPE_MAX:
|
|
|
|
|
default:
|
|
|
|
|
return "<error>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-19 13:54:13 -07:00
|
|
|
|
static enum odp_key_fitness
|
2016-04-19 18:36:04 -07:00
|
|
|
|
odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,
|
|
|
|
|
struct flow_tnl *tun)
|
2013-01-18 18:10:59 -08:00
|
|
|
|
{
|
|
|
|
|
unsigned int left;
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
bool ttl = false;
|
|
|
|
|
bool unknown = false;
|
|
|
|
|
|
|
|
|
|
NL_NESTED_FOR_EACH(a, left, attr) {
|
|
|
|
|
uint16_t type = nl_attr_type(a);
|
|
|
|
|
size_t len = nl_attr_get_size(a);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
int expected_len = odp_key_attr_len(ovs_tun_key_attr_lens,
|
|
|
|
|
OVS_TUNNEL_ATTR_MAX, type);
|
2013-01-18 18:10:59 -08:00
|
|
|
|
|
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_ID:
|
|
|
|
|
tun->tun_id = nl_attr_get_be64(a);
|
|
|
|
|
tun->flags |= FLOW_TNL_F_KEY;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
|
|
|
|
|
tun->ip_src = nl_attr_get_be32(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
|
|
|
|
|
tun->ip_dst = nl_attr_get_be32(a);
|
|
|
|
|
break;
|
2015-11-25 11:31:11 -02:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV6_SRC:
|
|
|
|
|
tun->ipv6_src = nl_attr_get_in6_addr(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV6_DST:
|
|
|
|
|
tun->ipv6_dst = nl_attr_get_in6_addr(a);
|
|
|
|
|
break;
|
2013-01-18 18:10:59 -08:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TOS:
|
|
|
|
|
tun->ip_tos = nl_attr_get_u8(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TTL:
|
|
|
|
|
tun->ip_ttl = nl_attr_get_u8(a);
|
|
|
|
|
ttl = true;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_CSUM:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_CSUM;
|
|
|
|
|
break;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_SRC:
|
|
|
|
|
tun->tp_src = nl_attr_get_be16(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_DST:
|
|
|
|
|
tun->tp_dst = nl_attr_get_be16(a);
|
|
|
|
|
break;
|
2014-05-27 21:50:35 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_OAM:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_OAM;
|
|
|
|
|
break;
|
2015-02-14 15:13:17 +01:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: {
|
|
|
|
|
static const struct nl_policy vxlan_opts_policy[] = {
|
|
|
|
|
[OVS_VXLAN_EXT_GBP] = { .type = NL_A_U32 },
|
|
|
|
|
};
|
|
|
|
|
struct nlattr *ext[ARRAY_SIZE(vxlan_opts_policy)];
|
|
|
|
|
|
|
|
|
|
if (!nl_parse_nested(a, vxlan_opts_policy, ext, ARRAY_SIZE(ext))) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ext[OVS_VXLAN_EXT_GBP]) {
|
|
|
|
|
uint32_t gbp = nl_attr_get_u32(ext[OVS_VXLAN_EXT_GBP]);
|
|
|
|
|
|
|
|
|
|
tun->gbp_id = htons(gbp & 0xFFFF);
|
|
|
|
|
tun->gbp_flags = (gbp >> 16) & 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
tunnel: Geneve TLV handling support for OpenFlow.
The current support for Geneve in OVS is exactly equivalent to VXLAN:
it is possible to set and match on the VNI but not on any options
contained in the header. This patch enables the use of options.
The goal for Geneve support is not to add support for any particular option
but to allow end users or controllers to specify what they would like to
match. That is, the full range of Geneve's capabilities should be exposed
without modifying the code (the one exception being options that require
per-packet computation in the fast path).
The main issue with supporting Geneve options is how to integrate the
fields into the existing OpenFlow pipeline. All existing operations
are referred to by their NXM/OXM field name - matches, action generation,
arithmetic operations (i.e. tranfer to a register). However, the Geneve
option space is exactly the same as the OXM space, so a direct mapping
is not feasible. Instead, we create a pool of 64 NXMs that are then
dynamically mapped on Geneve option TLVs using OpenFlow. Once mapped,
these fields become first-class citizens in the OpenFlow pipeline.
An example of how to use Geneve options:
ovs-ofctl add-geneve-map br0 {class=0xffff,type=0,len=4}->tun_metadata0
ovs-ofctl add-flow br0 in_port=LOCAL,actions=set_field:0xffffffff->tun_metadata0,1
This will add a 4 bytes option (filled will all 1's) to all packets
coming from the LOCAL port and then send then out to port 1.
A limitation of this patch is that although the option table is specified
for a particular switch over OpenFlow, it is currently global to all
switches. This will be addressed in a future patch.
Based on work originally done by Madhu Challa. Ben Pfaff also significantly
improved the comments.
Signed-off-by: Madhu Challa <challa@noironetworks.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-04-30 18:09:57 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
|
2016-04-19 18:36:04 -07:00
|
|
|
|
tun_metadata_from_geneve_nlattr(a, is_mask, tun);
|
2014-06-05 19:07:32 -07:00
|
|
|
|
break;
|
tunnel: Geneve TLV handling support for OpenFlow.
The current support for Geneve in OVS is exactly equivalent to VXLAN:
it is possible to set and match on the VNI but not on any options
contained in the header. This patch enables the use of options.
The goal for Geneve support is not to add support for any particular option
but to allow end users or controllers to specify what they would like to
match. That is, the full range of Geneve's capabilities should be exposed
without modifying the code (the one exception being options that require
per-packet computation in the fast path).
The main issue with supporting Geneve options is how to integrate the
fields into the existing OpenFlow pipeline. All existing operations
are referred to by their NXM/OXM field name - matches, action generation,
arithmetic operations (i.e. tranfer to a register). However, the Geneve
option space is exactly the same as the OXM space, so a direct mapping
is not feasible. Instead, we create a pool of 64 NXMs that are then
dynamically mapped on Geneve option TLVs using OpenFlow. Once mapped,
these fields become first-class citizens in the OpenFlow pipeline.
An example of how to use Geneve options:
ovs-ofctl add-geneve-map br0 {class=0xffff,type=0,len=4}->tun_metadata0
ovs-ofctl add-flow br0 in_port=LOCAL,actions=set_field:0xffffffff->tun_metadata0,1
This will add a 4 bytes option (filled will all 1's) to all packets
coming from the LOCAL port and then send then out to port 1.
A limitation of this patch is that although the option table is specified
for a particular switch over OpenFlow, it is currently global to all
switches. This will be addressed in a future patch.
Based on work originally done by Madhu Challa. Ben Pfaff also significantly
improved the comments.
Signed-off-by: Madhu Challa <challa@noironetworks.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-04-30 18:09:57 -07:00
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
default:
|
|
|
|
|
/* Allow this to show up as unexpected, if there are unknown
|
|
|
|
|
* tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
|
|
|
|
|
unknown = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ttl) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (unknown) {
|
2013-12-30 15:58:58 -08:00
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
2013-01-18 18:10:59 -08:00
|
|
|
|
}
|
|
|
|
|
return ODP_FIT_PERFECT;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-19 13:54:13 -07:00
|
|
|
|
enum odp_key_fitness
|
2016-04-19 18:36:04 -07:00
|
|
|
|
odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
|
2015-06-19 13:54:13 -07:00
|
|
|
|
{
|
2015-06-30 19:19:40 -07:00
|
|
|
|
memset(tun, 0, sizeof *tun);
|
2016-04-19 18:36:04 -07:00
|
|
|
|
return odp_tun_key_from_attr__(attr, false, tun);
|
2015-06-19 13:54:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
static void
|
2015-06-19 13:54:13 -07:00
|
|
|
|
tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
|
tunnel: Geneve TLV handling support for OpenFlow.
The current support for Geneve in OVS is exactly equivalent to VXLAN:
it is possible to set and match on the VNI but not on any options
contained in the header. This patch enables the use of options.
The goal for Geneve support is not to add support for any particular option
but to allow end users or controllers to specify what they would like to
match. That is, the full range of Geneve's capabilities should be exposed
without modifying the code (the one exception being options that require
per-packet computation in the fast path).
The main issue with supporting Geneve options is how to integrate the
fields into the existing OpenFlow pipeline. All existing operations
are referred to by their NXM/OXM field name - matches, action generation,
arithmetic operations (i.e. tranfer to a register). However, the Geneve
option space is exactly the same as the OXM space, so a direct mapping
is not feasible. Instead, we create a pool of 64 NXMs that are then
dynamically mapped on Geneve option TLVs using OpenFlow. Once mapped,
these fields become first-class citizens in the OpenFlow pipeline.
An example of how to use Geneve options:
ovs-ofctl add-geneve-map br0 {class=0xffff,type=0,len=4}->tun_metadata0
ovs-ofctl add-flow br0 in_port=LOCAL,actions=set_field:0xffffffff->tun_metadata0,1
This will add a 4 bytes option (filled will all 1's) to all packets
coming from the LOCAL port and then send then out to port 1.
A limitation of this patch is that although the option table is specified
for a particular switch over OpenFlow, it is currently global to all
switches. This will be addressed in a future patch.
Based on work originally done by Madhu Challa. Ben Pfaff also significantly
improved the comments.
Signed-off-by: Madhu Challa <challa@noironetworks.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-04-30 18:09:57 -07:00
|
|
|
|
const struct flow_tnl *tun_flow_key,
|
|
|
|
|
const struct ofpbuf *key_buf)
|
2013-01-18 18:10:59 -08:00
|
|
|
|
{
|
|
|
|
|
size_t tun_key_ofs;
|
|
|
|
|
|
|
|
|
|
tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
|
|
|
|
|
|
2014-03-01 17:11:02 -08:00
|
|
|
|
/* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */
|
|
|
|
|
if (tun_key->tun_id || tun_key->flags & FLOW_TNL_F_KEY) {
|
2013-01-18 18:10:59 -08:00
|
|
|
|
nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->ip_src) {
|
|
|
|
|
nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->ip_dst) {
|
|
|
|
|
nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
|
|
|
|
|
}
|
2015-11-25 11:31:11 -02:00
|
|
|
|
if (ipv6_addr_is_set(&tun_key->ipv6_src)) {
|
|
|
|
|
nl_msg_put_in6_addr(a, OVS_TUNNEL_KEY_ATTR_IPV6_SRC, &tun_key->ipv6_src);
|
|
|
|
|
}
|
|
|
|
|
if (ipv6_addr_is_set(&tun_key->ipv6_dst)) {
|
|
|
|
|
nl_msg_put_in6_addr(a, OVS_TUNNEL_KEY_ATTR_IPV6_DST, &tun_key->ipv6_dst);
|
|
|
|
|
}
|
2013-01-18 18:10:59 -08:00
|
|
|
|
if (tun_key->ip_tos) {
|
|
|
|
|
nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
|
|
|
|
|
}
|
|
|
|
|
nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl);
|
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_CSUM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
|
|
|
|
|
}
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (tun_key->tp_src) {
|
|
|
|
|
nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_SRC, tun_key->tp_src);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->tp_dst) {
|
|
|
|
|
nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_DST, tun_key->tp_dst);
|
|
|
|
|
}
|
2014-05-27 21:50:35 -07:00
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_OAM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM);
|
|
|
|
|
}
|
2015-02-14 15:13:17 +01:00
|
|
|
|
if (tun_key->gbp_flags || tun_key->gbp_id) {
|
|
|
|
|
size_t vxlan_opts_ofs;
|
|
|
|
|
|
|
|
|
|
vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
|
|
|
|
|
nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP,
|
|
|
|
|
(tun_key->gbp_flags << 16) | ntohs(tun_key->gbp_id));
|
|
|
|
|
nl_msg_end_nested(a, vxlan_opts_ofs);
|
|
|
|
|
}
|
2015-06-29 18:01:59 -07:00
|
|
|
|
tun_metadata_to_geneve_nlattr(tun_key, tun_flow_key, key_buf, a);
|
tunnel: Geneve TLV handling support for OpenFlow.
The current support for Geneve in OVS is exactly equivalent to VXLAN:
it is possible to set and match on the VNI but not on any options
contained in the header. This patch enables the use of options.
The goal for Geneve support is not to add support for any particular option
but to allow end users or controllers to specify what they would like to
match. That is, the full range of Geneve's capabilities should be exposed
without modifying the code (the one exception being options that require
per-packet computation in the fast path).
The main issue with supporting Geneve options is how to integrate the
fields into the existing OpenFlow pipeline. All existing operations
are referred to by their NXM/OXM field name - matches, action generation,
arithmetic operations (i.e. tranfer to a register). However, the Geneve
option space is exactly the same as the OXM space, so a direct mapping
is not feasible. Instead, we create a pool of 64 NXMs that are then
dynamically mapped on Geneve option TLVs using OpenFlow. Once mapped,
these fields become first-class citizens in the OpenFlow pipeline.
An example of how to use Geneve options:
ovs-ofctl add-geneve-map br0 {class=0xffff,type=0,len=4}->tun_metadata0
ovs-ofctl add-flow br0 in_port=LOCAL,actions=set_field:0xffffffff->tun_metadata0,1
This will add a 4 bytes option (filled will all 1's) to all packets
coming from the LOCAL port and then send then out to port 1.
A limitation of this patch is that although the option table is specified
for a particular switch over OpenFlow, it is currently global to all
switches. This will be addressed in a future patch.
Based on work originally done by Madhu Challa. Ben Pfaff also significantly
improved the comments.
Signed-off-by: Madhu Challa <challa@noironetworks.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-04-30 18:09:57 -07:00
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
nl_msg_end_nested(a, tun_key_ofs);
|
2012-11-14 21:10:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-03 12:23:14 -07:00
|
|
|
|
static bool
|
|
|
|
|
odp_mask_attr_is_wildcard(const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
return is_all_zeros(nl_attr_get(ma), nl_attr_get_size(ma));
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static bool
|
2014-09-05 16:00:49 -07:00
|
|
|
|
odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
|
2013-06-19 07:15:10 +00:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (attr == OVS_KEY_ATTR_TCP_FLAGS) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return TCP_FLAGS(*(ovs_be16 *)mask) == TCP_FLAGS(OVS_BE16_MAX);
|
|
|
|
|
}
|
|
|
|
|
if (attr == OVS_KEY_ATTR_IPV6) {
|
|
|
|
|
const struct ovs_key_ipv6 *ipv6_mask = mask;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return
|
|
|
|
|
((ipv6_mask->ipv6_label & htonl(IPV6_LABEL_MASK))
|
2014-09-05 15:44:20 -07:00
|
|
|
|
== htonl(IPV6_LABEL_MASK))
|
2014-09-05 16:00:49 -07:00
|
|
|
|
&& ipv6_mask->ipv6_proto == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_tclass == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_hlimit == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_frag == UINT8_MAX
|
2017-01-04 16:10:56 -08:00
|
|
|
|
&& ipv6_mask_is_exact(&ipv6_mask->ipv6_src)
|
|
|
|
|
&& ipv6_mask_is_exact(&ipv6_mask->ipv6_dst);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
if (attr == OVS_KEY_ATTR_TUNNEL) {
|
2015-06-09 10:29:43 -07:00
|
|
|
|
return false;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attr == OVS_KEY_ATTR_ARP) {
|
|
|
|
|
/* ARP key has padding, ignore it. */
|
|
|
|
|
BUILD_ASSERT_DECL(sizeof(struct ovs_key_arp) == 24);
|
|
|
|
|
BUILD_ASSERT_DECL(offsetof(struct ovs_key_arp, arp_tha) == 10 + 6);
|
|
|
|
|
size = offsetof(struct ovs_key_arp, arp_tha) + ETH_ADDR_LEN;
|
|
|
|
|
ovs_assert(((uint16_t *)mask)[size/2] == 0);
|
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return is_all_ones(mask, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
odp_mask_attr_is_exact(const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
enum ovs_key_attr attr = nl_attr_type(ma);
|
|
|
|
|
const void *mask;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
if (attr == OVS_KEY_ATTR_TUNNEL) {
|
2015-06-09 10:29:43 -07:00
|
|
|
|
return false;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
} else {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask = nl_attr_get(ma);
|
|
|
|
|
size = nl_attr_get_size(ma);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return odp_mask_is_exact(attr, mask, size);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-23 22:58:46 -07:00
|
|
|
|
void
|
|
|
|
|
odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no,
|
|
|
|
|
char *port_name)
|
|
|
|
|
{
|
|
|
|
|
struct odp_portno_names *odp_portno_names;
|
|
|
|
|
|
|
|
|
|
odp_portno_names = xmalloc(sizeof *odp_portno_names);
|
|
|
|
|
odp_portno_names->port_no = port_no;
|
|
|
|
|
odp_portno_names->name = xstrdup(port_name);
|
|
|
|
|
hmap_insert(portno_names, &odp_portno_names->hmap_node,
|
|
|
|
|
hash_odp_port(port_no));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
odp_portno_names_get(const struct hmap *portno_names, odp_port_t port_no)
|
|
|
|
|
{
|
|
|
|
|
struct odp_portno_names *odp_portno_names;
|
|
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_IN_BUCKET (odp_portno_names, hmap_node,
|
|
|
|
|
hash_odp_port(port_no), portno_names) {
|
|
|
|
|
if (odp_portno_names->port_no == port_no) {
|
|
|
|
|
return odp_portno_names->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_portno_names_destroy(struct hmap *portno_names)
|
|
|
|
|
{
|
2016-04-06 18:53:59 -07:00
|
|
|
|
struct odp_portno_names *odp_portno_names;
|
|
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_POP (odp_portno_names, hmap_node, portno_names) {
|
2013-09-23 22:58:46 -07:00
|
|
|
|
free(odp_portno_names->name);
|
|
|
|
|
free(odp_portno_names);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Format helpers. */
|
|
|
|
|
|
|
|
|
|
static void
|
2015-08-28 14:55:11 -07:00
|
|
|
|
format_eth(struct ds *ds, const char *name, const struct eth_addr key,
|
|
|
|
|
const struct eth_addr *mask, bool verbose)
|
2014-09-09 14:50:36 -07:00
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && eth_addr_is_zero(*mask);
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || eth_mask_is_exact(*mask);
|
|
|
|
|
|
|
|
|
|
if (mask_full) {
|
|
|
|
|
ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, ETH_ADDR_ARGS(key));
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%s=", name);
|
2015-08-28 14:55:11 -07:00
|
|
|
|
eth_format_masked(key, mask, ds);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_be64(struct ds *ds, const char *name, ovs_be64 key,
|
|
|
|
|
const ovs_be64 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE64_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=0x%"PRIx64, name, ntohll(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx64, ntohll(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_ipv4(struct ds *ds, const char *name, ovs_be32 key,
|
|
|
|
|
const ovs_be32 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE32_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s="IP_FMT, name, IP_ARGS(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/"IP_FMT, IP_ARGS(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2015-11-25 11:31:10 -02:00
|
|
|
|
format_in6_addr(struct ds *ds, const char *name,
|
|
|
|
|
const struct in6_addr *key,
|
|
|
|
|
const struct in6_addr *mask,
|
|
|
|
|
bool verbose)
|
2014-09-09 14:50:36 -07:00
|
|
|
|
{
|
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
bool mask_empty = mask && ipv6_mask_is_any(mask);
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || ipv6_mask_is_exact(mask);
|
|
|
|
|
|
|
|
|
|
inet_ntop(AF_INET6, key, buf, sizeof buf);
|
|
|
|
|
ds_put_format(ds, "%s=%s", name, buf);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
inet_ntop(AF_INET6, mask, buf, sizeof buf);
|
|
|
|
|
ds_put_format(ds, "/%s", buf);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_ipv6_label(struct ds *ds, const char *name, ovs_be32 key,
|
|
|
|
|
const ovs_be32 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask
|
|
|
|
|
|| (*mask & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK);
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%#"PRIx32, name, ntohl(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, ntohl(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_u8x(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%#"PRIx8, name, key);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx8, *mask);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_u8u(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%"PRIu8, name, key);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx8, *mask);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_be16(struct ds *ds, const char *name, ovs_be16 key,
|
|
|
|
|
const ovs_be16 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE16_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%"PRIu16, name, ntohs(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx16, ntohs(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:03:01 -07:00
|
|
|
|
static void
|
|
|
|
|
format_be16x(struct ds *ds, const char *name, ovs_be16 key,
|
|
|
|
|
const ovs_be16 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE16_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%#"PRIx16, name, ntohs(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx16, ntohs(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static void
|
|
|
|
|
format_tun_flags(struct ds *ds, const char *name, uint16_t key,
|
|
|
|
|
const uint16_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
ds_put_cstr(ds, name);
|
|
|
|
|
ds_put_char(ds, '(');
|
2015-07-11 20:48:29 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
format_flags_masked(ds, NULL, flow_tun_flag_to_string, key,
|
|
|
|
|
*mask & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
} else { /* Fully masked. */
|
2015-07-11 20:48:29 -07:00
|
|
|
|
format_flags(ds, flow_tun_flag_to_string, key, '|');
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
static bool
|
|
|
|
|
check_attr_len(struct ds *ds, const struct nlattr *a, const struct nlattr *ma,
|
|
|
|
|
const struct attr_len_tbl tbl[], int max_len, bool need_key)
|
|
|
|
|
{
|
|
|
|
|
int expected_len;
|
|
|
|
|
|
|
|
|
|
expected_len = odp_key_attr_len(tbl, max_len, nl_attr_type(a));
|
|
|
|
|
if (expected_len != ATTR_LEN_VARIABLE &&
|
|
|
|
|
expected_len != ATTR_LEN_NESTED) {
|
|
|
|
|
|
|
|
|
|
bool bad_key_len = nl_attr_get_size(a) != expected_len;
|
|
|
|
|
bool bad_mask_len = ma && nl_attr_get_size(ma) != expected_len;
|
|
|
|
|
|
|
|
|
|
if (bad_key_len || bad_mask_len) {
|
|
|
|
|
if (need_key) {
|
|
|
|
|
ds_put_format(ds, "key%u", nl_attr_type(a));
|
|
|
|
|
}
|
|
|
|
|
if (bad_key_len) {
|
|
|
|
|
ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(",
|
|
|
|
|
nl_attr_get_size(a), expected_len);
|
|
|
|
|
}
|
|
|
|
|
format_generic_odp_key(a, ds);
|
|
|
|
|
if (ma) {
|
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
if (bad_mask_len) {
|
|
|
|
|
ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected %d)(",
|
|
|
|
|
nl_attr_get_size(ma), expected_len);
|
|
|
|
|
}
|
|
|
|
|
format_generic_odp_key(ma, ds);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_unknown_key(struct ds *ds, const struct nlattr *a,
|
|
|
|
|
const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "key%u(", nl_attr_type(a));
|
|
|
|
|
format_generic_odp_key(a, ds);
|
|
|
|
|
if (ma && !odp_mask_attr_is_exact(ma)) {
|
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
format_generic_odp_key(ma, ds);
|
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_odp_tun_vxlan_opt(const struct nlattr *attr,
|
|
|
|
|
const struct nlattr *mask_attr, struct ds *ds,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
unsigned int left;
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
struct ofpbuf ofp;
|
|
|
|
|
|
|
|
|
|
ofpbuf_init(&ofp, 100);
|
|
|
|
|
NL_NESTED_FOR_EACH(a, left, attr) {
|
|
|
|
|
uint16_t type = nl_attr_type(a);
|
|
|
|
|
const struct nlattr *ma = NULL;
|
|
|
|
|
|
|
|
|
|
if (mask_attr) {
|
|
|
|
|
ma = nl_attr_find__(nl_attr_get(mask_attr),
|
|
|
|
|
nl_attr_get_size(mask_attr), type);
|
|
|
|
|
if (!ma) {
|
|
|
|
|
ma = generate_all_wildcard_mask(ovs_vxlan_ext_attr_lens,
|
|
|
|
|
OVS_VXLAN_EXT_MAX,
|
|
|
|
|
&ofp, a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!check_attr_len(ds, a, ma, ovs_vxlan_ext_attr_lens,
|
|
|
|
|
OVS_VXLAN_EXT_MAX, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_VXLAN_EXT_GBP: {
|
|
|
|
|
uint32_t key = nl_attr_get_u32(a);
|
|
|
|
|
ovs_be16 id, id_mask;
|
2016-04-19 12:06:44 +01:00
|
|
|
|
uint8_t flags, flags_mask = 0;
|
2015-05-16 22:08:20 -07:00
|
|
|
|
|
|
|
|
|
id = htons(key & 0xFFFF);
|
|
|
|
|
flags = (key >> 16) & 0xFF;
|
|
|
|
|
if (ma) {
|
|
|
|
|
uint32_t mask = nl_attr_get_u32(ma);
|
|
|
|
|
id_mask = htons(mask & 0xFFFF);
|
|
|
|
|
flags_mask = (mask >> 16) & 0xFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "gbp(");
|
|
|
|
|
format_be16(ds, "id", id, ma ? &id_mask : NULL, verbose);
|
|
|
|
|
format_u8x(ds, "flags", flags, ma ? &flags_mask : NULL, verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
format_unknown_key(ds, a, ma);
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_clear(&ofp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
ofpbuf_uninit(&ofp);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:03:01 -07:00
|
|
|
|
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
|
|
|
|
|
|
|
|
|
|
static void
|
2015-06-22 14:23:37 -07:00
|
|
|
|
format_geneve_opts(const struct geneve_opt *opt,
|
|
|
|
|
const struct geneve_opt *mask, int opts_len,
|
|
|
|
|
struct ds *ds, bool verbose)
|
2015-05-18 16:03:01 -07:00
|
|
|
|
{
|
|
|
|
|
while (opts_len > 0) {
|
|
|
|
|
unsigned int len;
|
|
|
|
|
uint8_t data_len, data_len_mask;
|
|
|
|
|
|
|
|
|
|
if (opts_len < sizeof *opt) {
|
|
|
|
|
ds_put_format(ds, "opt len %u less than minimum %"PRIuSIZE,
|
|
|
|
|
opts_len, sizeof *opt);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data_len = opt->length * 4;
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (mask->length == 0x1f) {
|
|
|
|
|
data_len_mask = UINT8_MAX;
|
|
|
|
|
} else {
|
|
|
|
|
data_len_mask = mask->length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
len = sizeof *opt + data_len;
|
|
|
|
|
if (len > opts_len) {
|
|
|
|
|
ds_put_format(ds, "opt len %u greater than remaining %u",
|
|
|
|
|
len, opts_len);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_char(ds, '{');
|
|
|
|
|
format_be16x(ds, "class", opt->opt_class, MASK(mask, opt_class),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8x(ds, "type", opt->type, MASK(mask, type), verbose);
|
|
|
|
|
format_u8u(ds, "len", data_len, mask ? &data_len_mask : NULL, verbose);
|
2015-08-11 18:41:37 -07:00
|
|
|
|
if (data_len &&
|
|
|
|
|
(verbose || !mask || !is_all_zeros(mask + 1, data_len))) {
|
2015-05-18 16:03:01 -07:00
|
|
|
|
ds_put_hex(ds, opt + 1, data_len);
|
|
|
|
|
if (mask && !is_all_ones(mask + 1, data_len)) {
|
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
ds_put_hex(ds, mask + 1, data_len);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, '}');
|
|
|
|
|
|
|
|
|
|
opt += len / sizeof(*opt);
|
|
|
|
|
if (mask) {
|
|
|
|
|
mask += len / sizeof(*opt);
|
|
|
|
|
}
|
|
|
|
|
opts_len -= len;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-22 14:23:37 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_tun_geneve(const struct nlattr *attr,
|
|
|
|
|
const struct nlattr *mask_attr, struct ds *ds,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
|
|
|
|
int opts_len = nl_attr_get_size(attr);
|
|
|
|
|
const struct geneve_opt *opt = nl_attr_get(attr);
|
|
|
|
|
const struct geneve_opt *mask = mask_attr ?
|
|
|
|
|
nl_attr_get(mask_attr) : NULL;
|
|
|
|
|
|
|
|
|
|
if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
|
|
|
|
|
ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
|
|
|
|
|
nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
format_geneve_opts(opt, mask, opts_len, ds, verbose);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
|
|
|
|
|
struct ds *ds, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
unsigned int left;
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
uint16_t flags = 0;
|
|
|
|
|
uint16_t mask_flags = 0;
|
|
|
|
|
struct ofpbuf ofp;
|
|
|
|
|
|
|
|
|
|
ofpbuf_init(&ofp, 100);
|
|
|
|
|
NL_NESTED_FOR_EACH(a, left, attr) {
|
|
|
|
|
enum ovs_tunnel_key_attr type = nl_attr_type(a);
|
|
|
|
|
const struct nlattr *ma = NULL;
|
|
|
|
|
|
|
|
|
|
if (mask_attr) {
|
|
|
|
|
ma = nl_attr_find__(nl_attr_get(mask_attr),
|
|
|
|
|
nl_attr_get_size(mask_attr), type);
|
|
|
|
|
if (!ma) {
|
|
|
|
|
ma = generate_all_wildcard_mask(ovs_tun_key_attr_lens,
|
|
|
|
|
OVS_TUNNEL_KEY_ATTR_MAX,
|
|
|
|
|
&ofp, a);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!check_attr_len(ds, a, ma, ovs_tun_key_attr_lens,
|
|
|
|
|
OVS_TUNNEL_KEY_ATTR_MAX, true)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_ID:
|
|
|
|
|
format_be64(ds, "tun_id", nl_attr_get_be64(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
flags |= FLOW_TNL_F_KEY;
|
|
|
|
|
if (ma) {
|
|
|
|
|
mask_flags |= FLOW_TNL_F_KEY;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
|
|
|
|
|
format_ipv4(ds, "src", nl_attr_get_be32(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
|
|
|
|
|
format_ipv4(ds, "dst", nl_attr_get_be32(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
2015-11-25 11:31:11 -02:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: {
|
|
|
|
|
struct in6_addr ipv6_src;
|
|
|
|
|
ipv6_src = nl_attr_get_in6_addr(a);
|
|
|
|
|
format_in6_addr(ds, "ipv6_src", &ipv6_src,
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV6_DST: {
|
|
|
|
|
struct in6_addr ipv6_dst;
|
|
|
|
|
ipv6_dst = nl_attr_get_in6_addr(a);
|
|
|
|
|
format_in6_addr(ds, "ipv6_dst", &ipv6_dst,
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-05-16 22:08:20 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TOS:
|
|
|
|
|
format_u8x(ds, "tos", nl_attr_get_u8(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TTL:
|
|
|
|
|
format_u8u(ds, "ttl", nl_attr_get_u8(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
|
|
|
|
|
flags |= FLOW_TNL_F_DONT_FRAGMENT;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_CSUM:
|
|
|
|
|
flags |= FLOW_TNL_F_CSUM;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_SRC:
|
|
|
|
|
format_be16(ds, "tp_src", nl_attr_get_be16(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_DST:
|
|
|
|
|
format_be16(ds, "tp_dst", nl_attr_get_be16(a),
|
|
|
|
|
ma ? nl_attr_get(ma) : NULL, verbose);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_OAM:
|
|
|
|
|
flags |= FLOW_TNL_F_OAM;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS:
|
|
|
|
|
ds_put_cstr(ds, "vxlan(");
|
|
|
|
|
format_odp_tun_vxlan_opt(a, ma, ds, verbose);
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
|
2015-05-18 16:03:01 -07:00
|
|
|
|
ds_put_cstr(ds, "geneve(");
|
|
|
|
|
format_odp_tun_geneve(a, ma, ds, verbose);
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
break;
|
2016-07-17 09:52:11 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_PAD:
|
|
|
|
|
break;
|
2015-05-16 22:08:20 -07:00
|
|
|
|
case __OVS_TUNNEL_KEY_ATTR_MAX:
|
|
|
|
|
default:
|
|
|
|
|
format_unknown_key(ds, a, ma);
|
|
|
|
|
}
|
|
|
|
|
ofpbuf_clear(&ofp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Flags can have a valid mask even if the attribute is not set, so
|
|
|
|
|
* we need to collect these separately. */
|
|
|
|
|
if (mask_attr) {
|
|
|
|
|
NL_NESTED_FOR_EACH(a, left, mask_attr) {
|
|
|
|
|
switch (nl_attr_type(a)) {
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
|
|
|
|
|
mask_flags |= FLOW_TNL_F_DONT_FRAGMENT;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_CSUM:
|
|
|
|
|
mask_flags |= FLOW_TNL_F_CSUM;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_OAM:
|
|
|
|
|
mask_flags |= FLOW_TNL_F_OAM;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
format_tun_flags(ds, "flags", flags, mask_attr ? &mask_flags : NULL,
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
|
|
|
|
ofpbuf_uninit(&ofp);
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static const char *
|
|
|
|
|
odp_ct_state_to_string(uint32_t flag)
|
|
|
|
|
{
|
|
|
|
|
switch (flag) {
|
|
|
|
|
case OVS_CS_F_REPLY_DIR:
|
|
|
|
|
return "rpl";
|
|
|
|
|
case OVS_CS_F_TRACKED:
|
|
|
|
|
return "trk";
|
|
|
|
|
case OVS_CS_F_NEW:
|
|
|
|
|
return "new";
|
|
|
|
|
case OVS_CS_F_ESTABLISHED:
|
|
|
|
|
return "est";
|
|
|
|
|
case OVS_CS_F_RELATED:
|
|
|
|
|
return "rel";
|
|
|
|
|
case OVS_CS_F_INVALID:
|
|
|
|
|
return "inv";
|
2015-12-04 15:01:36 -08:00
|
|
|
|
case OVS_CS_F_SRC_NAT:
|
|
|
|
|
return "snat";
|
|
|
|
|
case OVS_CS_F_DST_NAT:
|
|
|
|
|
return "dnat";
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
default:
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static void
|
|
|
|
|
format_frag(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
/* ODP frag is an enumeration field; partial masks are not meaningful. */
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "error: partial mask not supported for frag (%#"
|
|
|
|
|
PRIx8"),", *mask);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%s=%s,", name, ovs_frag_type_to_string(key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static bool
|
|
|
|
|
mask_empty(const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
const void *mask;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
if (!ma) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
mask = nl_attr_get(ma);
|
|
|
|
|
n = nl_attr_get_size(ma);
|
|
|
|
|
|
|
|
|
|
return is_all_zeros(mask, n);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
static void
|
2013-06-19 07:15:10 +00:00
|
|
|
|
format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct hmap *portno_names, struct ds *ds,
|
|
|
|
|
bool verbose)
|
2011-01-23 18:44:44 -08:00
|
|
|
|
{
|
2011-11-07 09:13:53 -08:00
|
|
|
|
enum ovs_key_attr attr = nl_attr_type(a);
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
2013-06-27 22:02:58 -07:00
|
|
|
|
bool is_exact;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
if (!check_attr_len(ds, a, ma, ovs_flow_key_attr_lens,
|
|
|
|
|
OVS_KEY_ATTR_MAX, false)) {
|
|
|
|
|
return;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, '(');
|
2011-11-07 09:13:53 -08:00
|
|
|
|
switch (attr) {
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_ENCAP:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
|
|
|
|
|
odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
|
2013-09-23 22:58:46 -07:00
|
|
|
|
nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds,
|
2013-08-03 12:23:14 -07:00
|
|
|
|
verbose);
|
2013-09-23 22:58:46 -07:00
|
|
|
|
} else if (nl_attr_get_size(a)) {
|
|
|
|
|
odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL,
|
|
|
|
|
ds, verbose);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2011-11-01 10:13:16 -07:00
|
|
|
|
case OVS_KEY_ATTR_PRIORITY:
|
2012-11-13 19:19:36 +02:00
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK:
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_DP_HASH:
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
break;
|
|
|
|
|
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_MARK:
|
|
|
|
|
if (verbose || !mask_empty(ma)) {
|
|
|
|
|
ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
|
|
|
|
|
if (!is_exact) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_STATE:
|
|
|
|
|
if (verbose) {
|
|
|
|
|
ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
|
|
|
|
|
if (!is_exact) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32,
|
|
|
|
|
mask_empty(ma) ? 0 : nl_attr_get_u32(ma));
|
|
|
|
|
}
|
|
|
|
|
} else if (!is_exact) {
|
|
|
|
|
format_flags_masked(ds, NULL, odp_ct_state_to_string,
|
|
|
|
|
nl_attr_get_u32(a),
|
|
|
|
|
mask_empty(ma) ? 0 : nl_attr_get_u32(ma),
|
|
|
|
|
UINT32_MAX);
|
|
|
|
|
} else {
|
|
|
|
|
format_flags(ds, odp_ct_state_to_string, nl_attr_get_u32(a), '|');
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_CT_ZONE:
|
|
|
|
|
if (verbose || !mask_empty(ma)) {
|
|
|
|
|
ds_put_format(ds, "%#"PRIx16, nl_attr_get_u16(a));
|
|
|
|
|
if (!is_exact) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx16, nl_attr_get_u16(ma));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_LABELS: {
|
|
|
|
|
const ovs_u128 *value = nl_attr_get(a);
|
|
|
|
|
const ovs_u128 *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
|
|
|
|
|
format_u128(ds, value, mask, verbose);
|
|
|
|
|
break;
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL:
|
|
|
|
|
format_odp_tun_attr(a, ma, ds, verbose);
|
2012-10-20 12:15:24 -07:00
|
|
|
|
break;
|
2015-05-16 22:08:20 -07:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_IN_PORT:
|
2013-09-23 22:58:46 -07:00
|
|
|
|
if (portno_names && verbose && is_exact) {
|
|
|
|
|
char *name = odp_portno_names_get(portno_names,
|
2016-11-14 13:29:05 -08:00
|
|
|
|
nl_attr_get_odp_port(a));
|
2013-09-23 22:58:46 -07:00
|
|
|
|
if (name) {
|
|
|
|
|
ds_put_format(ds, "%s", name);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
|
|
|
|
if (!is_exact) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_ETHERNET: {
|
|
|
|
|
const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_ethernet *key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_eth(ds, "src", key->eth_src, MASK(mask, eth_src), verbose);
|
|
|
|
|
format_eth(ds, "dst", key->eth_dst, MASK(mask, eth_dst), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_VLAN:
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(ds, nl_attr_get_be16(a),
|
|
|
|
|
ma ? nl_attr_get_be16(ma) : OVS_BE16_MAX, verbose);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_KEY_ATTR_MPLS: {
|
|
|
|
|
const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
const struct ovs_key_mpls *mpls_mask = NULL;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
size_t size = nl_attr_get_size(a);
|
|
|
|
|
|
|
|
|
|
if (!size || size % sizeof *mpls_key) {
|
2014-09-05 15:44:19 -07:00
|
|
|
|
ds_put_format(ds, "(bad key length %"PRIuSIZE")", size);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
mpls_mask = nl_attr_get(ma);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (size != nl_attr_get_size(ma)) {
|
2014-02-04 10:32:35 -08:00
|
|
|
|
ds_put_format(ds, "(key length %"PRIuSIZE" != "
|
|
|
|
|
"mask length %"PRIuSIZE")",
|
2014-09-05 15:44:19 -07:00
|
|
|
|
size, nl_attr_get_size(ma));
|
2014-02-04 10:32:35 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_IPV4: {
|
|
|
|
|
const struct ovs_key_ipv4 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_ipv4 *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
|
|
|
|
|
format_ipv4(ds, "src", key->ipv4_src, MASK(mask, ipv4_src), verbose);
|
|
|
|
|
format_ipv4(ds, "dst", key->ipv4_dst, MASK(mask, ipv4_dst), verbose);
|
|
|
|
|
format_u8u(ds, "proto", key->ipv4_proto, MASK(mask, ipv4_proto),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8x(ds, "tos", key->ipv4_tos, MASK(mask, ipv4_tos), verbose);
|
|
|
|
|
format_u8u(ds, "ttl", key->ipv4_ttl, MASK(mask, ipv4_ttl), verbose);
|
|
|
|
|
format_frag(ds, "frag", key->ipv4_frag, MASK(mask, ipv4_frag),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_IPV6: {
|
|
|
|
|
const struct ovs_key_ipv6 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_ipv6 *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
|
2017-01-04 16:10:56 -08:00
|
|
|
|
format_in6_addr(ds, "src", &key->ipv6_src, MASK(mask, ipv6_src),
|
|
|
|
|
verbose);
|
|
|
|
|
format_in6_addr(ds, "dst", &key->ipv6_dst, MASK(mask, ipv6_dst),
|
|
|
|
|
verbose);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_ipv6_label(ds, "label", key->ipv6_label, MASK(mask, ipv6_label),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8u(ds, "proto", key->ipv6_proto, MASK(mask, ipv6_proto),
|
2017-01-04 16:10:56 -08:00
|
|
|
|
verbose);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8x(ds, "tclass", key->ipv6_tclass, MASK(mask, ipv6_tclass),
|
2017-01-04 16:10:56 -08:00
|
|
|
|
verbose);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8u(ds, "hlimit", key->ipv6_hlimit, MASK(mask, ipv6_hlimit),
|
2017-01-04 16:10:56 -08:00
|
|
|
|
verbose);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_frag(ds, "frag", key->ipv6_frag, MASK(mask, ipv6_frag),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2010-12-29 19:03:46 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
/* These have the same structure and format. */
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP:
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_UDP:
|
|
|
|
|
case OVS_KEY_ATTR_SCTP: {
|
|
|
|
|
const struct ovs_key_tcp *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_tcp *mask = ma ? nl_attr_get(ma) : NULL;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_be16(ds, "src", key->tcp_src, MASK(mask, tcp_src), verbose);
|
|
|
|
|
format_be16(ds, "dst", key->tcp_dst, MASK(mask, tcp_dst), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2013-10-28 13:54:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS:
|
|
|
|
|
if (!is_exact) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
format_flags_masked(ds, NULL, packet_tcp_flag_to_string,
|
|
|
|
|
ntohs(nl_attr_get_be16(a)),
|
2015-07-11 20:48:29 -07:00
|
|
|
|
TCP_FLAGS(nl_attr_get_be16(ma)),
|
|
|
|
|
TCP_FLAGS(OVS_BE16_MAX));
|
2014-09-05 15:44:20 -07:00
|
|
|
|
} else {
|
|
|
|
|
format_flags(ds, packet_tcp_flag_to_string,
|
2015-07-11 20:48:29 -07:00
|
|
|
|
ntohs(nl_attr_get_be16(a)), '|');
|
2013-10-28 13:54:40 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_ICMP: {
|
|
|
|
|
const struct ovs_key_icmp *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_icmp *mask = ma ? nl_attr_get(ma) : NULL;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8u(ds, "type", key->icmp_type, MASK(mask, icmp_type), verbose);
|
|
|
|
|
format_u8u(ds, "code", key->icmp_code, MASK(mask, icmp_code), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_ICMPV6: {
|
|
|
|
|
const struct ovs_key_icmpv6 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_icmpv6 *mask = ma ? nl_attr_get(ma) : NULL;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8u(ds, "type", key->icmpv6_type, MASK(mask, icmpv6_type),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8u(ds, "code", key->icmpv6_code, MASK(mask, icmpv6_code),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2010-12-29 19:03:46 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_ARP: {
|
|
|
|
|
const struct ovs_key_arp *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_arp *key = nl_attr_get(a);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_ipv4(ds, "sip", key->arp_sip, MASK(mask, arp_sip), verbose);
|
|
|
|
|
format_ipv4(ds, "tip", key->arp_tip, MASK(mask, arp_tip), verbose);
|
|
|
|
|
format_be16(ds, "op", key->arp_op, MASK(mask, arp_op), verbose);
|
|
|
|
|
format_eth(ds, "sha", key->arp_sha, MASK(mask, arp_sha), verbose);
|
|
|
|
|
format_eth(ds, "tha", key->arp_tha, MASK(mask, arp_tha), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ND: {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
const struct ovs_key_nd *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_nd *key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2017-01-04 16:10:56 -08:00
|
|
|
|
format_in6_addr(ds, "target", &key->nd_target, MASK(mask, nd_target),
|
|
|
|
|
verbose);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_eth(ds, "sll", key->nd_sll, MASK(mask, nd_sll), verbose);
|
|
|
|
|
format_eth(ds, "tll", key->nd_tll, MASK(mask, nd_tll), verbose);
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
ds_chomp(ds, ',');
|
2011-02-01 22:54:11 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-11-07 09:13:53 -08:00
|
|
|
|
case OVS_KEY_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
2011-01-23 18:44:44 -08:00
|
|
|
|
default:
|
|
|
|
|
format_generic_odp_key(a, ds);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
format_generic_odp_key(ma, ds);
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, ')');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
static struct nlattr *
|
2015-05-20 11:57:35 -07:00
|
|
|
|
generate_all_wildcard_mask(const struct attr_len_tbl tbl[], int max,
|
|
|
|
|
struct ofpbuf *ofp, const struct nlattr *key)
|
2013-06-27 22:02:58 -07:00
|
|
|
|
{
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
|
|
|
|
int type = nl_attr_type(key);
|
|
|
|
|
int size = nl_attr_get_size(key);
|
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
if (odp_key_attr_len(tbl, max, type) != ATTR_LEN_NESTED) {
|
2013-10-07 14:11:40 -07:00
|
|
|
|
nl_msg_put_unspec_zero(ofp, type, size);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
} else {
|
|
|
|
|
size_t nested_mask;
|
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
if (tbl[type].next) {
|
|
|
|
|
tbl = tbl[type].next;
|
|
|
|
|
max = tbl[type].next_max;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
nested_mask = nl_msg_start_nested(ofp, type);
|
|
|
|
|
NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) {
|
2015-05-20 11:57:35 -07:00
|
|
|
|
generate_all_wildcard_mask(tbl, max, ofp, nl_attr_get(a));
|
2013-06-27 22:02:58 -07:00
|
|
|
|
}
|
|
|
|
|
nl_msg_end_nested(ofp, nested_mask);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-02 17:29:44 -08:00
|
|
|
|
return ofp->base;
|
2013-06-27 22:02:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
static void
|
|
|
|
|
format_u128(struct ds *ds, const ovs_u128 *key, const ovs_u128 *mask,
|
|
|
|
|
bool verbose)
|
|
|
|
|
{
|
2016-05-03 18:20:51 -07:00
|
|
|
|
if (verbose || (mask && !ovs_u128_is_zero(*mask))) {
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
ovs_be128 value;
|
|
|
|
|
|
2015-10-31 03:12:38 -07:00
|
|
|
|
value = hton128(*key);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
ds_put_hex(ds, &value, sizeof value);
|
2016-05-03 18:20:51 -07:00
|
|
|
|
if (mask && !(ovs_u128_is_ones(*mask))) {
|
2015-10-31 03:12:38 -07:00
|
|
|
|
value = hton128(*mask);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
ds_put_hex(ds, &value, sizeof value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-31 04:45:28 -07:00
|
|
|
|
/* Read the string from 's_' as a 128-bit value. If the string contains
|
|
|
|
|
* a "/", the rest of the string will be treated as a 128-bit mask.
|
|
|
|
|
*
|
|
|
|
|
* If either the value or mask is larger than 64 bits, the string must
|
|
|
|
|
* be in hexadecimal.
|
|
|
|
|
*/
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u128(const char *s_, ovs_u128 *value, ovs_u128 *mask)
|
|
|
|
|
{
|
|
|
|
|
char *s = CONST_CAST(char *, s_);
|
|
|
|
|
ovs_be128 be_value;
|
|
|
|
|
ovs_be128 be_mask;
|
|
|
|
|
|
|
|
|
|
if (!parse_int_string(s, (uint8_t *)&be_value, sizeof be_value, &s)) {
|
2015-10-31 03:12:38 -07:00
|
|
|
|
*value = ntoh128(be_value);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "/%n", &n)) {
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
s += n;
|
|
|
|
|
error = parse_int_string(s, (uint8_t *)&be_mask,
|
|
|
|
|
sizeof be_mask, &s);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
2015-10-31 03:12:38 -07:00
|
|
|
|
*mask = ntoh128(be_mask);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_U128_MAX;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return s - s_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-12 09:49:22 -08:00
|
|
|
|
int
|
|
|
|
|
odp_ufid_from_string(const char *s_, ovs_u128 *ufid)
|
|
|
|
|
{
|
|
|
|
|
const char *s = s_;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "ufid:")) {
|
|
|
|
|
s += 5;
|
|
|
|
|
|
2015-05-29 17:08:45 -07:00
|
|
|
|
if (!uuid_from_string_prefix((struct uuid *)ufid, s)) {
|
2014-11-12 09:49:22 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2015-05-29 17:08:45 -07:00
|
|
|
|
s += UUID_LEN;
|
2014-11-12 09:49:22 -08:00
|
|
|
|
|
|
|
|
|
return s - s_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 16:26:35 +12:00
|
|
|
|
void
|
|
|
|
|
odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
|
|
|
|
|
{
|
2015-05-29 17:08:45 -07:00
|
|
|
|
ds_put_format(ds, "ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid));
|
2014-09-24 16:26:35 +12:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
/* Appends to 'ds' a string representation of the 'key_len' bytes of
|
2013-06-19 07:15:10 +00:00
|
|
|
|
* OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
|
2013-10-14 08:09:17 -07:00
|
|
|
|
* 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
|
|
|
|
|
* non-null and 'verbose' is true, translates odp port number to its name. */
|
2010-10-11 13:31:35 -07:00
|
|
|
|
void
|
2013-06-19 07:15:10 +00:00
|
|
|
|
odp_flow_format(const struct nlattr *key, size_t key_len,
|
|
|
|
|
const struct nlattr *mask, size_t mask_len,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct hmap *portno_names, struct ds *ds, bool verbose)
|
2010-10-11 13:31:35 -07:00
|
|
|
|
{
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (key_len) {
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
bool has_ethtype_key = false;
|
|
|
|
|
const struct nlattr *ma = NULL;
|
2013-06-27 22:02:58 -07:00
|
|
|
|
struct ofpbuf ofp;
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool first_field = true;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_init(&ofp, 100);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
NL_ATTR_FOR_EACH (a, left, key, key_len) {
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool is_nested_attr;
|
|
|
|
|
bool is_wildcard = false;
|
|
|
|
|
int attr_type = nl_attr_type(a);
|
|
|
|
|
|
|
|
|
|
if (attr_type == OVS_KEY_ATTR_ETHERTYPE) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
has_ethtype_key = true;
|
|
|
|
|
}
|
2013-08-03 12:23:14 -07:00
|
|
|
|
|
2015-05-20 11:57:35 -07:00
|
|
|
|
is_nested_attr = odp_key_attr_len(ovs_flow_key_attr_lens,
|
|
|
|
|
OVS_KEY_ATTR_MAX, attr_type) ==
|
|
|
|
|
ATTR_LEN_NESTED;
|
2013-08-03 12:23:14 -07:00
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask && mask_len) {
|
|
|
|
|
ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
|
2013-08-03 12:23:14 -07:00
|
|
|
|
is_wildcard = ma ? odp_mask_attr_is_wildcard(ma) : true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verbose || !is_wildcard || is_nested_attr) {
|
|
|
|
|
if (is_wildcard && !ma) {
|
2015-05-20 11:57:35 -07:00
|
|
|
|
ma = generate_all_wildcard_mask(ovs_flow_key_attr_lens,
|
|
|
|
|
OVS_KEY_ATTR_MAX,
|
|
|
|
|
&ofp, a);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
}
|
2013-08-03 12:23:14 -07:00
|
|
|
|
if (!first_field) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
2013-09-23 22:58:46 -07:00
|
|
|
|
format_odp_key_attr(a, ma, portno_names, ds, verbose);
|
2013-08-03 12:23:14 -07:00
|
|
|
|
first_field = false;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_clear(&ofp);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_uninit(&ofp);
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (left) {
|
2012-01-26 16:21:50 -08:00
|
|
|
|
int i;
|
2013-09-16 17:23:31 -07:00
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (left == key_len) {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2012-01-26 16:21:50 -08:00
|
|
|
|
ds_put_format(ds, ",***%u leftover bytes*** (", left);
|
|
|
|
|
for (i = 0; i < left; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (!has_ethtype_key) {
|
|
|
|
|
ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE);
|
|
|
|
|
if (ma) {
|
|
|
|
|
ds_put_format(ds, ",eth_type(0/0x%04"PRIx16")",
|
|
|
|
|
ntohs(nl_attr_get_be16(ma)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2010-10-11 13:31:35 -07:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
/* Appends to 'ds' a string representation of the 'key_len' bytes of
|
|
|
|
|
* OVS_KEY_ATTR_* attributes in 'key'. */
|
|
|
|
|
void
|
|
|
|
|
odp_flow_key_format(const struct nlattr *key,
|
|
|
|
|
size_t key_len, struct ds *ds)
|
|
|
|
|
{
|
2013-09-23 22:58:46 -07:00
|
|
|
|
odp_flow_format(key, key_len, NULL, 0, NULL, ds, true);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static bool
|
|
|
|
|
ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type)
|
|
|
|
|
{
|
|
|
|
|
if (!strcasecmp(s, "no")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_NONE;
|
|
|
|
|
} else if (!strcasecmp(s, "first")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_FIRST;
|
|
|
|
|
} else if (!strcasecmp(s, "later")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_LATER;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Parsing. */
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2011-08-04 16:20:34 -07:00
|
|
|
|
static int
|
2015-08-28 14:55:11 -07:00
|
|
|
|
scan_eth(const char *s, struct eth_addr *key, struct eth_addr *mask)
|
2011-08-04 16:20:34 -07:00
|
|
|
|
{
|
2014-09-09 14:50:36 -07:00
|
|
|
|
int n;
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2015-08-28 14:55:11 -07:00
|
|
|
|
if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n",
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(*key), &n)) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"ETH_ADDR_SCAN_FMT"%n",
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(*mask), &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
memset(mask, 0xff, sizeof *mask);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-11-01 10:13:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-11-01 10:13:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_ipv4(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(key), &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"IP_SCAN_FMT"%n",
|
|
|
|
|
IP_SCAN_ARGS(mask), &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
2015-11-25 11:31:10 -02:00
|
|
|
|
scan_in6_addr(const char *s, struct in6_addr *key, struct in6_addr *mask)
|
2014-09-09 14:50:36 -07:00
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
char ipv6_s[IPV6_SCAN_LEN + 1];
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, key) == 1) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, mask) == 1) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
memset(mask, 0xff, sizeof *mask);
|
2014-08-26 18:34:52 +02:00
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_ipv6_label(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
int key_, mask_;
|
|
|
|
|
int n;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%i%n", &key_, &n)
|
|
|
|
|
&& (key_ & ~IPV6_LABEL_MASK) == 0) {
|
|
|
|
|
int len = n;
|
2012-11-14 21:10:54 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = htonl(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%i%n", &mask_, &n)
|
|
|
|
|
&& (mask_ & ~IPV6_LABEL_MASK) == 0) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htonl(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = htonl(IPV6_LABEL_MASK);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2012-11-01 15:50:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2012-11-01 15:50:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-11-01 15:50:16 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u8(const char *s, uint8_t *key, uint8_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi8"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi8"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT8_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u16(const char *s, uint16_t *key, uint16_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "%"SCNi16"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi16"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT16_MAX;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u32(const char *s, uint32_t *key, uint32_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-10-26 10:01:32 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-10-26 10:01:32 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-10-26 10:01:32 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be16(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = htons(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htons(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE16_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be64(const char *s, ovs_be64 *key, ovs_be64 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint64_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi64"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
*key = htonll(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi64"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htonll(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE64_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_tun_flags(const char *s, uint16_t *key, uint16_t *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t flags, fmask;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2015-07-11 20:48:29 -07:00
|
|
|
|
n = parse_odp_flags(s, flow_tun_flag_to_string, &flags,
|
|
|
|
|
FLOW_TNL_F_MASK, mask ? &fmask : NULL);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (n >= 0 && s[n] == ')') {
|
|
|
|
|
*key = flags;
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = fmask;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return n + 1;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_tcp_flags(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t flags, fmask;
|
|
|
|
|
int n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2015-07-11 20:48:29 -07:00
|
|
|
|
n = parse_odp_flags(s, packet_tcp_flag_to_string, &flags,
|
|
|
|
|
TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (n >= 0) {
|
|
|
|
|
*key = htons(flags);
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = htons(fmask);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
static uint32_t
|
|
|
|
|
ovs_to_odp_ct_state(uint8_t state)
|
|
|
|
|
{
|
|
|
|
|
uint32_t odp = 0;
|
|
|
|
|
|
|
|
|
|
if (state & CS_NEW) {
|
|
|
|
|
odp |= OVS_CS_F_NEW;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_ESTABLISHED) {
|
|
|
|
|
odp |= OVS_CS_F_ESTABLISHED;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_RELATED) {
|
|
|
|
|
odp |= OVS_CS_F_RELATED;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_INVALID) {
|
|
|
|
|
odp |= OVS_CS_F_INVALID;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_REPLY_DIR) {
|
|
|
|
|
odp |= OVS_CS_F_REPLY_DIR;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_TRACKED) {
|
|
|
|
|
odp |= OVS_CS_F_TRACKED;
|
|
|
|
|
}
|
2015-12-04 15:01:36 -08:00
|
|
|
|
if (state & CS_SRC_NAT) {
|
|
|
|
|
odp |= OVS_CS_F_SRC_NAT;
|
|
|
|
|
}
|
|
|
|
|
if (state & CS_DST_NAT) {
|
|
|
|
|
odp |= OVS_CS_F_DST_NAT;
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
|
|
|
|
return odp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
odp_to_ovs_ct_state(uint32_t flags)
|
|
|
|
|
{
|
|
|
|
|
uint32_t state = 0;
|
|
|
|
|
|
|
|
|
|
if (flags & OVS_CS_F_NEW) {
|
|
|
|
|
state |= CS_NEW;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_ESTABLISHED) {
|
|
|
|
|
state |= CS_ESTABLISHED;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_RELATED) {
|
|
|
|
|
state |= CS_RELATED;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_INVALID) {
|
|
|
|
|
state |= CS_INVALID;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_REPLY_DIR) {
|
|
|
|
|
state |= CS_REPLY_DIR;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_TRACKED) {
|
|
|
|
|
state |= CS_TRACKED;
|
|
|
|
|
}
|
2015-12-04 15:01:36 -08:00
|
|
|
|
if (flags & OVS_CS_F_SRC_NAT) {
|
|
|
|
|
state |= CS_SRC_NAT;
|
|
|
|
|
}
|
|
|
|
|
if (flags & OVS_CS_F_DST_NAT) {
|
|
|
|
|
state |= CS_DST_NAT;
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
scan_ct_state(const char *s, uint32_t *key, uint32_t *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t flags, fmask;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
n = parse_flags(s, odp_ct_state_to_string, ')', NULL, NULL, &flags,
|
|
|
|
|
ovs_to_odp_ct_state(CS_SUPPORTED_MASK),
|
|
|
|
|
mask ? &fmask : NULL);
|
|
|
|
|
|
|
|
|
|
if (n >= 0) {
|
|
|
|
|
*key = flags;
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = fmask;
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_frag(const char *s, uint8_t *key, uint8_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
char frag[8];
|
|
|
|
|
enum ovs_frag_type frag_type;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%7[a-z]%n", frag, &n)
|
|
|
|
|
&& ovs_frag_type_from_string(frag, &frag_type)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = frag_type;
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = UINT8_MAX;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_port(const char *s, uint32_t *key, uint32_t *mask,
|
|
|
|
|
const struct simap *port_names)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
|
|
|
|
} else if (port_names) {
|
|
|
|
|
const struct simap_node *node;
|
|
|
|
|
int len;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
len = strcspn(s, ")");
|
|
|
|
|
node = simap_find_len(port_names, s, len);
|
|
|
|
|
if (node) {
|
|
|
|
|
*key = node->data;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Helper for vlan parsing. */
|
|
|
|
|
struct ovs_key_vlan__ {
|
|
|
|
|
ovs_be16 tci;
|
|
|
|
|
};
|
2013-10-28 13:54:40 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static bool
|
|
|
|
|
set_be16_bf(ovs_be16 *bf, uint8_t bits, uint8_t offset, uint16_t value)
|
|
|
|
|
{
|
|
|
|
|
const uint16_t mask = ((1U << bits) - 1) << offset;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (value >> bits) {
|
|
|
|
|
return false;
|
2013-10-28 13:54:40 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*bf = htons((ntohs(*bf) & ~mask) | (value << offset));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be16_bf(const char *s, ovs_be16 *key, ovs_be16 *mask, uint8_t bits,
|
|
|
|
|
uint8_t offset)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (set_be16_bf(key, bits, offset, key_)) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
|
|
|
|
|
if (!set_be16_bf(mask, bits, offset, mask_)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*mask |= htons(((1U << bits) - 1) << offset);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_vid(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 12, VLAN_VID_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_pcp(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 3, VLAN_PCP_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_cfi(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 1, VLAN_CFI_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* For MPLS. */
|
|
|
|
|
static bool
|
|
|
|
|
set_be32_bf(ovs_be32 *bf, uint8_t bits, uint8_t offset, uint32_t value)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t mask = ((1U << bits) - 1) << offset;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (value >> bits) {
|
|
|
|
|
return false;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*bf = htonl((ntohl(*bf) & ~mask) | (value << offset));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be32_bf(const char *s, ovs_be32 *key, ovs_be32 *mask, uint8_t bits,
|
|
|
|
|
uint8_t offset)
|
|
|
|
|
{
|
|
|
|
|
uint32_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (set_be32_bf(key, bits, offset, key_)) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (!set_be32_bf(mask, bits, offset, mask_)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*mask |= htonl(((1U << bits) - 1) << offset);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_label(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 20, MPLS_LABEL_SHIFT);
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_tc(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 3, MPLS_TC_SHIFT);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_ttl(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 8, MPLS_TTL_SHIFT);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_bos(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 1, MPLS_BOS_SHIFT);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
|
|
|
|
|
{
|
|
|
|
|
const char *s_base = s;
|
2015-06-09 15:24:33 -07:00
|
|
|
|
ovs_be16 id = 0, id_mask = 0;
|
|
|
|
|
uint8_t flags = 0, flags_mask = 0;
|
2015-05-16 22:08:20 -07:00
|
|
|
|
|
|
|
|
|
if (!strncmp(s, "id=", 3)) {
|
|
|
|
|
s += 3;
|
|
|
|
|
s += scan_be16(s, &id, mask ? &id_mask : NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == ',') {
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
if (!strncmp(s, "flags=", 6)) {
|
|
|
|
|
s += 6;
|
|
|
|
|
s += scan_u8(s, &flags, mask ? &flags_mask : NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strncmp(s, "))", 2)) {
|
|
|
|
|
s += 2;
|
|
|
|
|
|
|
|
|
|
*key = (flags << 16) | ntohs(id);
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = (flags_mask << 16) | ntohs(id_mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s - s_base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:03:01 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
|
|
|
|
|
{
|
|
|
|
|
const char *s_base = s;
|
2015-05-29 10:41:05 -07:00
|
|
|
|
struct geneve_opt *opt = key->d;
|
|
|
|
|
struct geneve_opt *opt_mask = mask ? mask->d : NULL;
|
2015-05-18 16:03:01 -07:00
|
|
|
|
int len_remain = sizeof key->d;
|
|
|
|
|
|
|
|
|
|
while (s[0] == '{' && len_remain >= sizeof *opt) {
|
|
|
|
|
int data_len = 0;
|
|
|
|
|
|
|
|
|
|
s++;
|
|
|
|
|
len_remain -= sizeof *opt;
|
|
|
|
|
|
|
|
|
|
if (!strncmp(s, "class=", 6)) {
|
|
|
|
|
s += 6;
|
|
|
|
|
s += scan_be16(s, &opt->opt_class,
|
|
|
|
|
mask ? &opt_mask->opt_class : NULL);
|
|
|
|
|
} else if (mask) {
|
|
|
|
|
memset(&opt_mask->opt_class, 0, sizeof opt_mask->opt_class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == ',') {
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
if (!strncmp(s, "type=", 5)) {
|
|
|
|
|
s += 5;
|
|
|
|
|
s += scan_u8(s, &opt->type, mask ? &opt_mask->type : NULL);
|
|
|
|
|
} else if (mask) {
|
|
|
|
|
memset(&opt_mask->type, 0, sizeof opt_mask->type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == ',') {
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
if (!strncmp(s, "len=", 4)) {
|
|
|
|
|
uint8_t opt_len, opt_len_mask;
|
|
|
|
|
s += 4;
|
|
|
|
|
s += scan_u8(s, &opt_len, mask ? &opt_len_mask : NULL);
|
|
|
|
|
|
|
|
|
|
if (opt_len > 124 || opt_len % 4 || opt_len > len_remain) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
opt->length = opt_len / 4;
|
|
|
|
|
if (mask) {
|
|
|
|
|
opt_mask->length = opt_len_mask;
|
|
|
|
|
}
|
|
|
|
|
data_len = opt_len;
|
|
|
|
|
} else if (mask) {
|
|
|
|
|
memset(&opt_mask->type, 0, sizeof opt_mask->type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == ',') {
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
if (parse_int_string(s, (uint8_t *)(opt + 1), data_len, (char **)&s)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (s[0] == '/') {
|
|
|
|
|
s++;
|
|
|
|
|
if (parse_int_string(s, (uint8_t *)(opt_mask + 1),
|
|
|
|
|
data_len, (char **)&s)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
opt_mask->r1 = 0;
|
|
|
|
|
opt_mask->r2 = 0;
|
|
|
|
|
opt_mask->r3 = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == '}') {
|
|
|
|
|
s++;
|
|
|
|
|
opt += 1 + data_len / 4;
|
|
|
|
|
if (mask) {
|
|
|
|
|
opt_mask += 1 + data_len / 4;
|
|
|
|
|
}
|
|
|
|
|
len_remain -= data_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s[0] == ')') {
|
|
|
|
|
int len = sizeof key->d - len_remain;
|
|
|
|
|
|
|
|
|
|
s++;
|
|
|
|
|
key->len = len;
|
|
|
|
|
if (mask) {
|
|
|
|
|
mask->len = len;
|
|
|
|
|
}
|
|
|
|
|
return s - s_base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
static void
|
|
|
|
|
tun_flags_to_attr(struct ofpbuf *a, const void *data_)
|
|
|
|
|
{
|
|
|
|
|
const uint16_t *flags = data_;
|
|
|
|
|
|
|
|
|
|
if (*flags & FLOW_TNL_F_DONT_FRAGMENT) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
|
|
|
|
|
}
|
|
|
|
|
if (*flags & FLOW_TNL_F_CSUM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
|
|
|
|
|
}
|
|
|
|
|
if (*flags & FLOW_TNL_F_OAM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
vxlan_gbp_to_attr(struct ofpbuf *a, const void *data_)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t *gbp = data_;
|
|
|
|
|
|
|
|
|
|
if (*gbp) {
|
|
|
|
|
size_t vxlan_opts_ofs;
|
|
|
|
|
|
|
|
|
|
vxlan_opts_ofs = nl_msg_start_nested(a, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
|
|
|
|
|
nl_msg_put_u32(a, OVS_VXLAN_EXT_GBP, *gbp);
|
|
|
|
|
nl_msg_end_nested(a, vxlan_opts_ofs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-18 16:03:01 -07:00
|
|
|
|
static void
|
|
|
|
|
geneve_to_attr(struct ofpbuf *a, const void *data_)
|
|
|
|
|
{
|
|
|
|
|
const struct geneve_scan *geneve = data_;
|
|
|
|
|
|
|
|
|
|
nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS, geneve->d,
|
|
|
|
|
geneve->len);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
#define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC) \
|
|
|
|
|
{ \
|
|
|
|
|
unsigned long call_fn = (unsigned long)FUNC; \
|
|
|
|
|
if (call_fn) { \
|
|
|
|
|
typedef void (*fn)(struct ofpbuf *, const void *); \
|
|
|
|
|
fn func = FUNC; \
|
|
|
|
|
func(BUF, &(DATA)); \
|
|
|
|
|
} else { \
|
|
|
|
|
nl_msg_put_unspec(BUF, ATTR, &(DATA), sizeof (DATA)); \
|
|
|
|
|
} \
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_IF(NAME) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
const char *start = s; \
|
|
|
|
|
int len; \
|
|
|
|
|
\
|
|
|
|
|
s += strlen(NAME)
|
|
|
|
|
|
|
|
|
|
/* Usually no special initialization is needed. */
|
|
|
|
|
#define SCAN_BEGIN(NAME, TYPE) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey, smask; \
|
|
|
|
|
memset(&skey, 0, sizeof skey); \
|
|
|
|
|
memset(&smask, 0, sizeof smask); \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
2015-03-13 13:27:19 -07:00
|
|
|
|
/* Init as fully-masked as mask will not be scanned. */
|
|
|
|
|
#define SCAN_BEGIN_FULLY_MASKED(NAME, TYPE) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey, smask; \
|
|
|
|
|
memset(&skey, 0, sizeof skey); \
|
|
|
|
|
memset(&smask, 0xff, sizeof smask); \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* VLAN needs special initialization. */
|
|
|
|
|
#define SCAN_BEGIN_INIT(NAME, TYPE, KEY_INIT, MASK_INIT) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey = KEY_INIT; \
|
|
|
|
|
TYPE smask = MASK_INIT; \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
/* Scan unnamed entry as 'TYPE' */
|
|
|
|
|
#define SCAN_TYPE(TYPE, KEY, MASK) \
|
|
|
|
|
len = scan_##TYPE(s, KEY, MASK); \
|
|
|
|
|
if (len == 0) { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
s += len
|
|
|
|
|
|
|
|
|
|
/* Scan named ('NAME') entry 'FIELD' as 'TYPE'. */
|
|
|
|
|
#define SCAN_FIELD(NAME, TYPE, FIELD) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
s += strlen(NAME); \
|
|
|
|
|
SCAN_TYPE(TYPE, &skey.FIELD, mask ? &smask.FIELD : NULL); \
|
|
|
|
|
continue; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FINISH() \
|
|
|
|
|
} while (*s++ == ',' && len != 0); \
|
|
|
|
|
if (s[-1] != ')') { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FINISH_SINGLE() \
|
|
|
|
|
} while (false); \
|
|
|
|
|
if (*s++ != ')') { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
/* Beginning of nested attribute. */
|
|
|
|
|
#define SCAN_BEGIN_NESTED(NAME, ATTR) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
size_t key_offset, mask_offset; \
|
|
|
|
|
key_offset = nl_msg_start_nested(key, ATTR); \
|
|
|
|
|
if (mask) { \
|
|
|
|
|
mask_offset = nl_msg_start_nested(mask, ATTR); \
|
|
|
|
|
} \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
#define SCAN_END_NESTED() \
|
|
|
|
|
SCAN_FINISH(); \
|
|
|
|
|
nl_msg_end_nested(key, key_offset); \
|
|
|
|
|
if (mask) { \
|
|
|
|
|
nl_msg_end_nested(mask, mask_offset); \
|
|
|
|
|
} \
|
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, ATTR, FUNC) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
TYPE skey, smask; \
|
|
|
|
|
memset(&skey, 0, sizeof skey); \
|
|
|
|
|
memset(&smask, 0xff, sizeof smask); \
|
|
|
|
|
s += strlen(NAME); \
|
|
|
|
|
SCAN_TYPE(SCAN_AS, &skey, &smask); \
|
|
|
|
|
SCAN_PUT(ATTR, FUNC); \
|
|
|
|
|
continue; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FIELD_NESTED(NAME, TYPE, SCAN_AS, ATTR) \
|
|
|
|
|
SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, ATTR, NULL)
|
|
|
|
|
|
|
|
|
|
#define SCAN_FIELD_NESTED_FUNC(NAME, TYPE, SCAN_AS, FUNC) \
|
|
|
|
|
SCAN_FIELD_NESTED__(NAME, TYPE, SCAN_AS, 0, FUNC)
|
|
|
|
|
|
|
|
|
|
#define SCAN_PUT(ATTR, FUNC) \
|
2016-01-14 13:29:07 +02:00
|
|
|
|
SCAN_PUT_ATTR(key, ATTR, skey, FUNC); \
|
|
|
|
|
if (mask) \
|
|
|
|
|
SCAN_PUT_ATTR(mask, ATTR, smask, FUNC); \
|
2014-09-09 14:50:36 -07:00
|
|
|
|
|
|
|
|
|
#define SCAN_END(ATTR) \
|
|
|
|
|
SCAN_FINISH(); \
|
2015-05-16 22:08:20 -07:00
|
|
|
|
SCAN_PUT(ATTR, NULL); \
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-24 16:10:42 -08:00
|
|
|
|
#define SCAN_BEGIN_ARRAY(NAME, TYPE, CNT) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey[CNT], smask[CNT]; \
|
|
|
|
|
memset(&skey, 0, sizeof skey); \
|
|
|
|
|
memset(&smask, 0, sizeof smask); \
|
|
|
|
|
int idx = 0, cnt = CNT; \
|
|
|
|
|
uint64_t fields = 0; \
|
|
|
|
|
do { \
|
|
|
|
|
int field = 0; \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
/* Scan named ('NAME') entry 'FIELD' as 'TYPE'. */
|
|
|
|
|
#define SCAN_FIELD_ARRAY(NAME, TYPE, FIELD) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
if (fields & (1UL << field)) { \
|
|
|
|
|
fields = 0; \
|
|
|
|
|
if (++idx == cnt) { \
|
|
|
|
|
break; \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
s += strlen(NAME); \
|
|
|
|
|
SCAN_TYPE(TYPE, &skey[idx].FIELD, mask ? &smask[idx].FIELD : NULL); \
|
|
|
|
|
fields |= 1UL << field; \
|
|
|
|
|
continue; \
|
|
|
|
|
} \
|
|
|
|
|
field++;
|
|
|
|
|
|
|
|
|
|
#define SCAN_PUT_ATTR_ARRAY(BUF, ATTR, DATA, CNT) \
|
|
|
|
|
nl_msg_put_unspec(BUF, ATTR, &(DATA), sizeof (DATA)[0] * (CNT)); \
|
|
|
|
|
|
|
|
|
|
#define SCAN_PUT_ARRAY(ATTR, CNT) \
|
|
|
|
|
SCAN_PUT_ATTR_ARRAY(key, ATTR, skey, CNT); \
|
|
|
|
|
if (mask) { \
|
|
|
|
|
SCAN_PUT_ATTR_ARRAY(mask, ATTR, smask, CNT); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_END_ARRAY(ATTR) \
|
|
|
|
|
SCAN_FINISH(); \
|
|
|
|
|
if (idx == cnt) { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
SCAN_PUT_ARRAY(ATTR, idx + 1); \
|
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
#define SCAN_END_SINGLE(ATTR) \
|
|
|
|
|
SCAN_FINISH_SINGLE(); \
|
2015-05-16 22:08:20 -07:00
|
|
|
|
SCAN_PUT(ATTR, NULL); \
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_SINGLE(NAME, TYPE, SCAN_AS, ATTR) \
|
|
|
|
|
SCAN_BEGIN(NAME, TYPE) { \
|
|
|
|
|
SCAN_TYPE(SCAN_AS, &skey, &smask); \
|
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
|
|
|
|
|
2015-03-13 13:27:19 -07:00
|
|
|
|
#define SCAN_SINGLE_FULLY_MASKED(NAME, TYPE, SCAN_AS, ATTR) \
|
|
|
|
|
SCAN_BEGIN_FULLY_MASKED(NAME, TYPE) { \
|
|
|
|
|
SCAN_TYPE(SCAN_AS, &skey, NULL); \
|
2014-09-09 14:50:36 -07:00
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
|
|
|
|
|
|
|
|
|
/* scan_port needs one extra argument. */
|
|
|
|
|
#define SCAN_SINGLE_PORT(NAME, TYPE, ATTR) \
|
|
|
|
|
SCAN_BEGIN(NAME, TYPE) { \
|
|
|
|
|
len = scan_port(s, &skey, &smask, port_names); \
|
|
|
|
|
if (len == 0) { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
s += len; \
|
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *key, struct ofpbuf *mask)
|
|
|
|
|
{
|
2015-05-29 17:08:45 -07:00
|
|
|
|
ovs_u128 ufid;
|
|
|
|
|
int len;
|
2015-05-20 17:04:33 -07:00
|
|
|
|
|
2015-05-29 17:08:45 -07:00
|
|
|
|
/* Skip UFID. */
|
|
|
|
|
len = odp_ufid_from_string(s, &ufid);
|
|
|
|
|
if (len) {
|
|
|
|
|
return len;
|
2015-05-20 17:04:33 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY);
|
|
|
|
|
SCAN_SINGLE("skb_mark(", uint32_t, u32, OVS_KEY_ATTR_SKB_MARK);
|
2015-03-13 13:27:19 -07:00
|
|
|
|
SCAN_SINGLE_FULLY_MASKED("recirc_id(", uint32_t, u32,
|
|
|
|
|
OVS_KEY_ATTR_RECIRC_ID);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
SCAN_SINGLE("dp_hash(", uint32_t, u32, OVS_KEY_ATTR_DP_HASH);
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
SCAN_SINGLE("ct_state(", uint32_t, ct_state, OVS_KEY_ATTR_CT_STATE);
|
|
|
|
|
SCAN_SINGLE("ct_zone(", uint16_t, u16, OVS_KEY_ATTR_CT_ZONE);
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
SCAN_SINGLE("ct_mark(", uint32_t, u32, OVS_KEY_ATTR_CT_MARK);
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
SCAN_SINGLE("ct_label(", ovs_u128, u128, OVS_KEY_ATTR_CT_LABELS);
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
2015-05-16 22:08:20 -07:00
|
|
|
|
SCAN_BEGIN_NESTED("tunnel(", OVS_KEY_ATTR_TUNNEL) {
|
|
|
|
|
SCAN_FIELD_NESTED("tun_id=", ovs_be64, be64, OVS_TUNNEL_KEY_ATTR_ID);
|
|
|
|
|
SCAN_FIELD_NESTED("src=", ovs_be32, ipv4, OVS_TUNNEL_KEY_ATTR_IPV4_SRC);
|
|
|
|
|
SCAN_FIELD_NESTED("dst=", ovs_be32, ipv4, OVS_TUNNEL_KEY_ATTR_IPV4_DST);
|
2015-11-25 11:31:11 -02:00
|
|
|
|
SCAN_FIELD_NESTED("ipv6_src=", struct in6_addr, in6_addr, OVS_TUNNEL_KEY_ATTR_IPV6_SRC);
|
|
|
|
|
SCAN_FIELD_NESTED("ipv6_dst=", struct in6_addr, in6_addr, OVS_TUNNEL_KEY_ATTR_IPV6_DST);
|
2015-05-16 22:08:20 -07:00
|
|
|
|
SCAN_FIELD_NESTED("tos=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TOS);
|
|
|
|
|
SCAN_FIELD_NESTED("ttl=", uint8_t, u8, OVS_TUNNEL_KEY_ATTR_TTL);
|
|
|
|
|
SCAN_FIELD_NESTED("tp_src=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_SRC);
|
|
|
|
|
SCAN_FIELD_NESTED("tp_dst=", ovs_be16, be16, OVS_TUNNEL_KEY_ATTR_TP_DST);
|
|
|
|
|
SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
|
2015-05-18 16:03:01 -07:00
|
|
|
|
SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
|
|
|
|
|
geneve_to_attr);
|
2015-05-16 22:08:20 -07:00
|
|
|
|
SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
|
|
|
|
|
} SCAN_END_NESTED();
|
2014-09-09 14:50:36 -07:00
|
|
|
|
|
|
|
|
|
SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
|
|
|
|
|
SCAN_FIELD("src=", eth, eth_src);
|
|
|
|
|
SCAN_FIELD("dst=", eth, eth_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ETHERNET);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN_INIT("vlan(", struct ovs_key_vlan__,
|
|
|
|
|
{ htons(VLAN_CFI) }, { htons(VLAN_CFI) }) {
|
|
|
|
|
SCAN_FIELD("vid=", vid, tci);
|
|
|
|
|
SCAN_FIELD("pcp=", pcp, tci);
|
|
|
|
|
SCAN_FIELD("cfi=", cfi, tci);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_VLAN);
|
|
|
|
|
|
|
|
|
|
SCAN_SINGLE("eth_type(", ovs_be16, be16, OVS_KEY_ATTR_ETHERTYPE);
|
|
|
|
|
|
2016-02-29 11:13:28 -08:00
|
|
|
|
SCAN_BEGIN_ARRAY("mpls(", struct ovs_key_mpls, FLOW_MAX_MPLS_LABELS) {
|
2016-02-24 16:10:42 -08:00
|
|
|
|
SCAN_FIELD_ARRAY("label=", mpls_label, mpls_lse);
|
|
|
|
|
SCAN_FIELD_ARRAY("tc=", mpls_tc, mpls_lse);
|
|
|
|
|
SCAN_FIELD_ARRAY("ttl=", mpls_ttl, mpls_lse);
|
|
|
|
|
SCAN_FIELD_ARRAY("bos=", mpls_bos, mpls_lse);
|
|
|
|
|
} SCAN_END_ARRAY(OVS_KEY_ATTR_MPLS);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("ipv4(", struct ovs_key_ipv4) {
|
|
|
|
|
SCAN_FIELD("src=", ipv4, ipv4_src);
|
|
|
|
|
SCAN_FIELD("dst=", ipv4, ipv4_dst);
|
|
|
|
|
SCAN_FIELD("proto=", u8, ipv4_proto);
|
|
|
|
|
SCAN_FIELD("tos=", u8, ipv4_tos);
|
|
|
|
|
SCAN_FIELD("ttl=", u8, ipv4_ttl);
|
|
|
|
|
SCAN_FIELD("frag=", frag, ipv4_frag);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_IPV4);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("ipv6(", struct ovs_key_ipv6) {
|
2017-01-04 16:10:56 -08:00
|
|
|
|
SCAN_FIELD("src=", in6_addr, ipv6_src);
|
|
|
|
|
SCAN_FIELD("dst=", in6_addr, ipv6_dst);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
SCAN_FIELD("label=", ipv6_label, ipv6_label);
|
|
|
|
|
SCAN_FIELD("proto=", u8, ipv6_proto);
|
|
|
|
|
SCAN_FIELD("tclass=", u8, ipv6_tclass);
|
|
|
|
|
SCAN_FIELD("hlimit=", u8, ipv6_hlimit);
|
|
|
|
|
SCAN_FIELD("frag=", frag, ipv6_frag);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_IPV6);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("tcp(", struct ovs_key_tcp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, tcp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, tcp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_TCP);
|
|
|
|
|
|
|
|
|
|
SCAN_SINGLE("tcp_flags(", ovs_be16, tcp_flags, OVS_KEY_ATTR_TCP_FLAGS);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("udp(", struct ovs_key_udp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, udp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, udp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_UDP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("sctp(", struct ovs_key_sctp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, sctp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, sctp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_SCTP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("icmp(", struct ovs_key_icmp) {
|
|
|
|
|
SCAN_FIELD("type=", u8, icmp_type);
|
|
|
|
|
SCAN_FIELD("code=", u8, icmp_code);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ICMP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("icmpv6(", struct ovs_key_icmpv6) {
|
|
|
|
|
SCAN_FIELD("type=", u8, icmpv6_type);
|
|
|
|
|
SCAN_FIELD("code=", u8, icmpv6_code);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ICMPV6);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("arp(", struct ovs_key_arp) {
|
|
|
|
|
SCAN_FIELD("sip=", ipv4, arp_sip);
|
|
|
|
|
SCAN_FIELD("tip=", ipv4, arp_tip);
|
|
|
|
|
SCAN_FIELD("op=", be16, arp_op);
|
|
|
|
|
SCAN_FIELD("sha=", eth, arp_sha);
|
|
|
|
|
SCAN_FIELD("tha=", eth, arp_tha);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ARP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("nd(", struct ovs_key_nd) {
|
2017-01-04 16:10:56 -08:00
|
|
|
|
SCAN_FIELD("target=", in6_addr, nd_target);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
SCAN_FIELD("sll=", eth, nd_sll);
|
|
|
|
|
SCAN_FIELD("tll=", eth, nd_tll);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ND);
|
|
|
|
|
|
|
|
|
|
/* Encap open-coded. */
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (!strncmp(s, "encap(", 6)) {
|
|
|
|
|
const char *start = s;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
size_t encap, encap_mask = 0;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
|
|
|
|
encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP);
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
s += 6;
|
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
2014-12-05 14:07:19 -08:00
|
|
|
|
s += strspn(s, delimiters);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (!*s) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
} else if (*s == ')') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
retval = parse_odp_key_mask_attr(s, port_names, key, mask);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
nl_msg_end_nested(key, encap);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
|
|
|
|
nl_msg_end_nested(mask, encap_mask);
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
return s - start;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-04 16:20:34 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
/* Parses the string representation of a datapath flow key, in the
|
|
|
|
|
* format output by odp_flow_key_format(). Returns 0 if successful,
|
|
|
|
|
* otherwise a positive errno value. On success, the flow key is
|
|
|
|
|
* appended to 'key' as a series of Netlink attributes. On failure, no
|
|
|
|
|
* data is appended to 'key'. Either way, 'key''s data might be
|
|
|
|
|
* reallocated.
|
2011-08-04 16:20:34 -07:00
|
|
|
|
*
|
2012-05-22 10:32:02 -07:00
|
|
|
|
* If 'port_names' is nonnull, it points to an simap that maps from a port name
|
|
|
|
|
* to a port number. (Port names may be used instead of port numbers in
|
|
|
|
|
* in_port.)
|
2011-10-26 10:01:32 -07:00
|
|
|
|
*
|
2011-08-04 16:20:34 -07:00
|
|
|
|
* On success, the attributes appended to 'key' are individually syntactically
|
|
|
|
|
* valid, but they may not be valid as a sequence. 'key' might, for example,
|
2011-11-14 15:09:01 -08:00
|
|
|
|
* have duplicated keys. odp_flow_key_to_flow() will detect those errors. */
|
2011-08-04 16:20:34 -07:00
|
|
|
|
int
|
2013-06-19 07:15:10 +00:00
|
|
|
|
odp_flow_from_string(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *key, struct ofpbuf *mask)
|
2011-08-04 16:20:34 -07:00
|
|
|
|
{
|
2015-03-02 17:29:44 -08:00
|
|
|
|
const size_t old_size = key->size;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
s += strspn(s, delimiters);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
if (!*s) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
retval = parse_odp_key_mask_attr(s, port_names, key, mask);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
if (retval < 0) {
|
2015-03-02 17:29:44 -08:00
|
|
|
|
key->size = old_size;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
return -retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static uint8_t
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ovs_to_odp_frag(uint8_t nw_frag, bool is_mask)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
/* Netlink interface 'enum ovs_frag_type' is an 8-bit enumeration type,
|
|
|
|
|
* not a set of flags or bitfields. Hence, if the struct flow nw_frag
|
|
|
|
|
* mask, which is a set of bits, has the FLOW_NW_FRAG_ANY as zero, we
|
|
|
|
|
* must use a zero mask for the netlink frag field, and all ones mask
|
|
|
|
|
* otherwise. */
|
|
|
|
|
return (nw_frag & FLOW_NW_FRAG_ANY) ? UINT8_MAX : 0;
|
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return !(nw_frag & FLOW_NW_FRAG_ANY) ? OVS_FRAG_TYPE_NONE
|
|
|
|
|
: nw_frag & FLOW_NW_FRAG_LATER ? OVS_FRAG_TYPE_LATER
|
|
|
|
|
: OVS_FRAG_TYPE_FIRST;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
static void get_ethernet_key(const struct flow *, struct ovs_key_ethernet *);
|
|
|
|
|
static void put_ethernet_key(const struct ovs_key_ethernet *, struct flow *);
|
|
|
|
|
static void get_ipv4_key(const struct flow *, struct ovs_key_ipv4 *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void put_ipv4_key(const struct ovs_key_ipv4 *, struct flow *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void get_ipv6_key(const struct flow *, struct ovs_key_ipv6 *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void put_ipv6_key(const struct ovs_key_ipv6 *, struct flow *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void get_arp_key(const struct flow *, struct ovs_key_arp *);
|
|
|
|
|
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
|
2014-12-23 23:42:05 +00:00
|
|
|
|
static void get_nd_key(const struct flow *, struct ovs_key_nd *);
|
|
|
|
|
static void put_nd_key(const struct ovs_key_nd *, struct flow *);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
|
|
|
|
/* These share the same layout. */
|
|
|
|
|
union ovs_key_tp {
|
|
|
|
|
struct ovs_key_tcp tcp;
|
|
|
|
|
struct ovs_key_udp udp;
|
|
|
|
|
struct ovs_key_sctp sctp;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void get_tp_key(const struct flow *, union ovs_key_tp *);
|
|
|
|
|
static void put_tp_key(const union ovs_key_tp *, struct flow *);
|
2013-07-31 13:54:12 -07:00
|
|
|
|
|
2013-06-05 18:56:58 -07:00
|
|
|
|
static void
|
2015-06-16 11:15:28 -07:00
|
|
|
|
odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
|
|
|
|
|
bool export_mask, struct ofpbuf *buf)
|
2010-10-11 13:31:35 -07:00
|
|
|
|
{
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ethernet *eth_key;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
size_t encap;
|
2015-06-16 11:15:28 -07:00
|
|
|
|
const struct flow *flow = parms->flow;
|
|
|
|
|
const struct flow *data = export_mask ? parms->mask : parms->flow;
|
2013-06-05 18:56:58 -07:00
|
|
|
|
|
2013-08-03 12:23:15 -07:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2015-11-25 11:31:11 -02:00
|
|
|
|
if (flow_tnl_dst_is_set(&flow->tunnel) || export_mask) {
|
2015-06-19 13:54:13 -07:00
|
|
|
|
tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
|
|
|
|
|
parms->key_buf);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-06 12:57:13 -07:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
if (parms->support.ct_state) {
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_STATE,
|
|
|
|
|
ovs_to_odp_ct_state(data->ct_state));
|
|
|
|
|
}
|
|
|
|
|
if (parms->support.ct_zone) {
|
|
|
|
|
nl_msg_put_u16(buf, OVS_KEY_ATTR_CT_ZONE, data->ct_zone);
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (parms->support.ct_mark) {
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, data->ct_mark);
|
|
|
|
|
}
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
if (parms->support.ct_label) {
|
|
|
|
|
nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &data->ct_label,
|
|
|
|
|
sizeof(data->ct_label));
|
|
|
|
|
}
|
2015-06-30 16:43:03 -07:00
|
|
|
|
if (parms->support.recirc) {
|
2014-03-04 15:36:03 -08:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-09 13:32:50 -07:00
|
|
|
|
/* Add an ingress port attribute if this is a mask or 'in_port.odp_port'
|
2013-06-05 18:56:58 -07:00
|
|
|
|
* is not the magical value "ODPP_NONE". */
|
2016-06-09 13:32:50 -07:00
|
|
|
|
if (export_mask || flow->in_port.odp_port != ODPP_NONE) {
|
|
|
|
|
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, data->in_port.odp_port);
|
2011-09-08 16:30:20 -07:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *eth_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ethernet_key(data, eth_key);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 17:19:41 -08:00
|
|
|
|
if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (export_mask) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
|
2013-06-27 17:57:57 -07:00
|
|
|
|
} else {
|
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
|
|
|
|
|
}
|
2013-06-05 18:56:58 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
|
2011-11-14 17:19:41 -08:00
|
|
|
|
if (flow->vlan_tci == htons(0)) {
|
|
|
|
|
goto unencap;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
} else {
|
|
|
|
|
encap = 0;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
|
2013-06-05 18:56:58 -07:00
|
|
|
|
/* For backwards compatibility with kernels that don't support
|
|
|
|
|
* wildcarding, the following convention is used to encode the
|
|
|
|
|
* OVS_KEY_ATTR_ETHERTYPE for key and mask:
|
|
|
|
|
*
|
|
|
|
|
* key mask matches
|
|
|
|
|
* -------- -------- -------
|
|
|
|
|
* >0x5ff 0xffff Specified Ethernet II Ethertype.
|
|
|
|
|
* >0x5ff 0 Any Ethernet II or non-Ethernet II frame.
|
|
|
|
|
* <none> 0xffff Any non-Ethernet II frame (except valid
|
|
|
|
|
* 802.3 SNAP packet with valid eth_type).
|
|
|
|
|
*/
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (export_mask) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
goto unencap;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 18:56:58 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
|
|
|
|
if (flow->dl_type == htons(ETH_TYPE_IP)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ipv4 *ipv4_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *ipv4_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(data, ipv4_key, export_mask);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ipv6 *ipv6_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
|
2010-12-29 19:03:46 -08:00
|
|
|
|
sizeof *ipv6_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(data, ipv6_key, export_mask);
|
2012-11-02 11:43:46 -07:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
|
|
|
|
|
flow->dl_type == htons(ETH_TYPE_RARP)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_arp *arp_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
|
|
|
|
|
sizeof *arp_key);
|
|
|
|
|
get_arp_key(data, arp_key);
|
2013-09-27 06:55:19 +09:00
|
|
|
|
} else if (eth_type_mpls(flow->dl_type)) {
|
2013-01-25 16:22:07 +09:00
|
|
|
|
struct ovs_key_mpls *mpls_key;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
int i, n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
n = flow_count_mpls_labels(flow, NULL);
|
2015-06-16 11:15:28 -07:00
|
|
|
|
if (export_mask) {
|
2015-06-30 16:43:03 -07:00
|
|
|
|
n = MIN(n, parms->support.max_mpls_depth);
|
2015-06-16 11:15:28 -07:00
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
n * sizeof *mpls_key);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
mpls_key[i].mpls_lse = data->mpls_lse[i];
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-22 19:38:32 -08:00
|
|
|
|
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
2011-02-02 11:33:20 -08:00
|
|
|
|
if (flow->nw_proto == IPPROTO_TCP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *tcp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *tcp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, tcp_key);
|
2013-10-28 13:54:40 -07:00
|
|
|
|
if (data->tcp_flags) {
|
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags);
|
|
|
|
|
}
|
2011-02-02 11:33:20 -08:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_UDP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *udp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *udp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, udp_key);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_SCTP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *sctp_key;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
|
|
|
|
sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP,
|
|
|
|
|
sizeof *sctp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, sctp_key);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& flow->nw_proto == IPPROTO_ICMP) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_icmp *icmp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *icmp_key);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
icmp_key->icmp_type = ntohs(data->tp_src);
|
|
|
|
|
icmp_key->icmp_code = ntohs(data->tp_dst);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)
|
|
|
|
|
&& flow->nw_proto == IPPROTO_ICMPV6) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_icmpv6 *icmpv6_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6,
|
2010-12-29 19:03:46 -08:00
|
|
|
|
sizeof *icmpv6_key);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
icmpv6_key->icmpv6_type = ntohs(data->tp_src);
|
|
|
|
|
icmpv6_key->icmpv6_code = ntohs(data->tp_dst);
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2016-07-02 11:35:29 -07:00
|
|
|
|
if (is_nd(flow, NULL)
|
2015-12-08 18:39:18 -08:00
|
|
|
|
/* Even though 'tp_src' and 'tp_dst' are 16 bits wide, ICMP
|
|
|
|
|
* type and code are 8 bits wide. Therefore, an exact match
|
|
|
|
|
* looks like htons(0xff), not htons(0xffff). See
|
|
|
|
|
* xlate_wc_finish() for details. */
|
|
|
|
|
&& (!export_mask || (data->tp_src == htons(0xff)
|
|
|
|
|
&& data->tp_dst == htons(0xff)))) {
|
2013-08-30 09:57:13 -07:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_nd *nd_key;
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
|
2011-02-01 22:54:11 -08:00
|
|
|
|
sizeof *nd_key);
|
2017-01-04 16:10:56 -08:00
|
|
|
|
nd_key->nd_target = data->nd_target;
|
2015-08-28 14:55:11 -07:00
|
|
|
|
nd_key->nd_sll = data->arp_sha;
|
|
|
|
|
nd_key->nd_tll = data->arp_tha;
|
2011-02-01 22:54:11 -08:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
unencap:
|
|
|
|
|
if (encap) {
|
|
|
|
|
nl_msg_end_nested(buf, encap);
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-05 18:56:58 -07:00
|
|
|
|
|
|
|
|
|
/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
|
|
|
|
|
*
|
|
|
|
|
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
|
2015-06-16 11:15:28 -07:00
|
|
|
|
* capable of being expanded to allow for that much space. */
|
2013-06-05 18:56:58 -07:00
|
|
|
|
void
|
2015-06-16 11:15:28 -07:00
|
|
|
|
odp_flow_key_from_flow(const struct odp_flow_key_parms *parms,
|
|
|
|
|
struct ofpbuf *buf)
|
2013-06-05 18:56:58 -07:00
|
|
|
|
{
|
2015-06-16 11:15:28 -07:00
|
|
|
|
odp_flow_key_from_flow__(parms, false, buf);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
|
2015-06-16 11:15:28 -07:00
|
|
|
|
* 'buf'.
|
2013-06-05 18:56:58 -07:00
|
|
|
|
*
|
|
|
|
|
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
|
2015-06-16 11:15:28 -07:00
|
|
|
|
* capable of being expanded to allow for that much space. */
|
2013-06-05 18:56:58 -07:00
|
|
|
|
void
|
2015-06-16 11:15:28 -07:00
|
|
|
|
odp_flow_key_from_mask(const struct odp_flow_key_parms *parms,
|
|
|
|
|
struct ofpbuf *buf)
|
2013-06-05 18:56:58 -07:00
|
|
|
|
{
|
2015-06-16 11:15:28 -07:00
|
|
|
|
odp_flow_key_from_flow__(parms, true, buf);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-12-30 15:58:58 -08:00
|
|
|
|
/* Generate ODP flow key from the given packet metadata */
|
|
|
|
|
void
|
|
|
|
|
odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
|
|
|
|
|
{
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
|
|
|
|
|
|
2015-11-25 11:31:11 -02:00
|
|
|
|
if (flow_tnl_dst_is_set(&md->tunnel)) {
|
2015-06-19 13:54:13 -07:00
|
|
|
|
tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
if (md->ct_state) {
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_STATE,
|
|
|
|
|
ovs_to_odp_ct_state(md->ct_state));
|
|
|
|
|
if (md->ct_zone) {
|
|
|
|
|
nl_msg_put_u16(buf, OVS_KEY_ATTR_CT_ZONE, md->ct_zone);
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (md->ct_mark) {
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_CT_MARK, md->ct_mark);
|
|
|
|
|
}
|
2016-05-03 18:20:51 -07:00
|
|
|
|
if (!ovs_u128_is_zero(md->ct_label)) {
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
nl_msg_put_unspec(buf, OVS_KEY_ATTR_CT_LABELS, &md->ct_label,
|
|
|
|
|
sizeof(md->ct_label));
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-30 15:58:58 -08:00
|
|
|
|
/* Add an ingress port attribute if 'odp_in_port' is not the magical
|
|
|
|
|
* value "ODPP_NONE". */
|
2014-02-26 18:08:04 -08:00
|
|
|
|
if (md->in_port.odp_port != ODPP_NONE) {
|
|
|
|
|
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate packet metadata from the given ODP flow key. */
|
|
|
|
|
void
|
|
|
|
|
odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct pkt_metadata *md)
|
|
|
|
|
{
|
|
|
|
|
const struct nlattr *nla;
|
|
|
|
|
size_t left;
|
|
|
|
|
uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY |
|
|
|
|
|
1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
|
|
|
|
|
1u << OVS_KEY_ATTR_IN_PORT;
|
|
|
|
|
|
2015-06-30 19:19:40 -07:00
|
|
|
|
pkt_metadata_init(md, ODPP_NONE);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
|
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
size_t len = nl_attr_get_size(nla);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
int expected_len = odp_key_attr_len(ovs_flow_key_attr_lens,
|
|
|
|
|
OVS_KEY_ATTR_MAX, type);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
|
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 15:36:03 -08:00
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID:
|
|
|
|
|
md->recirc_id = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_RECIRC_ID);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_DP_HASH:
|
|
|
|
|
md->dp_hash = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_DP_HASH);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_PRIORITY:
|
2013-12-30 15:58:58 -08:00
|
|
|
|
md->skb_priority = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_PRIORITY);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK:
|
2013-12-30 15:58:58 -08:00
|
|
|
|
md->pkt_mark = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_STATE:
|
|
|
|
|
md->ct_state = odp_to_ovs_ct_state(nl_attr_get_u32(nla));
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_STATE);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_CT_ZONE:
|
|
|
|
|
md->ct_zone = nl_attr_get_u16(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_ZONE);
|
|
|
|
|
break;
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_MARK:
|
|
|
|
|
md->ct_mark = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_MARK);
|
|
|
|
|
break;
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
case OVS_KEY_ATTR_CT_LABELS: {
|
|
|
|
|
const ovs_u128 *cl = nl_attr_get(nla);
|
|
|
|
|
|
|
|
|
|
md->ct_label = *cl;
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_CT_LABELS);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: {
|
2013-12-30 15:58:58 -08:00
|
|
|
|
enum odp_key_fitness res;
|
|
|
|
|
|
2016-04-19 18:36:04 -07:00
|
|
|
|
res = odp_tun_key_from_attr(nla, &md->tunnel);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
if (res == ODP_FIT_ERROR) {
|
|
|
|
|
memset(&md->tunnel, 0, sizeof md->tunnel);
|
|
|
|
|
} else if (res == ODP_FIT_PERFECT) {
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL);
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_IN_PORT:
|
2014-02-26 18:08:04 -08:00
|
|
|
|
md->in_port.odp_port = nl_attr_get_odp_port(nla);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2013-12-30 15:58:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!wanted_attrs) {
|
|
|
|
|
return; /* Have everything. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
uint32_t
|
|
|
|
|
odp_flow_key_hash(const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
BUILD_ASSERT_DECL(!(NLA_ALIGNTO % sizeof(uint32_t)));
|
2016-01-18 22:52:48 -08:00
|
|
|
|
return hash_bytes32(ALIGNED_CAST(const uint32_t *, key), key_len, 0);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
static void
|
|
|
|
|
log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title,
|
2011-11-23 10:26:02 -08:00
|
|
|
|
uint64_t attrs, int out_of_range_attr,
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
struct ds s;
|
|
|
|
|
int i;
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (VLOG_DROP_DBG(rl)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
|
if (attrs & (UINT64_C(1) << i)) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
|
|
|
|
ds_put_format(&s, " %s",
|
|
|
|
|
ovs_key_attr_to_string(i, namebuf, sizeof namebuf));
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (out_of_range_attr) {
|
|
|
|
|
ds_put_format(&s, " %d (and possibly others)", out_of_range_attr);
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(&s, ": ");
|
|
|
|
|
odp_flow_key_format(key, key_len, &s);
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_DBG("%s:%s", title, ds_cstr(&s));
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
static uint8_t
|
|
|
|
|
odp_to_ovs_frag(uint8_t odp_frag, bool is_mask)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
2011-11-14 15:09:01 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
return odp_frag ? FLOW_NW_FRAG_MASK : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-02 18:17:36 -07:00
|
|
|
|
if (odp_frag > OVS_FRAG_TYPE_LATER) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "invalid frag %"PRIu8" in flow key", odp_frag);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return 0xff; /* Error. */
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return (odp_frag == OVS_FRAG_TYPE_NONE) ? 0
|
|
|
|
|
: (odp_frag == OVS_FRAG_TYPE_FIRST) ? FLOW_NW_FRAG_ANY
|
|
|
|
|
: FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static bool
|
2011-11-14 15:56:43 -08:00
|
|
|
|
parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
|
2011-11-23 10:26:02 -08:00
|
|
|
|
const struct nlattr *attrs[], uint64_t *present_attrsp,
|
|
|
|
|
int *out_of_range_attrp)
|
2011-01-23 18:44:44 -08:00
|
|
|
|
{
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
const struct nlattr *nla;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
uint64_t present_attrs;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
size_t left;
|
|
|
|
|
|
2013-03-22 16:25:36 -07:00
|
|
|
|
BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs);
|
2011-11-14 15:09:01 -08:00
|
|
|
|
present_attrs = 0;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*out_of_range_attrp = 0;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
size_t len = nl_attr_get_size(nla);
|
2015-05-20 11:57:35 -07:00
|
|
|
|
int expected_len = odp_key_attr_len(ovs_flow_key_attr_lens,
|
|
|
|
|
OVS_KEY_ATTR_MAX, type);
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
2013-11-25 23:38:48 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "attribute %s has length %"PRIuSIZE" but should have "
|
2013-04-15 15:40:21 -07:00
|
|
|
|
"length %d", ovs_key_attr_to_string(type, namebuf,
|
|
|
|
|
sizeof namebuf),
|
2011-11-23 10:26:02 -08:00
|
|
|
|
len, expected_len);
|
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-22 16:25:36 -07:00
|
|
|
|
if (type > OVS_KEY_ATTR_MAX) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*out_of_range_attrp = type;
|
|
|
|
|
} else {
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << type)) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key",
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ovs_key_attr_to_string(type,
|
|
|
|
|
namebuf, sizeof namebuf));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
present_attrs |= UINT64_C(1) << type;
|
|
|
|
|
attrs[type] = nla;
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
if (left) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "trailing garbage in flow key");
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 15:56:43 -08:00
|
|
|
|
*present_attrsp = present_attrs;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return true;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
check_expectations(uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs,
|
2011-11-14 15:56:43 -08:00
|
|
|
|
const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
uint64_t missing_attrs;
|
|
|
|
|
uint64_t extra_attrs;
|
|
|
|
|
|
|
|
|
|
missing_attrs = expected_attrs & ~present_attrs;
|
|
|
|
|
if (missing_attrs) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
|
|
|
|
log_odp_key_attributes(&rl, "expected but not present",
|
|
|
|
|
missing_attrs, 0, key, key_len);
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extra_attrs = present_attrs & ~expected_attrs;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (extra_attrs || out_of_range_attr) {
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
|
|
|
|
log_odp_key_attributes(&rl, "present but not expected",
|
|
|
|
|
extra_attrs, out_of_range_attr, key, key_len);
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_PERFECT;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static bool
|
|
|
|
|
parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, uint64_t *expected_attrs,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
struct flow *flow, const struct flow *src_flow)
|
2011-11-14 15:56:43 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = flow != src_flow;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask && ntohs(flow->dl_type) < ETH_TYPE_MIN) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key",
|
|
|
|
|
ntohs(flow->dl_type));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask && ntohs(src_flow->dl_type) < ETH_TYPE_MIN &&
|
|
|
|
|
flow->dl_type != htons(0xffff)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
} else {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
flow->dl_type = htons(FLOW_DL_TYPE_NONE);
|
|
|
|
|
} else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) {
|
|
|
|
|
/* See comments in odp_flow_key_from_flow__(). */
|
|
|
|
|
VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum odp_key_fitness
|
2013-01-25 16:22:07 +09:00
|
|
|
|
parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs, struct flow *flow,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
const struct nlattr *key, size_t key_len,
|
2013-10-04 07:49:13 -07:00
|
|
|
|
const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
|
|
|
|
const void *check_start = NULL;
|
|
|
|
|
size_t check_len = 0;
|
|
|
|
|
enum ovs_key_attr expected_bit = 0xff;
|
|
|
|
|
|
|
|
|
|
if (eth_type_mpls(src_flow->dl_type)) {
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!is_mask || present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
|
2013-10-04 07:49:13 -07:00
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
|
2014-10-31 14:05:46 -07:00
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
|
|
|
|
|
size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
|
|
|
|
|
const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
|
|
|
|
|
int n = size / sizeof(ovs_be32);
|
|
|
|
|
int i;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!size || size % sizeof(ovs_be32)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
2013-10-04 07:49:13 -07:00
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
|
|
|
|
|
flow->mpls_lse[i] = mpls_lse[i];
|
|
|
|
|
}
|
|
|
|
|
if (n > FLOW_MAX_MPLS_LABELS) {
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
/* BOS may be set only in the innermost label. */
|
|
|
|
|
for (i = 0; i < n - 1; i++) {
|
|
|
|
|
if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
/* BOS must be set in the innermost label. */
|
|
|
|
|
if (n < FLOW_MAX_MPLS_LABELS
|
|
|
|
|
&& !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
goto done;
|
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_ipv4 *ipv4_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(ipv4_key, flow, is_mask);
|
|
|
|
|
if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = ipv4_key;
|
|
|
|
|
check_len = sizeof *ipv4_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_IPV4;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_IPV6)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_ipv6 *ipv6_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(ipv6_key, flow, is_mask);
|
|
|
|
|
if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = ipv6_key;
|
|
|
|
|
check_len = sizeof *ipv6_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_IPV6;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_ARP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_RARP)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_arp *arp_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow "
|
|
|
|
|
"key", ntohs(arp_key->arp_op));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_arp_key(arp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = arp_key;
|
|
|
|
|
check_len = sizeof *arp_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_ARP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2013-12-06 08:30:17 -08:00
|
|
|
|
if (check_len > 0) { /* Happens only when 'is_mask'. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_all_zeros(check_start, check_len) &&
|
|
|
|
|
flow->dl_type != htons(0xffff)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << expected_bit;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_UNSPEC;
|
|
|
|
|
if (src_flow->nw_proto == IPPROTO_TCP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *tcp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(tcp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_TCP;
|
|
|
|
|
}
|
2013-10-28 13:54:40 -07:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
|
|
|
|
|
flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_UDP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *udp_key;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(udp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_UDP;
|
|
|
|
|
}
|
2013-10-30 00:44:32 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_SCTP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
2013-08-22 20:24:44 +12:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
|
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *sctp_key;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
|
|
|
|
sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(sctp_key, flow);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_SCTP;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_ICMP
|
|
|
|
|
&& src_flow->dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_icmp *icmp_key;
|
|
|
|
|
|
|
|
|
|
icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]);
|
|
|
|
|
flow->tp_src = htons(icmp_key->icmp_type);
|
|
|
|
|
flow->tp_dst = htons(icmp_key->icmp_code);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_ICMP;
|
|
|
|
|
}
|
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_ICMPV6
|
|
|
|
|
&& src_flow->dl_type == htons(ETH_TYPE_IPV6)
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
|
2011-02-01 22:54:11 -08:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_icmpv6 *icmpv6_key;
|
|
|
|
|
|
|
|
|
|
icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]);
|
|
|
|
|
flow->tp_src = htons(icmpv6_key->icmpv6_type);
|
|
|
|
|
flow->tp_dst = htons(icmpv6_key->icmpv6_code);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_ICMPV6;
|
2016-07-02 11:35:29 -07:00
|
|
|
|
if (is_nd(src_flow, NULL)) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_nd *nd_key;
|
|
|
|
|
|
|
|
|
|
nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]);
|
2017-01-04 16:10:56 -08:00
|
|
|
|
flow->nd_target = nd_key->nd_target;
|
2015-08-28 14:55:11 -07:00
|
|
|
|
flow->arp_sha = nd_key->nd_sll;
|
|
|
|
|
flow->arp_tha = nd_key->nd_tll;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
2015-12-08 18:39:18 -08:00
|
|
|
|
/* Even though 'tp_src' and 'tp_dst' are 16 bits wide,
|
|
|
|
|
* ICMP type and code are 8 bits wide. Therefore, an
|
|
|
|
|
* exact match looks like htons(0xff), not
|
|
|
|
|
* htons(0xffff). See xlate_wc_finish() for details.
|
|
|
|
|
* */
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (!is_all_zeros(nd_key, sizeof *nd_key) &&
|
2015-12-08 18:39:18 -08:00
|
|
|
|
(flow->tp_src != htons(0xff) ||
|
|
|
|
|
flow->tp_dst != htons(0xff))) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
|
|
|
|
|
if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << expected_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
done:
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return check_expectations(present_attrs, out_of_range_attr, expected_attrs,
|
|
|
|
|
key, key_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse 802.1Q header then encapsulated L3 attributes. */
|
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs, struct flow *flow,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
const struct nlattr *key, size_t key_len,
|
|
|
|
|
const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
const struct nlattr *encap
|
|
|
|
|
= (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
|
|
|
|
|
? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
|
|
|
|
|
enum odp_key_fitness encap_fitness;
|
|
|
|
|
enum odp_key_fitness fitness;
|
|
|
|
|
|
2013-06-04 17:35:36 -05:00
|
|
|
|
/* Calculate fitness of outer attributes. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
|
|
|
|
|
(UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
|
|
|
|
|
} else {
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
|
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
fitness = check_expectations(present_attrs, out_of_range_attr,
|
|
|
|
|
expected_attrs, key, key_len);
|
|
|
|
|
|
2013-12-31 10:35:27 -08:00
|
|
|
|
/* Set vlan_tci.
|
|
|
|
|
* Remove the TPID from dl_type since it's not the real Ethertype. */
|
|
|
|
|
flow->dl_type = htons(0);
|
|
|
|
|
flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
|
|
|
|
|
? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
|
|
|
|
|
: htons(0));
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
|
|
|
|
} else if (flow->vlan_tci == htons(0)) {
|
|
|
|
|
/* Corner case for a truncated 802.1Q header. */
|
|
|
|
|
if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
|
|
|
|
}
|
|
|
|
|
return fitness;
|
|
|
|
|
} else if (!(flow->vlan_tci & htons(VLAN_CFI))) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
|
|
|
|
|
"but CFI bit is not set", ntohs(flow->vlan_tci));
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else {
|
2013-12-31 10:35:27 -08:00
|
|
|
|
if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
|
|
|
|
|
return fitness;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
/* Now parse the encapsulated attributes. */
|
|
|
|
|
if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
|
|
|
|
|
attrs, &present_attrs, &out_of_range_attr)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
expected_attrs = 0;
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len,
|
|
|
|
|
src_flow);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
/* The overall fitness is the worse of the outer and inner attributes. */
|
|
|
|
|
return MAX(fitness, encap_fitness);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
|
2016-04-19 18:36:04 -07:00
|
|
|
|
struct flow *flow, const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
|
|
|
|
|
uint64_t expected_attrs;
|
|
|
|
|
uint64_t present_attrs;
|
|
|
|
|
int out_of_range_attr;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
memset(flow, 0, sizeof *flow);
|
|
|
|
|
|
|
|
|
|
/* Parse attributes. */
|
|
|
|
|
if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs,
|
|
|
|
|
&out_of_range_attr)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
expected_attrs = 0;
|
|
|
|
|
|
|
|
|
|
/* Metadata. */
|
2014-03-04 15:36:03 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) {
|
|
|
|
|
flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID;
|
|
|
|
|
} else if (is_mask) {
|
2014-05-13 10:07:18 +12:00
|
|
|
|
/* Always exact match recirc_id if it is not specified. */
|
2014-03-04 15:36:03 -08:00
|
|
|
|
flow->recirc_id = UINT32_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_DP_HASH)) {
|
|
|
|
|
flow->dp_hash = nl_attr_get_u32(attrs[OVS_KEY_ATTR_DP_HASH]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_DP_HASH;
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PRIORITY)) {
|
2011-12-21 15:52:23 -08:00
|
|
|
|
flow->skb_priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-13 19:19:36 +02:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK)) {
|
2013-08-06 12:57:13 -07:00
|
|
|
|
flow->pkt_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_SKB_MARK]);
|
2012-11-13 19:19:36 +02:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
|
|
|
|
|
}
|
|
|
|
|
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_STATE)) {
|
|
|
|
|
uint32_t odp_state = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CT_STATE]);
|
|
|
|
|
|
|
|
|
|
flow->ct_state = odp_to_ovs_ct_state(odp_state);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_STATE;
|
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_ZONE)) {
|
|
|
|
|
flow->ct_zone = nl_attr_get_u16(attrs[OVS_KEY_ATTR_CT_ZONE]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_ZONE;
|
|
|
|
|
}
|
Add connection tracking mark support.
This patch adds a new 32-bit metadata field to the connection tracking
interface. When a mark is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_mark" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a mark with those
connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_mark)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_mark=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-18 13:58:00 -07:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_MARK)) {
|
|
|
|
|
flow->ct_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_CT_MARK]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_MARK;
|
|
|
|
|
}
|
Add connection tracking label support.
This patch adds a new 128-bit metadata field to the connection tracking
interface. When a label is specified as part of the ct action and the
connection is committed, the value is saved with the current connection.
Subsequent ct lookups with the table specified will expose this metadata
as the "ct_label" field in the flow.
For example, to allow new TCP connections from port 1->2 and only allow
established connections from port 2->1, and to associate a label with
those connections:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(commit,exec(set_field:1->ct_label)),2
table=0,in_port=2,ct_state=-trk,tcp,action=ct(table=1)
table=1,in_port=2,ct_state=+trk,ct_label=1,tcp,action=1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-10-13 11:13:10 -07:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_CT_LABELS)) {
|
|
|
|
|
const ovs_u128 *cl = nl_attr_get(attrs[OVS_KEY_ATTR_CT_LABELS]);
|
|
|
|
|
|
|
|
|
|
flow->ct_label = *cl;
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_CT_LABELS;
|
|
|
|
|
}
|
Add support for connection tracking.
This patch adds a new action and fields to OVS that allow connection
tracking to be performed. This support works in conjunction with the
Linux kernel support merged into the Linux-4.3 development cycle.
Packets have two possible states with respect to connection tracking:
Untracked packets have not previously passed through the connection
tracker, while tracked packets have previously been through the
connection tracker. For OpenFlow pipeline processing, untracked packets
can become tracked, and they will remain tracked until the end of the
pipeline. Tracked packets cannot become untracked.
Connections can be unknown, uncommitted, or committed. Packets which are
untracked have unknown connection state. To know the connection state,
the packet must become tracked. Uncommitted connections have no
connection state stored about them, so it is only possible for the
connection tracker to identify whether they are a new connection or
whether they are invalid. Committed connections have connection state
stored beyond the lifetime of the packet, which allows later packets in
the same connection to be identified as part of the same established
connection, or related to an existing connection - for instance ICMP
error responses.
The new 'ct' action transitions the packet from "untracked" to
"tracked" by sending this flow through the connection tracker.
The following parameters are supported initally:
- "commit": When commit is executed, the connection moves from
uncommitted state to committed state. This signals that information
about the connection should be stored beyond the lifetime of the
packet within the pipeline. This allows future packets in the same
connection to be recognized as part of the same "established" (est)
connection, as well as identifying packets in the reply (rpl)
direction, or packets related to an existing connection (rel).
- "zone=[u16|NXM]": Perform connection tracking in the zone specified.
Each zone is an independent connection tracking context. When the
"commit" parameter is used, the connection will only be committed in
the specified zone, and not in other zones. This is 0 by default.
- "table=NUMBER": Fork pipeline processing in two. The original instance
of the packet will continue processing the current actions list as an
untracked packet. An additional instance of the packet will be sent to
the connection tracker, which will be re-injected into the OpenFlow
pipeline to resume processing in the specified table, with the
ct_state and other ct match fields set. If the table is not specified,
then the packet is submitted to the connection tracker, but the
pipeline does not fork and the ct match fields are not populated. It
is strongly recommended to specify a table later than the current
table to prevent loops.
When the "table" option is used, the packet that continues processing in
the specified table will have the ct_state populated. The ct_state may
have any of the following flags set:
- Tracked (trk): Connection tracking has occurred.
- Reply (rpl): The flow is in the reply direction.
- Invalid (inv): The connection tracker couldn't identify the connection.
- New (new): This is the beginning of a new connection.
- Established (est): This is part of an already existing connection.
- Related (rel): This connection is related to an existing connection.
For more information, consult the ovs-ofctl(8) man pages.
Below is a simple example flow table to allow outbound TCP traffic from
port 1 and drop traffic from port 2 that was not initiated by port 1:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,ct_state=-trk,action=ct(commit,zone=9),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(zone=9,table=1)
table=1,in_port=2,ct_state=+trk+est,tcp,action=1
table=1,in_port=2,ct_state=+trk+new,tcp,action=drop
Based on original design by Justin Pettit, contributions from Thomas
Graf and Daniele Di Proietto.
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-08-11 10:56:09 -07:00
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
|
|
|
|
|
enum odp_key_fitness res;
|
2012-12-21 10:44:38 +02:00
|
|
|
|
|
2016-04-19 18:36:04 -07:00
|
|
|
|
res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], is_mask,
|
|
|
|
|
&flow->tunnel);
|
2013-01-18 18:10:59 -08:00
|
|
|
|
if (res == ODP_FIT_ERROR) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else if (res == ODP_FIT_PERFECT) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
|
2012-12-21 10:44:38 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
flow->in_port.odp_port
|
|
|
|
|
= nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (!is_mask) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
flow->in_port.odp_port = ODPP_NONE;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ethernet header. */
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
|
|
|
|
|
const struct ovs_key_ethernet *eth_key;
|
|
|
|
|
|
|
|
|
|
eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ethernet_key(eth_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
|
|
|
|
|
src_flow)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-31 10:36:19 -08:00
|
|
|
|
if (is_mask
|
|
|
|
|
? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0
|
|
|
|
|
: src_flow->dl_type == htons(ETH_TYPE_VLAN)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len, src_flow);
|
|
|
|
|
}
|
|
|
|
|
if (is_mask) {
|
2015-09-18 17:47:37 -07:00
|
|
|
|
/* A missing VLAN mask means exact match on vlan_tci 0 (== no VLAN). */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
flow->vlan_tci = htons(0xffff);
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
|
|
|
|
|
flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len, src_flow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
|
|
|
|
|
* structure in 'flow'. Returns an ODP_FIT_* value that indicates how well
|
|
|
|
|
* 'key' fits our expectations for what a flow key should contain.
|
|
|
|
|
*
|
|
|
|
|
* The 'in_port' will be the datapath's understanding of the port. The
|
|
|
|
|
* caller will need to translate with odp_port_to_ofp_port() if the
|
|
|
|
|
* OpenFlow port is needed.
|
|
|
|
|
*
|
|
|
|
|
* This function doesn't take the packet itself as an argument because none of
|
|
|
|
|
* the currently understood OVS_KEY_ATTR_* attributes require it. Currently,
|
|
|
|
|
* it is always possible to infer which additional attribute(s) should appear
|
|
|
|
|
* by looking at the attributes for lower-level protocols, e.g. if the network
|
|
|
|
|
* protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we
|
|
|
|
|
* know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it
|
|
|
|
|
* must be absent. */
|
|
|
|
|
enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct flow *flow)
|
|
|
|
|
{
|
2016-04-19 18:36:04 -07:00
|
|
|
|
return odp_flow_key_to_flow__(key, key_len, flow, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
}
|
|
|
|
|
|
2016-04-19 18:36:04 -07:00
|
|
|
|
/* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key'
|
|
|
|
|
* to a mask structure in 'mask'. 'flow' must be a previously translated flow
|
|
|
|
|
* corresponding to 'mask' and similarly flow_key/flow_key_len must be the
|
|
|
|
|
* attributes from that flow. Returns an ODP_FIT_* value that indicates how
|
|
|
|
|
* well 'key' fits our expectations for what a flow key should contain. */
|
|
|
|
|
enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
|
|
|
|
|
struct flow_wildcards *mask, const struct flow *src_flow)
|
2015-12-07 17:30:25 -08:00
|
|
|
|
{
|
|
|
|
|
if (mask_key_len) {
|
|
|
|
|
return odp_flow_key_to_flow__(mask_key, mask_key_len,
|
2016-04-19 18:36:04 -07:00
|
|
|
|
&mask->masks, src_flow);
|
2015-12-07 17:30:25 -08:00
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
/* A missing mask means that the flow should be exact matched.
|
|
|
|
|
* Generate an appropriate exact wildcard for the flow. */
|
|
|
|
|
flow_wildcards_init_for_packet(mask, src_flow);
|
|
|
|
|
|
|
|
|
|
return ODP_FIT_PERFECT;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-25 16:54:42 -07:00
|
|
|
|
|
2012-01-16 12:37:44 -08:00
|
|
|
|
/* Returns 'fitness' as a string, for use in debug messages. */
|
|
|
|
|
const char *
|
|
|
|
|
odp_key_fitness_to_string(enum odp_key_fitness fitness)
|
|
|
|
|
{
|
|
|
|
|
switch (fitness) {
|
|
|
|
|
case ODP_FIT_PERFECT:
|
|
|
|
|
return "OK";
|
|
|
|
|
case ODP_FIT_TOO_MUCH:
|
|
|
|
|
return "too_much";
|
|
|
|
|
case ODP_FIT_TOO_LITTLE:
|
|
|
|
|
return "too_little";
|
|
|
|
|
case ODP_FIT_ERROR:
|
|
|
|
|
return "error";
|
|
|
|
|
default:
|
|
|
|
|
return "<unknown>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-25 16:54:42 -07:00
|
|
|
|
/* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies
|
2013-02-15 16:48:32 -08:00
|
|
|
|
* Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute
|
|
|
|
|
* whose contents are the 'userdata_size' bytes at 'userdata' and returns the
|
|
|
|
|
* offset within 'odp_actions' of the start of the cookie. (If 'userdata' is
|
|
|
|
|
* null, then the return value is not meaningful.) */
|
2011-10-25 16:54:42 -07:00
|
|
|
|
size_t
|
2013-02-15 16:48:32 -08:00
|
|
|
|
odp_put_userspace_action(uint32_t pid,
|
|
|
|
|
const void *userdata, size_t userdata_size,
|
2014-08-17 20:19:36 -07:00
|
|
|
|
odp_port_t tunnel_out_port,
|
2015-07-17 21:37:02 -07:00
|
|
|
|
bool include_actions,
|
2011-10-25 16:54:42 -07:00
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
2013-02-15 16:48:32 -08:00
|
|
|
|
size_t userdata_ofs;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
|
|
offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE);
|
|
|
|
|
nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
|
2013-02-15 16:48:32 -08:00
|
|
|
|
if (userdata) {
|
2015-03-02 17:29:44 -08:00
|
|
|
|
userdata_ofs = odp_actions->size + NLA_HDRLEN;
|
2013-10-07 14:26:28 -07:00
|
|
|
|
|
|
|
|
|
/* The OVS kernel module before OVS 1.11 and the upstream Linux kernel
|
|
|
|
|
* module before Linux 3.10 required the userdata to be exactly 8 bytes
|
|
|
|
|
* long:
|
|
|
|
|
*
|
|
|
|
|
* - The kernel rejected shorter userdata with -ERANGE.
|
|
|
|
|
*
|
|
|
|
|
* - The kernel silently dropped userdata beyond the first 8 bytes.
|
|
|
|
|
*
|
|
|
|
|
* Thus, for maximum compatibility, always put at least 8 bytes. (We
|
|
|
|
|
* separately disable features that required more than 8 bytes.) */
|
|
|
|
|
memcpy(nl_msg_put_unspec_zero(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
|
|
|
|
|
MAX(8, userdata_size)),
|
|
|
|
|
userdata, userdata_size);
|
2013-02-15 16:48:32 -08:00
|
|
|
|
} else {
|
|
|
|
|
userdata_ofs = 0;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
}
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (tunnel_out_port != ODPP_NONE) {
|
|
|
|
|
nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,
|
|
|
|
|
tunnel_out_port);
|
|
|
|
|
}
|
2015-07-17 21:37:02 -07:00
|
|
|
|
if (include_actions) {
|
|
|
|
|
nl_msg_put_flag(odp_actions, OVS_USERSPACE_ATTR_ACTIONS);
|
|
|
|
|
}
|
2011-10-25 16:54:42 -07:00
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
|
2013-02-15 16:48:32 -08:00
|
|
|
|
return userdata_ofs;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
}
|
2012-12-14 19:14:54 -08:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_put_tunnel_action(const struct flow_tnl *tunnel,
|
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
|
2015-06-19 13:54:13 -07:00
|
|
|
|
tun_key_to_attr(odp_actions, tunnel, tunnel, NULL);
|
2012-12-14 19:14:54 -08:00
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_put_tnl_push_action(struct ofpbuf *odp_actions,
|
|
|
|
|
struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
int size = offsetof(struct ovs_action_push_tnl, header);
|
|
|
|
|
|
|
|
|
|
size += data->header_len;
|
|
|
|
|
nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_PUSH, data, size);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
|
|
|
|
/* The commit_odp_actions() function and its helpers. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
|
|
|
|
|
const void *key, size_t key_size)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
|
|
|
|
|
nl_msg_put_unspec(odp_actions, key_type, key, key_size);
|
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:19 -07:00
|
|
|
|
/* Masked set actions have a mask following the data within the netlink
|
|
|
|
|
* attribute. The unmasked bits in the data will be cleared as the data
|
|
|
|
|
* is copied to the action. */
|
|
|
|
|
void
|
|
|
|
|
commit_masked_set_action(struct ofpbuf *odp_actions,
|
|
|
|
|
enum ovs_key_attr key_type,
|
|
|
|
|
const void *key_, const void *mask_, size_t key_size)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions,
|
|
|
|
|
OVS_ACTION_ATTR_SET_MASKED);
|
|
|
|
|
char *data = nl_msg_put_unspec_uninit(odp_actions, key_type, key_size * 2);
|
|
|
|
|
const char *key = key_, *mask = mask_;
|
|
|
|
|
|
|
|
|
|
memcpy(data + key_size, mask, key_size);
|
|
|
|
|
/* Clear unmasked bits while copying. */
|
|
|
|
|
while (key_size--) {
|
|
|
|
|
*data++ = *key++ & *mask++;
|
|
|
|
|
}
|
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-14 19:14:54 -08:00
|
|
|
|
/* If any of the flow key data that ODP actions can modify are different in
|
|
|
|
|
* 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
|
|
|
|
|
* 'odp_actions' that change the flow tunneling information in key from
|
|
|
|
|
* 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
|
|
|
|
|
* same way. In other words, operates the same as commit_odp_actions(), but
|
|
|
|
|
* only on tunneling information. */
|
|
|
|
|
void
|
|
|
|
|
commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
|
2011-11-28 14:14:23 -08:00
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
2015-11-25 11:31:11 -02:00
|
|
|
|
/* A valid IPV4_TUNNEL must have non-zero ip_dst; a valid IPv6 tunnel
|
|
|
|
|
* must have non-zero ipv6_dst. */
|
|
|
|
|
if (flow_tnl_dst_is_set(&flow->tunnel)) {
|
2013-04-18 18:07:39 +03:00
|
|
|
|
if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
|
2012-12-14 19:14:54 -08:00
|
|
|
|
odp_put_tunnel_action(&base->tunnel, odp_actions);
|
2012-12-21 10:44:38 +02:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static bool
|
|
|
|
|
commit(enum ovs_key_attr attr, bool use_masked_set,
|
|
|
|
|
const void *key, void *base, void *mask, size_t size,
|
|
|
|
|
struct ofpbuf *odp_actions)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (memcmp(key, base, size)) {
|
|
|
|
|
bool fully_masked = odp_mask_is_exact(attr, mask, size);
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (use_masked_set && !fully_masked) {
|
|
|
|
|
commit_masked_set_action(odp_actions, attr, key, mask, size);
|
|
|
|
|
} else {
|
|
|
|
|
if (!fully_masked) {
|
|
|
|
|
memset(mask, 0xff, size);
|
|
|
|
|
}
|
|
|
|
|
commit_set_action(odp_actions, attr, key, size);
|
|
|
|
|
}
|
|
|
|
|
memcpy(base, key, size);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
/* Mask bits are set when we have either read or set the corresponding
|
|
|
|
|
* values. Masked bits will be exact-matched, no need to set them
|
|
|
|
|
* if the value did not actually change. */
|
|
|
|
|
return false;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
get_ethernet_key(const struct flow *flow, struct ovs_key_ethernet *eth)
|
|
|
|
|
{
|
2015-08-28 14:55:11 -07:00
|
|
|
|
eth->eth_src = flow->dl_src;
|
|
|
|
|
eth->eth_dst = flow->dl_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
|
|
|
|
|
{
|
2015-08-28 14:55:11 -07:00
|
|
|
|
flow->dl_src = eth->eth_src;
|
|
|
|
|
flow->dl_dst = eth->eth_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions,
|
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ethernet key, base, mask;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_ethernet_key(flow, &key);
|
|
|
|
|
get_ethernet_key(base_flow, &base);
|
|
|
|
|
get_ethernet_key(&wc->masks, &mask);
|
|
|
|
|
|
|
|
|
|
if (commit(OVS_KEY_ATTR_ETHERNET, use_masked,
|
|
|
|
|
&key, &base, &mask, sizeof key, odp_actions)) {
|
|
|
|
|
put_ethernet_key(&base, base_flow);
|
|
|
|
|
put_ethernet_key(&mask, &wc->masks);
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-02-04 10:32:35 -08:00
|
|
|
|
pop_vlan(struct flow *base,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2013-06-18 23:55:47 -07:00
|
|
|
|
memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
if (base->vlan_tci & htons(VLAN_CFI)) {
|
|
|
|
|
nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
base->vlan_tci = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
if (base->vlan_tci == vlan_tci) {
|
|
|
|
|
return;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
pop_vlan(base, odp_actions, wc);
|
2013-09-27 09:18:30 +09:00
|
|
|
|
if (vlan_tci & htons(VLAN_CFI)) {
|
2011-11-28 14:14:23 -08:00
|
|
|
|
struct ovs_action_push_vlan vlan;
|
|
|
|
|
|
|
|
|
|
vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
|
2013-09-27 09:18:30 +09:00
|
|
|
|
vlan.vlan_tci = vlan_tci;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
|
|
|
|
|
&vlan, sizeof vlan);
|
|
|
|
|
}
|
2013-09-27 09:18:30 +09:00
|
|
|
|
base->vlan_tci = vlan_tci;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 13:34:43 -07:00
|
|
|
|
/* Wildcarding already done at action translation time. */
|
2013-01-25 16:22:07 +09:00
|
|
|
|
static void
|
|
|
|
|
commit_mpls_action(const struct flow *flow, struct flow *base,
|
2014-09-30 13:34:43 -07:00
|
|
|
|
struct ofpbuf *odp_actions)
|
2013-01-25 16:22:07 +09:00
|
|
|
|
{
|
2014-09-30 13:34:43 -07:00
|
|
|
|
int base_n = flow_count_mpls_labels(base, NULL);
|
|
|
|
|
int flow_n = flow_count_mpls_labels(flow, NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n,
|
2014-09-30 13:34:43 -07:00
|
|
|
|
NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
|
|
|
|
while (base_n > common_n) {
|
|
|
|
|
if (base_n - 1 == common_n && flow_n > common_n) {
|
|
|
|
|
/* If there is only one more LSE in base than there are common
|
|
|
|
|
* between base and flow; and flow has at least one more LSE than
|
|
|
|
|
* is common then the topmost LSE of base may be updated using
|
|
|
|
|
* set */
|
|
|
|
|
struct ovs_key_mpls mpls_key;
|
|
|
|
|
|
|
|
|
|
mpls_key.mpls_lse = flow->mpls_lse[flow_n - base_n];
|
|
|
|
|
commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
|
|
|
|
|
&mpls_key, sizeof mpls_key);
|
|
|
|
|
flow_set_mpls_lse(base, 0, mpls_key.mpls_lse);
|
|
|
|
|
common_n++;
|
|
|
|
|
} else {
|
|
|
|
|
/* Otherwise, if there more LSEs in base than are common between
|
|
|
|
|
* base and flow then pop the topmost one. */
|
|
|
|
|
ovs_be16 dl_type;
|
|
|
|
|
bool popped;
|
|
|
|
|
|
|
|
|
|
/* If all the LSEs are to be popped and this is not the outermost
|
|
|
|
|
* LSE then use ETH_TYPE_MPLS as the ethertype parameter of the
|
|
|
|
|
* POP_MPLS action instead of flow->dl_type.
|
|
|
|
|
*
|
|
|
|
|
* This is because the POP_MPLS action requires its ethertype
|
|
|
|
|
* argument to be an MPLS ethernet type but in this case
|
|
|
|
|
* flow->dl_type will be a non-MPLS ethernet type.
|
|
|
|
|
*
|
|
|
|
|
* When the final POP_MPLS action occurs it use flow->dl_type and
|
|
|
|
|
* the and the resulting packet will have the desired dl_type. */
|
|
|
|
|
if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) {
|
|
|
|
|
dl_type = htons(ETH_TYPE_MPLS);
|
|
|
|
|
} else {
|
|
|
|
|
dl_type = flow->dl_type;
|
|
|
|
|
}
|
|
|
|
|
nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type);
|
2014-09-30 13:34:43 -07:00
|
|
|
|
popped = flow_pop_mpls(base, base_n, flow->dl_type, NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
ovs_assert(popped);
|
|
|
|
|
base_n--;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
/* If, after the above popping and setting, there are more LSEs in flow
|
|
|
|
|
* than base then some LSEs need to be pushed. */
|
|
|
|
|
while (base_n < flow_n) {
|
2013-01-25 16:22:07 +09:00
|
|
|
|
struct ovs_action_push_mpls *mpls;
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
mpls = nl_msg_put_unspec_zero(odp_actions,
|
|
|
|
|
OVS_ACTION_ATTR_PUSH_MPLS,
|
2013-10-07 14:11:40 -07:00
|
|
|
|
sizeof *mpls);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
mpls->mpls_ethertype = flow->dl_type;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
|
mpls: Fix MPLS restoration after patch port and group bucket.
This patch fixes problems with MPLS handling related to patch ports
and group buckets.
If a group bucket or a peer bridge across a patch port pushes MPLS
headers to a non-MPLS packet and outputs, the flow translation after
returning from the group bucket or patch port would undo the packet
transformations so that the processing could continue with the packet
as it was before entering the patch port. There were two problems
with this:
1. As part of the first MPLS push on a non-MPLS packet, the flow
translation would first clear the L3/4 headers of the 'flow' to mark
those fields invalid. Later, when committing 'flow' changes to
datapath actions before output, the necessary datapath MPLS actions
are created and the corresponding changes updated to the 'base flow'.
This was done using the same flow_push_mpls() function that clears
the L2/3 headers, so also the 'base flow' L2/3 headers were cleared.
Then, when translation returns from a patch port or group bucket, the
original 'flow' is restored, now showing no sign of the MPLS labels.
Since the 'base flow' now has the MPLS labels, following translations
know to issue MPLS POP actions before any output actions. However, as
part of checking for changes to IP headers we test that the IP
protocol type was not changed. But now the 'base flow's 'nw_proto'
field is zero and an assert fail crashes OVS.
This is solved by not clearing the L3/4 fields of the 'base
flow'. This allows the processing after the patch port to continue
with L3/4 fields as if no MPLS was done, after first issuing the
necessary MPLS POP actions.
2. IP header updates were done before the MPLS POP actions were
issued. This caused incorrect packet output after, e.g., group action
or patch port. For example, with actions:
group 1234: all bucket=push_mpls,output:LOCAL
ip actions=group:1234,dec_ttl,output:LOCAL,output:LOCAL
the dec_ttl would only be executed before the last output to LOCAL,
since at the time of committing IP changes after the group action the
packet was still an MPLS packet.
This is solved by checking the dl_type of both 'flow' and 'base flow'
and issuing MPLS actions if they can transform the packet from an MPLS
packet to a non-MPLS packet. For an IP packet the change in ttl can
then be correctly committed before the last two output actions.
Two test cases are added to prevent future regressions.
Reported-by: Thomas Morin <thomas.morin@orange.com>
Suggested-by: Takashi YAMAMOTO <yamamoto@ovn.org>
Fixes: 8bfd0fdac ("Enhance userspace support for MPLS, for up to 3 labels.")
Fixes: 1b035ef20 ("mpls: Allow l3 and l4 actions to prior to a push_mpls action")
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: YAMAMOTO Takashi <yamamoto@ovn.org>
2016-12-01 14:05:24 -08:00
|
|
|
|
/* Update base flow's MPLS stack, but do not clear L3. We need the L3
|
|
|
|
|
* headers if the flow is restored later due to returning from a patch
|
|
|
|
|
* port or group bucket. */
|
|
|
|
|
flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL, false);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
flow_set_mpls_lse(base, 0, mpls->mpls_lse);
|
|
|
|
|
base_n++;
|
2013-09-27 06:55:19 +09:00
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(const struct flow *flow, struct ovs_key_ipv4 *ipv4, bool is_mask)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
ipv4->ipv4_src = flow->nw_src;
|
|
|
|
|
ipv4->ipv4_dst = flow->nw_dst;
|
|
|
|
|
ipv4->ipv4_proto = flow->nw_proto;
|
|
|
|
|
ipv4->ipv4_tos = flow->nw_tos;
|
|
|
|
|
ipv4->ipv4_ttl = flow->nw_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ipv4->ipv4_frag = ovs_to_odp_frag(flow->nw_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(const struct ovs_key_ipv4 *ipv4, struct flow *flow, bool is_mask)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
|
|
|
|
flow->nw_src = ipv4->ipv4_src;
|
|
|
|
|
flow->nw_dst = ipv4->ipv4_dst;
|
|
|
|
|
flow->nw_proto = ipv4->ipv4_proto;
|
|
|
|
|
flow->nw_tos = ipv4->ipv4_tos;
|
|
|
|
|
flow->nw_ttl = ipv4->ipv4_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->nw_frag = odp_to_ovs_frag(ipv4->ipv4_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_ipv4_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ipv4 key, mask, base;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* Check that nw_proto and nw_frag remain unchanged. */
|
|
|
|
|
ovs_assert(flow->nw_proto == base_flow->nw_proto &&
|
|
|
|
|
flow->nw_frag == base_flow->nw_frag);
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(flow, &key, false);
|
|
|
|
|
get_ipv4_key(base_flow, &base, false);
|
|
|
|
|
get_ipv4_key(&wc->masks, &mask, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask.ipv4_proto = 0; /* Not writeable. */
|
|
|
|
|
mask.ipv4_frag = 0; /* Not writable. */
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_IPV4, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(&base, base_flow, false);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (mask.ipv4_proto != 0) { /* Mask was changed by commit(). */
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(&mask, &wc->masks, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-11 12:44:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(const struct flow *flow, struct ovs_key_ipv6 *ipv6, bool is_mask)
|
2012-01-11 12:44:23 -08:00
|
|
|
|
{
|
2017-01-04 16:10:56 -08:00
|
|
|
|
ipv6->ipv6_src = flow->ipv6_src;
|
|
|
|
|
ipv6->ipv6_dst = flow->ipv6_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
ipv6->ipv6_label = flow->ipv6_label;
|
|
|
|
|
ipv6->ipv6_proto = flow->nw_proto;
|
|
|
|
|
ipv6->ipv6_tclass = flow->nw_tos;
|
|
|
|
|
ipv6->ipv6_hlimit = flow->nw_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ipv6->ipv6_frag = ovs_to_odp_frag(flow->nw_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(const struct ovs_key_ipv6 *ipv6, struct flow *flow, bool is_mask)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
2017-01-04 16:10:56 -08:00
|
|
|
|
flow->ipv6_src = ipv6->ipv6_src;
|
|
|
|
|
flow->ipv6_dst = ipv6->ipv6_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
flow->ipv6_label = ipv6->ipv6_label;
|
|
|
|
|
flow->nw_proto = ipv6->ipv6_proto;
|
|
|
|
|
flow->nw_tos = ipv6->ipv6_tclass;
|
|
|
|
|
flow->nw_ttl = ipv6->ipv6_hlimit;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->nw_frag = odp_to_ovs_frag(ipv6->ipv6_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
commit_set_ipv6_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ipv6 key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* Check that nw_proto and nw_frag remain unchanged. */
|
|
|
|
|
ovs_assert(flow->nw_proto == base_flow->nw_proto &&
|
|
|
|
|
flow->nw_frag == base_flow->nw_frag);
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(flow, &key, false);
|
|
|
|
|
get_ipv6_key(base_flow, &base, false);
|
|
|
|
|
get_ipv6_key(&wc->masks, &mask, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask.ipv6_proto = 0; /* Not writeable. */
|
|
|
|
|
mask.ipv6_frag = 0; /* Not writable. */
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_IPV6, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(&base, base_flow, false);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (mask.ipv6_proto != 0) { /* Mask was changed by commit(). */
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(&mask, &wc->masks, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
get_arp_key(const struct flow *flow, struct ovs_key_arp *arp)
|
2013-10-09 17:37:30 -07:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* ARP key has padding, clear it. */
|
|
|
|
|
memset(arp, 0, sizeof *arp);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
arp->arp_sip = flow->nw_src;
|
|
|
|
|
arp->arp_tip = flow->nw_dst;
|
|
|
|
|
arp->arp_op = htons(flow->nw_proto);
|
2015-08-28 14:55:11 -07:00
|
|
|
|
arp->arp_sha = flow->arp_sha;
|
|
|
|
|
arp->arp_tha = flow->arp_tha;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
put_arp_key(const struct ovs_key_arp *arp, struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
flow->nw_src = arp->arp_sip;
|
|
|
|
|
flow->nw_dst = arp->arp_tip;
|
|
|
|
|
flow->nw_proto = ntohs(arp->arp_op);
|
2015-08-28 14:55:11 -07:00
|
|
|
|
flow->arp_sha = arp->arp_sha;
|
|
|
|
|
flow->arp_tha = arp->arp_tha;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static enum slow_path_reason
|
|
|
|
|
commit_set_arp_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_arp key, mask, base;
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_arp_key(flow, &key);
|
|
|
|
|
get_arp_key(base_flow, &base);
|
|
|
|
|
get_arp_key(&wc->masks, &mask);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_ARP, true, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_arp_key(&base, base_flow);
|
|
|
|
|
put_arp_key(&mask, &wc->masks);
|
|
|
|
|
return SLOW_ACTION;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2013-10-09 17:37:30 -07:00
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 22:03:02 -07:00
|
|
|
|
static void
|
|
|
|
|
get_icmp_key(const struct flow *flow, struct ovs_key_icmp *icmp)
|
|
|
|
|
{
|
|
|
|
|
/* icmp_type and icmp_code are stored in tp_src and tp_dst, respectively */
|
|
|
|
|
icmp->icmp_type = ntohs(flow->tp_src);
|
|
|
|
|
icmp->icmp_code = ntohs(flow->tp_dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
put_icmp_key(const struct ovs_key_icmp *icmp, struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
/* icmp_type and icmp_code are stored in tp_src and tp_dst, respectively */
|
|
|
|
|
flow->tp_src = htons(icmp->icmp_type);
|
|
|
|
|
flow->tp_dst = htons(icmp->icmp_code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum slow_path_reason
|
|
|
|
|
commit_set_icmp_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_icmp key, mask, base;
|
|
|
|
|
enum ovs_key_attr attr;
|
|
|
|
|
|
2016-05-08 10:34:10 -07:00
|
|
|
|
if (is_icmpv4(flow, NULL)) {
|
2015-12-08 15:44:51 -08:00
|
|
|
|
attr = OVS_KEY_ATTR_ICMP;
|
2016-05-08 10:34:10 -07:00
|
|
|
|
} else if (is_icmpv6(flow, NULL)) {
|
2015-12-08 15:44:51 -08:00
|
|
|
|
attr = OVS_KEY_ATTR_ICMPV6;
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 22:03:02 -07:00
|
|
|
|
get_icmp_key(flow, &key);
|
|
|
|
|
get_icmp_key(base_flow, &base);
|
|
|
|
|
get_icmp_key(&wc->masks, &mask);
|
|
|
|
|
|
|
|
|
|
if (commit(attr, false, &key, &base, &mask, sizeof key, odp_actions)) {
|
|
|
|
|
put_icmp_key(&base, base_flow);
|
|
|
|
|
put_icmp_key(&mask, &wc->masks);
|
|
|
|
|
return SLOW_ACTION;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-23 23:42:05 +00:00
|
|
|
|
static void
|
|
|
|
|
get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
|
|
|
|
|
{
|
2017-01-04 16:10:56 -08:00
|
|
|
|
nd->nd_target = flow->nd_target;
|
2014-12-23 23:42:05 +00:00
|
|
|
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
2015-08-28 14:55:11 -07:00
|
|
|
|
nd->nd_sll = flow->arp_sha;
|
|
|
|
|
nd->nd_tll = flow->arp_tha;
|
2014-12-23 23:42:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
|
|
|
|
|
{
|
2017-01-04 16:10:56 -08:00
|
|
|
|
flow->nd_target = nd->nd_target;
|
2014-12-23 23:42:05 +00:00
|
|
|
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
2015-08-28 14:55:11 -07:00
|
|
|
|
flow->arp_sha = nd->nd_sll;
|
|
|
|
|
flow->arp_tha = nd->nd_tll;
|
2014-12-23 23:42:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum slow_path_reason
|
|
|
|
|
commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions,
|
|
|
|
|
struct flow_wildcards *wc, bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_nd key, mask, base;
|
|
|
|
|
|
|
|
|
|
get_nd_key(flow, &key);
|
|
|
|
|
get_nd_key(base_flow, &base);
|
|
|
|
|
get_nd_key(&wc->masks, &mask);
|
|
|
|
|
|
|
|
|
|
if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_nd_key(&base, base_flow);
|
|
|
|
|
put_nd_key(&mask, &wc->masks);
|
|
|
|
|
return SLOW_ACTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 17:37:30 -07:00
|
|
|
|
static enum slow_path_reason
|
2012-01-11 12:44:23 -08:00
|
|
|
|
commit_set_nw_action(const struct flow *flow, struct flow *base,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2012-01-11 12:44:23 -08:00
|
|
|
|
{
|
2013-10-09 17:37:30 -07:00
|
|
|
|
/* Check if 'flow' really has an L3 header. */
|
2012-01-11 12:44:23 -08:00
|
|
|
|
if (!flow->nw_proto) {
|
2013-10-09 17:37:30 -07:00
|
|
|
|
return 0;
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 17:37:30 -07:00
|
|
|
|
switch (ntohs(base->dl_type)) {
|
|
|
|
|
case ETH_TYPE_IP:
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ipv4_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ETH_TYPE_IPV6:
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
|
2014-12-23 23:42:05 +00:00
|
|
|
|
return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
|
|
|
|
case ETH_TYPE_ARP:
|
|
|
|
|
return commit_set_arp_action(flow, base, odp_actions, wc);
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
|
|
|
|
return 0;
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* TCP, UDP, and SCTP keys have the same layout. */
|
|
|
|
|
BUILD_ASSERT_DECL(sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_udp) &&
|
|
|
|
|
sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_sctp));
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(const struct flow *flow, union ovs_key_tp *tp)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
tp->tcp.tcp_src = flow->tp_src;
|
|
|
|
|
tp->tcp.tcp_dst = flow->tp_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(const union ovs_key_tp *tp, struct flow *flow)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->tp_src = tp->tcp.tcp_src;
|
|
|
|
|
flow->tp_dst = tp->tcp.tcp_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_port_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
enum ovs_key_attr key_type;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp key, mask, base;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
|
2014-06-04 16:56:09 +00:00
|
|
|
|
/* Check if 'flow' really has an L3 header. */
|
|
|
|
|
if (!flow->nw_proto) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (!is_ip_any(base_flow)) {
|
2011-11-28 14:14:23 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flow->nw_proto == IPPROTO_TCP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_TCP;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_UDP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_UDP;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_SCTP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_SCTP;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_tp_key(flow, &key);
|
|
|
|
|
get_tp_key(base_flow, &base);
|
|
|
|
|
get_tp_key(&wc->masks, &mask);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(key_type, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_tp_key(&base, base_flow);
|
|
|
|
|
put_tp_key(&mask, &wc->masks);
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_priority_action(const struct flow *flow, struct flow *base_flow,
|
2013-06-18 23:55:47 -07:00
|
|
|
|
struct ofpbuf *odp_actions,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
uint32_t key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key = flow->skb_priority;
|
|
|
|
|
base = base_flow->skb_priority;
|
|
|
|
|
mask = wc->masks.skb_priority;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_PRIORITY, use_masked, &key, &base, &mask,
|
|
|
|
|
sizeof key, odp_actions)) {
|
|
|
|
|
base_flow->skb_priority = base;
|
|
|
|
|
wc->masks.skb_priority = mask;
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-13 19:19:36 +02:00
|
|
|
|
static void
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
|
2013-06-18 23:55:47 -07:00
|
|
|
|
struct ofpbuf *odp_actions,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2012-11-13 19:19:36 +02:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
uint32_t key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key = flow->pkt_mark;
|
|
|
|
|
base = base_flow->pkt_mark;
|
|
|
|
|
mask = wc->masks.pkt_mark;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_SKB_MARK, use_masked, &key, &base, &mask,
|
|
|
|
|
sizeof key, odp_actions)) {
|
|
|
|
|
base_flow->pkt_mark = base;
|
|
|
|
|
wc->masks.pkt_mark = mask;
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2013-10-09 17:28:05 -07:00
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
/* If any of the flow key data that ODP actions can modify are different in
|
|
|
|
|
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
|
2012-12-14 19:14:54 -08:00
|
|
|
|
* key from 'base' into 'flow', and then changes 'base' the same way. Does not
|
|
|
|
|
* commit set_tunnel actions. Users should call commit_odp_tunnel_action()
|
2013-06-18 23:55:47 -07:00
|
|
|
|
* in addition to this function if needed. Sets fields in 'wc' that are
|
2013-10-09 17:28:05 -07:00
|
|
|
|
* used as part of the action.
|
|
|
|
|
*
|
|
|
|
|
* Returns a reason to force processing the flow's packets into the userspace
|
|
|
|
|
* slow path, if there is one, otherwise 0. */
|
|
|
|
|
enum slow_path_reason
|
2011-11-28 14:14:23 -08:00
|
|
|
|
commit_odp_actions(const struct flow *flow, struct flow *base,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2015-10-20 22:03:02 -07:00
|
|
|
|
enum slow_path_reason slow1, slow2;
|
mpls: Fix MPLS restoration after patch port and group bucket.
This patch fixes problems with MPLS handling related to patch ports
and group buckets.
If a group bucket or a peer bridge across a patch port pushes MPLS
headers to a non-MPLS packet and outputs, the flow translation after
returning from the group bucket or patch port would undo the packet
transformations so that the processing could continue with the packet
as it was before entering the patch port. There were two problems
with this:
1. As part of the first MPLS push on a non-MPLS packet, the flow
translation would first clear the L3/4 headers of the 'flow' to mark
those fields invalid. Later, when committing 'flow' changes to
datapath actions before output, the necessary datapath MPLS actions
are created and the corresponding changes updated to the 'base flow'.
This was done using the same flow_push_mpls() function that clears
the L2/3 headers, so also the 'base flow' L2/3 headers were cleared.
Then, when translation returns from a patch port or group bucket, the
original 'flow' is restored, now showing no sign of the MPLS labels.
Since the 'base flow' now has the MPLS labels, following translations
know to issue MPLS POP actions before any output actions. However, as
part of checking for changes to IP headers we test that the IP
protocol type was not changed. But now the 'base flow's 'nw_proto'
field is zero and an assert fail crashes OVS.
This is solved by not clearing the L3/4 fields of the 'base
flow'. This allows the processing after the patch port to continue
with L3/4 fields as if no MPLS was done, after first issuing the
necessary MPLS POP actions.
2. IP header updates were done before the MPLS POP actions were
issued. This caused incorrect packet output after, e.g., group action
or patch port. For example, with actions:
group 1234: all bucket=push_mpls,output:LOCAL
ip actions=group:1234,dec_ttl,output:LOCAL,output:LOCAL
the dec_ttl would only be executed before the last output to LOCAL,
since at the time of committing IP changes after the group action the
packet was still an MPLS packet.
This is solved by checking the dl_type of both 'flow' and 'base flow'
and issuing MPLS actions if they can transform the packet from an MPLS
packet to a non-MPLS packet. For an IP packet the change in ttl can
then be correctly committed before the last two output actions.
Two test cases are added to prevent future regressions.
Reported-by: Thomas Morin <thomas.morin@orange.com>
Suggested-by: Takashi YAMAMOTO <yamamoto@ovn.org>
Fixes: 8bfd0fdac ("Enhance userspace support for MPLS, for up to 3 labels.")
Fixes: 1b035ef20 ("mpls: Allow l3 and l4 actions to prior to a push_mpls action")
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: YAMAMOTO Takashi <yamamoto@ovn.org>
2016-12-01 14:05:24 -08:00
|
|
|
|
bool mpls_done = false;
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
|
mpls: Fix MPLS restoration after patch port and group bucket.
This patch fixes problems with MPLS handling related to patch ports
and group buckets.
If a group bucket or a peer bridge across a patch port pushes MPLS
headers to a non-MPLS packet and outputs, the flow translation after
returning from the group bucket or patch port would undo the packet
transformations so that the processing could continue with the packet
as it was before entering the patch port. There were two problems
with this:
1. As part of the first MPLS push on a non-MPLS packet, the flow
translation would first clear the L3/4 headers of the 'flow' to mark
those fields invalid. Later, when committing 'flow' changes to
datapath actions before output, the necessary datapath MPLS actions
are created and the corresponding changes updated to the 'base flow'.
This was done using the same flow_push_mpls() function that clears
the L2/3 headers, so also the 'base flow' L2/3 headers were cleared.
Then, when translation returns from a patch port or group bucket, the
original 'flow' is restored, now showing no sign of the MPLS labels.
Since the 'base flow' now has the MPLS labels, following translations
know to issue MPLS POP actions before any output actions. However, as
part of checking for changes to IP headers we test that the IP
protocol type was not changed. But now the 'base flow's 'nw_proto'
field is zero and an assert fail crashes OVS.
This is solved by not clearing the L3/4 fields of the 'base
flow'. This allows the processing after the patch port to continue
with L3/4 fields as if no MPLS was done, after first issuing the
necessary MPLS POP actions.
2. IP header updates were done before the MPLS POP actions were
issued. This caused incorrect packet output after, e.g., group action
or patch port. For example, with actions:
group 1234: all bucket=push_mpls,output:LOCAL
ip actions=group:1234,dec_ttl,output:LOCAL,output:LOCAL
the dec_ttl would only be executed before the last output to LOCAL,
since at the time of committing IP changes after the group action the
packet was still an MPLS packet.
This is solved by checking the dl_type of both 'flow' and 'base flow'
and issuing MPLS actions if they can transform the packet from an MPLS
packet to a non-MPLS packet. For an IP packet the change in ttl can
then be correctly committed before the last two output actions.
Two test cases are added to prevent future regressions.
Reported-by: Thomas Morin <thomas.morin@orange.com>
Suggested-by: Takashi YAMAMOTO <yamamoto@ovn.org>
Fixes: 8bfd0fdac ("Enhance userspace support for MPLS, for up to 3 labels.")
Fixes: 1b035ef20 ("mpls: Allow l3 and l4 actions to prior to a push_mpls action")
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: YAMAMOTO Takashi <yamamoto@ovn.org>
2016-12-01 14:05:24 -08:00
|
|
|
|
/* Make packet a non-MPLS packet before committing L3/4 actions,
|
|
|
|
|
* which would otherwise do nothing. */
|
|
|
|
|
if (eth_type_mpls(base->dl_type) && !eth_type_mpls(flow->dl_type)) {
|
|
|
|
|
commit_mpls_action(flow, base, odp_actions);
|
|
|
|
|
mpls_done = true;
|
|
|
|
|
}
|
2015-10-20 22:03:02 -07:00
|
|
|
|
slow1 = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_port_action(flow, base, odp_actions, wc, use_masked);
|
2015-10-20 22:03:02 -07:00
|
|
|
|
slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
|
mpls: Fix MPLS restoration after patch port and group bucket.
This patch fixes problems with MPLS handling related to patch ports
and group buckets.
If a group bucket or a peer bridge across a patch port pushes MPLS
headers to a non-MPLS packet and outputs, the flow translation after
returning from the group bucket or patch port would undo the packet
transformations so that the processing could continue with the packet
as it was before entering the patch port. There were two problems
with this:
1. As part of the first MPLS push on a non-MPLS packet, the flow
translation would first clear the L3/4 headers of the 'flow' to mark
those fields invalid. Later, when committing 'flow' changes to
datapath actions before output, the necessary datapath MPLS actions
are created and the corresponding changes updated to the 'base flow'.
This was done using the same flow_push_mpls() function that clears
the L2/3 headers, so also the 'base flow' L2/3 headers were cleared.
Then, when translation returns from a patch port or group bucket, the
original 'flow' is restored, now showing no sign of the MPLS labels.
Since the 'base flow' now has the MPLS labels, following translations
know to issue MPLS POP actions before any output actions. However, as
part of checking for changes to IP headers we test that the IP
protocol type was not changed. But now the 'base flow's 'nw_proto'
field is zero and an assert fail crashes OVS.
This is solved by not clearing the L3/4 fields of the 'base
flow'. This allows the processing after the patch port to continue
with L3/4 fields as if no MPLS was done, after first issuing the
necessary MPLS POP actions.
2. IP header updates were done before the MPLS POP actions were
issued. This caused incorrect packet output after, e.g., group action
or patch port. For example, with actions:
group 1234: all bucket=push_mpls,output:LOCAL
ip actions=group:1234,dec_ttl,output:LOCAL,output:LOCAL
the dec_ttl would only be executed before the last output to LOCAL,
since at the time of committing IP changes after the group action the
packet was still an MPLS packet.
This is solved by checking the dl_type of both 'flow' and 'base flow'
and issuing MPLS actions if they can transform the packet from an MPLS
packet to a non-MPLS packet. For an IP packet the change in ttl can
then be correctly committed before the last two output actions.
Two test cases are added to prevent future regressions.
Reported-by: Thomas Morin <thomas.morin@orange.com>
Suggested-by: Takashi YAMAMOTO <yamamoto@ovn.org>
Fixes: 8bfd0fdac ("Enhance userspace support for MPLS, for up to 3 labels.")
Fixes: 1b035ef20 ("mpls: Allow l3 and l4 actions to prior to a push_mpls action")
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: YAMAMOTO Takashi <yamamoto@ovn.org>
2016-12-01 14:05:24 -08:00
|
|
|
|
if (!mpls_done) {
|
|
|
|
|
commit_mpls_action(flow, base, odp_actions);
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
|
|
|
|
|
commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:28:05 -07:00
|
|
|
|
|
2015-10-20 22:03:02 -07:00
|
|
|
|
return slow1 ? slow1 : slow2;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|