summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorWilliam Tu <u9012063@gmail.com>2016-06-24 07:42:30 -0700
committerPravin B Shelar <pshelar@ovn.org>2016-06-24 09:17:00 -0700
commitaaca4fe0ce9e90a41b3f4db84be7d05823c733e4 (patch)
tree501ddeb047ddb614067f89787d71bb01427bacc1 /lib
parent4c7804f14b9b5bbcd37cf9170781e2f820992532 (diff)
downloadopenvswitch-aaca4fe0ce9e90a41b3f4db84be7d05823c733e4.tar.gz
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>
Diffstat (limited to 'lib')
-rw-r--r--lib/dp-packet.c2
-rw-r--r--lib/dp-packet.h65
-rw-r--r--lib/dpif-netdev.c33
-rw-r--r--lib/dpif.c25
-rw-r--r--lib/dpif.h1
-rw-r--r--lib/netdev-bsd.c3
-rw-r--r--lib/netdev-dpdk.c15
-rw-r--r--lib/netdev-dummy.c2
-rw-r--r--lib/netdev-linux.c3
-rw-r--r--lib/netdev.c5
-rw-r--r--lib/odp-execute.c12
-rw-r--r--lib/odp-util.c23
-rw-r--r--lib/ofp-actions.c106
13 files changed, 291 insertions, 4 deletions
diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 0c85d508a..8e7defc83 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -30,6 +30,7 @@ dp_packet_init__(struct dp_packet *b, size_t allocated, enum dp_packet_source so
dp_packet_reset_offsets(b);
pkt_metadata_init(&b->md, 0);
dp_packet_rss_invalidate(b);
+ dp_packet_reset_cutlen(b);
}
static void
@@ -168,6 +169,7 @@ dp_packet_clone_with_headroom(const struct dp_packet *buffer, size_t headroom)
new_buffer->l3_ofs = buffer->l3_ofs;
new_buffer->l4_ofs = buffer->l4_ofs;
new_buffer->md = buffer->md;
+ new_buffer->cutlen = buffer->cutlen;
#ifdef DPDK_NETDEV
new_buffer->mbuf.ol_flags = buffer->mbuf.ol_flags;
#else
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 118c84d3a..7c1e6373c 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -60,6 +60,7 @@ struct dp_packet {
* or UINT16_MAX. */
uint16_t l4_ofs; /* Transport-level header offset,
or UINT16_MAX. */
+ uint32_t cutlen; /* length in bytes to cut from the end. */
union {
struct pkt_metadata md;
uint64_t data[DP_PACKET_CONTEXT_SIZE / 8];
@@ -494,6 +495,34 @@ dp_packet_set_allocated(struct dp_packet *b, uint16_t s)
}
#endif
+static inline void
+dp_packet_reset_cutlen(struct dp_packet *b)
+{
+ b->cutlen = 0;
+}
+
+static inline uint32_t
+dp_packet_set_cutlen(struct dp_packet *b, uint32_t max_len)
+{
+ if (max_len < ETH_HEADER_LEN) {
+ max_len = ETH_HEADER_LEN;
+ }
+
+ if (max_len >= dp_packet_size(b)) {
+ b->cutlen = 0;
+ } else {
+ b->cutlen = dp_packet_size(b) - max_len;
+ }
+ return b->cutlen;
+}
+
+static inline uint32_t
+dp_packet_get_cutlen(struct dp_packet *b)
+{
+ /* Always in valid range if user uses dp_packet_set_cutlen. */
+ return b->cutlen;
+}
+
static inline void *
dp_packet_data(const struct dp_packet *b)
{
@@ -567,12 +596,14 @@ enum { NETDEV_MAX_BURST = 32 }; /* Maximum number packets in a batch. */
struct dp_packet_batch {
int count;
+ bool trunc; /* true if the batch needs truncate. */
struct dp_packet *packets[NETDEV_MAX_BURST];
};
static inline void dp_packet_batch_init(struct dp_packet_batch *b)
{
b->count = 0;
+ b->trunc = false;
}
static inline void
@@ -585,12 +616,14 @@ dp_packet_batch_clone(struct dp_packet_batch *dst,
dst->packets[i] = dp_packet_clone(src->packets[i]);
}
dst->count = src->count;
+ dst->trunc = src->trunc;
}
static inline void
packet_batch_init_packet(struct dp_packet_batch *b, struct dp_packet *p)
{
b->count = 1;
+ b->trunc = false;
b->packets[0] = p;
}
@@ -606,6 +639,38 @@ dp_packet_delete_batch(struct dp_packet_batch *batch, bool may_steal)
}
}
+static inline void
+dp_packet_batch_apply_cutlen(struct dp_packet_batch *pktb)
+{
+ int i;
+
+ if (!pktb->trunc)
+ return;
+
+ for (i = 0; i < pktb->count; i++) {
+ uint32_t cutlen = dp_packet_get_cutlen(pktb->packets[i]);
+
+ dp_packet_set_size(pktb->packets[i],
+ dp_packet_size(pktb->packets[i]) - cutlen);
+ dp_packet_reset_cutlen(pktb->packets[i]);
+ }
+ pktb->trunc = false;
+}
+
+static inline void
+dp_packet_batch_reset_cutlen(struct dp_packet_batch *pktb)
+{
+ int i;
+
+ if (!pktb->trunc)
+ return;
+
+ pktb->trunc = false;
+ for (i = 0; i < pktb->count; i++) {
+ dp_packet_reset_cutlen(pktb->packets[i]);
+ }
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 8d39d9ecc..70f320da0 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -4057,13 +4057,17 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_TUNNEL_PUSH:
if (*depth < MAX_RECIRC_DEPTH) {
struct dp_packet_batch tnl_pkt;
+ struct dp_packet_batch *orig_packets_ = packets_;
int err;
if (!may_steal) {
dp_packet_batch_clone(&tnl_pkt, packets_);
packets_ = &tnl_pkt;
+ dp_packet_batch_reset_cutlen(orig_packets_);
}
+ dp_packet_batch_apply_cutlen(packets_);
+
err = push_tnl_action(pmd, a, packets_);
if (!err) {
(*depth)++;
@@ -4076,6 +4080,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_TUNNEL_POP:
if (*depth < MAX_RECIRC_DEPTH) {
+ struct dp_packet_batch *orig_packets_ = packets_;
odp_port_t portno = u32_to_odp(nl_attr_get_u32(a));
p = pmd_tx_port_cache_lookup(pmd, portno);
@@ -4084,10 +4089,13 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
int i;
if (!may_steal) {
- dp_packet_batch_clone(&tnl_pkt, packets_);
- packets_ = &tnl_pkt;
+ dp_packet_batch_clone(&tnl_pkt, packets_);
+ packets_ = &tnl_pkt;
+ dp_packet_batch_reset_cutlen(orig_packets_);
}
+ dp_packet_batch_apply_cutlen(packets_);
+
netdev_pop_header(p->netdev, packets_);
if (!packets_->count) {
return;
@@ -4107,22 +4115,42 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_USERSPACE:
if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) {
+ struct dp_packet_batch *orig_packets_ = packets_;
struct dp_packet **packets = packets_->packets;
const struct nlattr *userdata;
+ struct dp_packet_batch usr_pkt;
struct ofpbuf actions;
struct flow flow;
ovs_u128 ufid;
+ bool clone = false;
int i;
userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
ofpbuf_init(&actions, 0);
+ if (packets_->trunc) {
+ if (!may_steal) {
+ dp_packet_batch_clone(&usr_pkt, packets_);
+ packets_ = &usr_pkt;
+ packets = packets_->packets;
+ clone = true;
+ dp_packet_batch_reset_cutlen(orig_packets_);
+ }
+
+ dp_packet_batch_apply_cutlen(packets_);
+ }
+
for (i = 0; i < packets_->count; i++) {
flow_extract(packets[i], &flow);
dpif_flow_hash(dp->dpif, &flow, sizeof flow, &ufid);
dp_execute_userspace_action(pmd, packets[i], may_steal, &flow,
&ufid, &actions, userdata);
}
+
+ if (clone) {
+ dp_packet_delete_batch(packets_, true);
+ }
+
ofpbuf_uninit(&actions);
fat_rwlock_unlock(&dp->upcall_rwlock);
@@ -4170,6 +4198,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_SAMPLE:
case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_TRUNC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}
diff --git a/lib/dpif.c b/lib/dpif.c
index c4f24c780..5f1be41c6 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1092,6 +1092,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
struct dpif_execute_helper_aux *aux = aux_;
int type = nl_attr_type(action);
struct dp_packet *packet = packets_->packets[0];
+ struct dp_packet *trunc_packet = NULL, *orig_packet;
ovs_assert(packets_->count == 1);
@@ -1106,7 +1107,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
struct ofpbuf execute_actions;
uint64_t stub[256 / 8];
struct pkt_metadata *md = &packet->md;
- bool dst_set;
+ bool dst_set, clone = false;
+ uint32_t cutlen = dp_packet_get_cutlen(packet);
dst_set = flow_tnl_dst_is_set(&md->tunnel);
if (dst_set) {
@@ -1124,6 +1126,22 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
execute.actions_len = NLA_ALIGN(action->nla_len);
}
+ orig_packet = packet;
+
+ if (cutlen > 0 && (type == OVS_ACTION_ATTR_OUTPUT ||
+ type == OVS_ACTION_ATTR_TUNNEL_PUSH ||
+ type == OVS_ACTION_ATTR_TUNNEL_POP ||
+ type == OVS_ACTION_ATTR_USERSPACE)) {
+ if (!may_steal) {
+ trunc_packet = dp_packet_clone(packet);
+ packet = trunc_packet;
+ clone = true;
+ }
+
+ dp_packet_set_size(packet, dp_packet_size(packet) - cutlen);
+ dp_packet_reset_cutlen(orig_packet);
+ }
+
execute.packet = packet;
execute.flow = aux->flow;
execute.needs_help = false;
@@ -1135,6 +1153,10 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
if (dst_set) {
ofpbuf_uninit(&execute_actions);
}
+
+ if (clone) {
+ dp_packet_delete(trunc_packet);
+ }
break;
}
@@ -1146,6 +1168,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_SET:
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_SAMPLE:
+ case OVS_ACTION_ATTR_TRUNC:
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
diff --git a/lib/dpif.h b/lib/dpif.h
index 678830135..981868cd1 100644
--- a/lib/dpif.h
+++ b/lib/dpif.h
@@ -784,6 +784,7 @@ struct dpif_upcall {
size_t key_len; /* Length of 'key' in bytes. */
ovs_u128 ufid; /* Unique flow identifier for 'key'. */
struct nlattr *mru; /* Maximum receive unit. */
+ struct nlattr *cutlen; /* Number of bytes shrink from the end. */
/* DPIF_UC_ACTION only. */
struct nlattr *userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index 43fa9821b..2e92d9768 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -699,6 +699,9 @@ netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
const void *data = dp_packet_data(pkts[i]);
size_t size = dp_packet_size(pkts[i]);
+ /* Truncate the packet if it is configured. */
+ size -= dp_packet_get_cutlen(pkts[i]);
+
while (!error) {
ssize_t retval;
if (dev->tap_fd >= 0) {
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index fc0c8d351..ed14a21e1 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -1503,6 +1503,10 @@ dpdk_do_tx_copy(struct netdev *netdev, int qid, struct dp_packet **pkts,
break;
}
+ /* Cut the size so only the truncated size is copied. */
+ size -= dp_packet_get_cutlen(pkts[i]);
+ dp_packet_reset_cutlen(pkts[i]);
+
/* We have to do a copy for now */
memcpy(rte_pktmbuf_mtod(mbufs[newcnt], void *), dp_packet_data(pkts[i]), size);
@@ -1550,6 +1554,14 @@ netdev_dpdk_vhost_send(struct netdev *netdev, int qid, struct dp_packet **pkts,
}
}
} else {
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ int cutlen = dp_packet_get_cutlen(pkts[i]);
+
+ dp_packet_set_size(pkts[i], dp_packet_size(pkts[i]) - cutlen);
+ dp_packet_reset_cutlen(pkts[i]);
+ }
__netdev_dpdk_vhost_send(netdev, qid, pkts, cnt, may_steal);
}
return 0;
@@ -1586,6 +1598,9 @@ netdev_dpdk_send__(struct netdev_dpdk *dev, int qid,
for (i = 0; i < cnt; i++) {
int size = dp_packet_size(pkts[i]);
+ size -= dp_packet_get_cutlen(pkts[i]);
+ dp_packet_set_size(pkts[i], size);
+
if (OVS_UNLIKELY(size > dev->max_packet_len)) {
if (next_tx_idx != i) {
temp_cnt = i - next_tx_idx;
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index aa244b60d..127b6ae01 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -1044,6 +1044,8 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
const void *buffer = dp_packet_data(pkts[i]);
size_t size = dp_packet_size(pkts[i]);
+ size -= dp_packet_get_cutlen(pkts[i]);
+
if (size < ETH_HEADER_LEN) {
error = EMSGSIZE;
break;
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 8a5661d12..ef11d12ba 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1172,6 +1172,9 @@ netdev_linux_send(struct netdev *netdev_, int qid OVS_UNUSED,
size_t size = dp_packet_size(pkts[i]);
ssize_t retval;
+ /* Truncate the packet if it is configured. */
+ size -= dp_packet_get_cutlen(pkts[i]);
+
if (!is_tap_netdev(netdev_)) {
/* Use our AF_PACKET socket to send to this device. */
struct sockaddr_ll sll;
diff --git a/lib/netdev.c b/lib/netdev.c
index 4be806d18..66511731d 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -681,7 +681,7 @@ netdev_set_tx_multiq(struct netdev *netdev, unsigned int n_txq)
return error;
}
-/* Sends 'buffers' on 'netdev'. Returns 0 if successful (for every packet),
+/* Sends 'batch' on 'netdev'. Returns 0 if successful (for every packet),
* otherwise a positive errno value. Returns EAGAIN without blocking if
* at least one the packets cannot be queued immediately. Returns EMSGSIZE
* if a partial packet was transmitted or if a packet is too big or too small
@@ -716,6 +716,9 @@ netdev_send(struct netdev *netdev, int qid, struct dp_packet_batch *batch,
may_steal);
if (!error) {
COVERAGE_INC(netdev_sent);
+ if (!may_steal) {
+ dp_packet_batch_reset_cutlen(batch);
+ }
}
return error;
}
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 423962406..5a4390405 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -503,6 +503,7 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_PUSH_MPLS:
case OVS_ACTION_ATTR_POP_MPLS:
+ case OVS_ACTION_ATTR_TRUNC:
return false;
case OVS_ACTION_ATTR_UNSPEC:
@@ -625,6 +626,17 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
}
break;
+ case OVS_ACTION_ATTR_TRUNC: {
+ const struct ovs_action_trunc *trunc =
+ nl_attr_get_unspec(a, sizeof *trunc);
+
+ batch->trunc = true;
+ for (i = 0; i < cnt; i++) {
+ dp_packet_set_cutlen(packets[i], trunc->max_len);
+ }
+ break;
+ }
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 48c05f509..d7b6a2d27 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -107,6 +107,7 @@ odp_action_len(uint16_t type)
switch ((enum ovs_action_attr) type) {
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
+ case OVS_ACTION_ATTR_TRUNC: return sizeof(struct ovs_action_trunc);
case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE;
@@ -777,6 +778,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
case OVS_ACTION_ATTR_OUTPUT:
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
break;
+ 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;
case OVS_ACTION_ATTR_TUNNEL_POP:
ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
break;
@@ -1528,6 +1537,20 @@ parse_odp_action(const char *s, const struct simap *port_names,
}
}
+ {
+ 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;
+ }
+ }
+
if (port_names) {
int len = strcspn(s, delimiters);
struct simap_node *node;
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index ea55896f1..997cc15b4 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -301,6 +301,9 @@ enum ofp_raw_action_type {
/* NX1.0+(36): struct nx_action_nat, ... */
NXAST_RAW_NAT,
+ /* NX1.0+(39): struct nx_action_output_trunc. */
+ NXAST_RAW_OUTPUT_TRUNC,
+
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -381,6 +384,7 @@ ofpact_next_flattened(const struct ofpact *ofpact)
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_OUTPUT_REG:
+ case OFPACT_OUTPUT_TRUNC:
case OFPACT_BUNDLE:
case OFPACT_SET_FIELD:
case OFPACT_SET_VLAN_VID:
@@ -538,6 +542,40 @@ encode_OUTPUT(const struct ofpact_output *output,
}
static char * OVS_WARN_UNUSED_RESULT
+parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
+ const char *arg_)
+{
+ char *key, *value;
+ char *arg = CONST_CAST(char *, arg_);
+
+ while (ofputil_parse_key_value(&arg, &key, &value)) {
+ if (!strcmp(key, "port")) {
+ if (!ofputil_port_from_string(value, &output_trunc->port)) {
+ return xasprintf("output to unknown truncate port: %s",
+ value);
+ }
+ if (ofp_to_u16(output_trunc->port) > ofp_to_u16(OFPP_MAX)) {
+ if (output_trunc->port != OFPP_LOCAL &&
+ output_trunc->port != OFPP_IN_PORT)
+ return xasprintf("output to unsupported truncate port: %s",
+ value);
+ }
+ } else if (!strcmp(key, "max_len")) {
+ char *err;
+
+ err = str_to_u32(value, &output_trunc->max_len);
+ if (err) {
+ return err;
+ }
+ } else {
+ return xasprintf("invalid key '%s' in output_trunc argument",
+ key);
+ }
+ }
+ return NULL;
+}
+
+static char * OVS_WARN_UNUSED_RESULT
parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
@@ -547,6 +585,11 @@ parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
output_reg = ofpact_put_OUTPUT_REG(ofpacts);
output_reg->max_len = UINT16_MAX;
return mf_parse_subfield(&output_reg->src, arg);
+ } else if (strstr(arg, "port") && strstr(arg, "max_len")) {
+ struct ofpact_output_trunc *output_trunc;
+
+ output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
+ return parse_truncate_subfield(output_trunc, arg);
} else {
struct ofpact_output *output;
@@ -5603,6 +5646,61 @@ parse_NAT(char *arg, struct ofpbuf *ofpacts,
return NULL;
}
+/* Truncate output action. */
+struct nx_action_output_trunc {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* At least 16. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_OUTPUT_TRUNC. */
+ ovs_be16 port; /* Output port */
+ ovs_be32 max_len; /* Truncate packet to size bytes */
+};
+OFP_ASSERT(sizeof(struct nx_action_output_trunc) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_OUTPUT_TRUNC(const struct nx_action_output_trunc *natrc,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ struct ofpact_output_trunc *output_trunc;
+
+ output_trunc = ofpact_put_OUTPUT_TRUNC(out);
+ output_trunc->max_len = ntohl(natrc->max_len);
+ output_trunc->port = u16_to_ofp(ntohs(natrc->port));
+
+ if (output_trunc->max_len < ETH_HEADER_LEN) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ return 0;
+}
+
+static void
+encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc,
+ enum ofp_version ofp_version OVS_UNUSED,
+ struct ofpbuf *out)
+{
+ struct nx_action_output_trunc *natrc = put_NXAST_OUTPUT_TRUNC(out);
+
+ natrc->max_len = htonl(output_trunc->max_len);
+ natrc->port = htons(ofp_to_u16(output_trunc->port));
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_OUTPUT_TRUNC(const char *arg, struct ofpbuf *ofpacts OVS_UNUSED,
+ enum ofputil_protocol *usable_protocols OVS_UNUSED)
+{
+ /* Disable output_trunc parsing. Expose as output(port=N,max_len=M) and
+ * reuse parse_OUTPUT to parse output_trunc action. */
+ return xasprintf("unknown action %s", arg);
+}
+
+static void
+format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a, struct ds *s)
+{
+ ds_put_format(s, "%soutput%s(port=%"PRIu16",max_len=%"PRIu32")",
+ colors.special, colors.end, a->port, a->max_len);
+}
+
/* Meter instruction. */
@@ -5997,6 +6095,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
case OFPACT_NOTE:
case OFPACT_OUTPUT:
case OFPACT_OUTPUT_REG:
+ case OFPACT_OUTPUT_TRUNC:
case OFPACT_POP_MPLS:
case OFPACT_POP_QUEUE:
case OFPACT_PUSH_MPLS:
@@ -6025,6 +6124,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
case OFPACT_DEC_TTL:
case OFPACT_GROUP:
case OFPACT_OUTPUT:
+ case OFPACT_OUTPUT_TRUNC:
case OFPACT_POP_MPLS:
case OFPACT_PUSH_MPLS:
case OFPACT_PUSH_VLAN:
@@ -6249,6 +6349,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
case OFPACT_CONTROLLER:
case OFPACT_ENQUEUE:
case OFPACT_OUTPUT_REG:
+ case OFPACT_OUTPUT_TRUNC:
case OFPACT_BUNDLE:
case OFPACT_SET_VLAN_VID:
case OFPACT_SET_VLAN_PCP:
@@ -6677,6 +6778,10 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
case OFPACT_OUTPUT_REG:
return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);
+ case OFPACT_OUTPUT_TRUNC:
+ return ofpact_check_output_port(ofpact_get_OUTPUT_TRUNC(a)->port,
+ max_ports);
+
case OFPACT_BUNDLE:
return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
@@ -7354,6 +7459,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
return port == OFPP_CONTROLLER;
case OFPACT_OUTPUT_REG:
+ case OFPACT_OUTPUT_TRUNC:
case OFPACT_BUNDLE:
case OFPACT_SET_VLAN_VID:
case OFPACT_SET_VLAN_PCP: