diff options
author | William Tu <u9012063@gmail.com> | 2016-06-24 07:42:30 -0700 |
---|---|---|
committer | Pravin B Shelar <pshelar@ovn.org> | 2016-06-24 09:17:00 -0700 |
commit | aaca4fe0ce9e90a41b3f4db84be7d05823c733e4 (patch) | |
tree | 501ddeb047ddb614067f89787d71bb01427bacc1 /lib | |
parent | 4c7804f14b9b5bbcd37cf9170781e2f820992532 (diff) | |
download | openvswitch-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.c | 2 | ||||
-rw-r--r-- | lib/dp-packet.h | 65 | ||||
-rw-r--r-- | lib/dpif-netdev.c | 33 | ||||
-rw-r--r-- | lib/dpif.c | 25 | ||||
-rw-r--r-- | lib/dpif.h | 1 | ||||
-rw-r--r-- | lib/netdev-bsd.c | 3 | ||||
-rw-r--r-- | lib/netdev-dpdk.c | 15 | ||||
-rw-r--r-- | lib/netdev-dummy.c | 2 | ||||
-rw-r--r-- | lib/netdev-linux.c | 3 | ||||
-rw-r--r-- | lib/netdev.c | 5 | ||||
-rw-r--r-- | lib/odp-execute.c | 12 | ||||
-rw-r--r-- | lib/odp-util.c | 23 | ||||
-rw-r--r-- | lib/ofp-actions.c | 106 |
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: |