@@ -738,10 +738,15 @@ err:
}
static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
- struct sw_flow_key *key)
+ uint16_t max_len, struct sw_flow_key *key)
{
struct vport *vport = ovs_vport_rcu(dp, out_port);
+ /* This is after skb_clone called from do_execute_actions,
+ so max_len only applies to the current skb. */
+ if (unlikely(max_len != 0))
+ OVS_CB(skb)->max_len = max_len;
+
if (likely(vport)) {
u16 mru = OVS_CB(skb)->mru;
@@ -1034,6 +1039,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
* is slightly obscure just to avoid that.
*/
int prev_port = -1;
+ uint16_t max_len = 0;
const struct nlattr *a;
int rem;
@@ -1045,15 +1051,18 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
struct sk_buff *out_skb = skb_clone(skb, GFP_ATOMIC);
if (out_skb)
- do_output(dp, out_skb, prev_port, key);
+ do_output(dp, out_skb, prev_port, max_len, key);
prev_port = -1;
}
switch (nla_type(a)) {
- case OVS_ACTION_ATTR_OUTPUT:
- prev_port = nla_get_u32(a);
+ case OVS_ACTION_ATTR_OUTPUT: {
+ struct ovs_action_output *output = nla_data(a);
+ prev_port = output->port;
+ max_len = output->max_len;
break;
+ }
case OVS_ACTION_ATTR_USERSPACE:
output_userspace(dp, skb, key, a, attr, len);
@@ -1126,7 +1135,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
}
if (prev_port != -1)
- do_output(dp, skb, prev_port, key);
+ do_output(dp, skb, prev_port, max_len, key);
else
consume_skb(skb);
@@ -102,6 +102,7 @@ struct datapath {
struct ovs_skb_cb {
struct vport *input_vport;
u16 mru;
+ u16 max_len;
};
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
@@ -2169,7 +2169,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
nla_for_each_nested(a, attr, rem) {
/* Expected argument lengths, (u32)-1 for variable length. */
static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
- [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
+ [OVS_ACTION_ATTR_OUTPUT] = sizeof(struct ovs_action_output),
[OVS_ACTION_ATTR_RECIRC] = sizeof(u32),
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
[OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls),
@@ -2202,10 +2202,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
return err;
break;
- case OVS_ACTION_ATTR_OUTPUT:
- if (nla_get_u32(a) >= DP_MAX_PORTS)
+ case OVS_ACTION_ATTR_OUTPUT: {
+ const struct ovs_action_output *output = nla_data(a);
+ if (output->port >= DP_MAX_PORTS ||
+ (output->max_len != 0 &&
+ output->max_len < OVS_ACTION_OUTPUT_MIN))
return -EINVAL;
break;
+ }
case OVS_ACTION_ATTR_HASH: {
const struct ovs_action_hash *act_hash = nla_data(a);
@@ -600,6 +600,13 @@ enum ovs_userspace_attr {
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
+struct ovs_action_output {
+ uint32_t port;
+ uint16_t max_len;
+};
+/* Minimum packet size max_len can have, 60 = ETH_MIN_FRAME_LEN. */
+#define OVS_ACTION_OUTPUT_MIN 60
+
/**
* struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
* @mpls_lse: MPLS label stack entry to push.
@@ -487,6 +487,8 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
OVS_CB(skb)->input_vport = vport;
OVS_CB(skb)->mru = 0;
+ OVS_CB(skb)->max_len = 0;
+
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
u32 mark;
@@ -615,6 +617,7 @@ static unsigned int packet_length(const struct sk_buff *skb)
void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
{
int mtu = vport->dev->mtu;
+ u16 max_len = OVS_CB(skb)->max_len;
if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
@@ -624,6 +627,9 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
goto drop;
}
+ if (unlikely(max_len != 0))
+ skb_trim(skb, max_len);
+
skb->dev = vport->dev;
vport->ops->send(skb);
return;
@@ -29,6 +29,7 @@ dp_packet_init__(struct dp_packet *b, size_t allocated, enum dp_packet_source so
b->source = source;
dp_packet_reset_offsets(b);
pkt_metadata_init(&b->md, 0);
+ b->max_len = 0;
}
static void
@@ -58,6 +58,7 @@ struct dp_packet {
* or UINT16_MAX. */
uint16_t l4_ofs; /* Transport-level header offset,
or UINT16_MAX. */
+ uint16_t max_len; /* packet's max_len, 0 means remain origingal size */
struct pkt_metadata md;
};
@@ -860,8 +860,10 @@ get_in_port_netdev_from_key(struct dpif *dpif, const struct ofpbuf *key)
struct dpif_port dpif_port;
odp_port_t port_no;
int error;
+ const struct ovs_action_output *output =
+ nl_attr_get_unspec(in_port_nla, sizeof *output);
- port_no = ODP_PORT_C(nl_attr_get_u32(in_port_nla));
+ port_no = ODP_PORT_C(output->port);
error = dpif_port_query_by_number(dpif, port_no, &dpif_port);
if (error) {
goto out;
@@ -1391,9 +1393,13 @@ compare_output_actions(const void *a_, const void *b_)
{
const struct nlattr *a = a_;
const struct nlattr *b = b_;
- uint32_t a_port = nl_attr_get_u32(a);
- uint32_t b_port = nl_attr_get_u32(b);
+ const struct ovs_action_output *a_output =
+ nl_attr_get_unspec(a, sizeof *a_output);
+ const struct ovs_action_output *b_output =
+ nl_attr_get_unspec(b, sizeof *b_output);
+ uint32_t a_port = a_output->port;
+ uint32_t b_port = b_output->port;
return a_port < b_port ? -1 : a_port > b_port;
}
@@ -1401,10 +1407,9 @@ static void
sort_output_actions__(struct nlattr *first, struct nlattr *end)
{
size_t bytes = (uint8_t *) end - (uint8_t *) first;
- size_t n = bytes / NL_A_U32_SIZE;
-
- ovs_assert(bytes % NL_A_U32_SIZE == 0);
- qsort(first, n, NL_A_U32_SIZE, compare_output_actions);
+ size_t n = bytes / NL_A_U48_SIZE;
+ ovs_assert(bytes % NL_A_U48_SIZE == 0);
+ qsort(first, n, NL_A_U48_SIZE, compare_output_actions);
}
static void
@@ -3744,8 +3744,23 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
int i;
switch ((enum ovs_action_attr)type) {
- case OVS_ACTION_ATTR_OUTPUT:
- p = dp_netdev_lookup_port(dp, u32_to_odp(nl_attr_get_u32(a)));
+ case OVS_ACTION_ATTR_OUTPUT: {
+ struct dp_packet *trunc_pkts[cnt];
+ const struct ovs_action_output *output =
+ nl_attr_get_unspec(a, sizeof *output);
+
+ p = dp_netdev_lookup_port(dp, output->port);
+
+ if (output->max_len >= OVS_ACTION_OUTPUT_MIN) {
+ if (!may_steal) {
+ dp_netdev_clone_pkt_batch(trunc_pkts, packets, cnt);
+ packets = trunc_pkts;
+ }
+ for (i = 0; i < cnt; i++) {
+ packets[i]->max_len = output->max_len;
+ }
+ }
+
if (OVS_LIKELY(p)) {
int tx_qid;
@@ -3755,6 +3770,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
return;
}
break;
+ }
case OVS_ACTION_ATTR_TUNNEL_PUSH:
if (*depth < MAX_RECIRC_DEPTH) {
@@ -758,6 +758,14 @@ netdev_send(struct netdev *netdev, int qid, struct dp_packet **buffers,
return EOPNOTSUPP;
}
+ for (int i = 0; i < cnt; i++) {
+ struct dp_packet *packet = buffers[i];
+ if (packet->max_len != 0 &&
+ packet->max_len < dp_packet_size(packet)) {
+ dp_packet_set_size(packet, packet->max_len);
+ }
+ }
+
int error = netdev->netdev_class->send(netdev, qid, buffers, cnt,
may_steal);
if (!error) {
@@ -111,6 +111,7 @@ struct nlmsghdr *nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg);
#define NL_A_U8_SIZE NL_ATTR_SIZE(sizeof(uint8_t))
#define NL_A_U16_SIZE NL_ATTR_SIZE(sizeof(uint16_t))
#define NL_A_U32_SIZE NL_ATTR_SIZE(sizeof(uint32_t))
+#define NL_A_U48_SIZE NL_ATTR_SIZE(sizeof(uint32_t) + sizeof(uint16_t))
#define NL_A_U64_SIZE NL_ATTR_SIZE(sizeof(uint64_t))
#define NL_A_BE16_SIZE NL_ATTR_SIZE(sizeof(ovs_be16))
#define NL_A_BE32_SIZE NL_ATTR_SIZE(sizeof(ovs_be32))
@@ -106,7 +106,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_OUTPUT: return sizeof(struct ovs_action_output);
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;
@@ -772,9 +772,16 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
}
switch (type) {
- case OVS_ACTION_ATTR_OUTPUT:
- ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
+ case OVS_ACTION_ATTR_OUTPUT: {
+ const struct ovs_action_output *output =
+ nl_attr_get_unspec(a, sizeof *output);
+ ds_put_format(ds, "%"PRIu32, output->port);
+ if (output->max_len != 0 &&
+ output->max_len != UINT16_MAX) {
+ ds_put_format(ds, "(max_len=%"PRIu32")", output->max_len);
+ }
break;
+ }
case OVS_ACTION_ATTR_TUNNEL_POP:
ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
break;
@@ -1516,9 +1523,14 @@ parse_odp_action(const char *s, const struct simap *port_names,
{
uint32_t port;
int n;
+ struct ovs_action_output *output;
if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) {
- nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
+ output = nl_msg_put_unspec_uninit(actions,
+ OVS_ACTION_ATTR_OUTPUT,
+ sizeof *output);
+ output->port = port;
+ output->max_len = 0;
return n;
}
}
@@ -1526,10 +1538,15 @@ parse_odp_action(const char *s, const struct simap *port_names,
if (port_names) {
int len = strcspn(s, delimiters);
struct simap_node *node;
+ struct ovs_action_output *output;
node = simap_find_len(port_names, s, len);
if (node) {
- nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
+ output = nl_msg_put_unspec_uninit(actions,
+ OVS_ACTION_ATTR_OUTPUT,
+ sizeof *output);
+ output->port = node->data;
+ output->max_len = 0;
return len;
}
}
@@ -545,6 +545,44 @@ 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, "max_len")) {
+ char *name, *value;
+ char *arg_, *arg_option, *arg_port;
+ uint16_t max_len, port_len, value_len;
+ struct ofpact_output *output;
+
+ arg_ = xstrdup(arg);
+ value_len = strspn(arg_, "0123456789 (\t");
+ arg_option = arg_ + value_len;
+ value_len = strcspn(arg_option, " )\t");
+ arg_option[value_len] = '\0';
+
+ while (ofputil_parse_key_value(&arg_option, &name, &value)) {
+ if (!strcmp(name, "max_len")) {
+ char *error = str_to_u16(value, "max_len", &max_len);
+ if (error) {
+ return error;
+ }
+ }
+ }
+ arg_port = arg_;
+ port_len = strspn(arg_port, "0123456789");
+ arg_port[port_len] = '\0';
+
+ output = ofpact_put_OUTPUT(ofpacts);
+ if (!ofputil_port_from_string(arg_port, &output->port)) {
+ return xasprintf("%s: output to unknown port", arg);
+ }
+
+ /* Re-use max_len as max send packet length. If max_len = 0,
+ * output to port with its original size. If max_len =
+ * (0, UINT16_MAX], output to port with MIN(original_size, max_len).
+ */
+ output->max_len = max_len;
+ free(arg_);
+ return NULL;
+
} else {
struct ofpact_output *output;
@@ -563,6 +601,9 @@ format_OUTPUT(const struct ofpact_output *a, struct ds *s)
if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) {
ds_put_format(s, "%soutput:%s%"PRIu16,
colors.special, colors.end, a->port);
+ if (ofp_to_u16(a->max_len) != 0) {
+ ds_put_format(s, "(max_len=%"PRIu16")", a->max_len);
+ }
} else {
ofputil_format_port(a->port, s);
if (a->port == OFPP_CONTROLLER) {
@@ -247,7 +247,9 @@ struct ofpact_null {
struct ofpact_output {
struct ofpact ofpact;
ofp_port_t port; /* Output port. */
- uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER. */
+ uint16_t max_len; /* Max send len, for port OFPP_CONTROLLER.
+ For non-controller port, send packet with
+ MIN(max_len, original_packet_size). */
};
/* OFPACT_CONTROLLER.
@@ -523,7 +523,7 @@ struct xlate_bond_recirc {
};
static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port,
- const struct xlate_bond_recirc *xr);
+ uint16_t max_len, const struct xlate_bond_recirc *xr);
static struct xbridge *xbridge_lookup(struct xlate_cfg *,
const struct ofproto_dpif *);
@@ -1910,7 +1910,7 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
}
*flow_tci = tci;
- compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL);
+ compose_output_action(ctx, xport->ofp_port, 0, use_recirc ? &xr : NULL);
*flow_tci = old_tci;
}
@@ -2932,6 +2932,7 @@ clear_conntrack(struct flow *flow)
static void
compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
+ uint16_t max_len,
const struct xlate_bond_recirc *xr, bool check_stp)
{
const struct xport *xport = get_ofp_port(ctx->xbridge, ofp_port);
@@ -3175,12 +3176,18 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
OVS_ACTION_ATTR_TUNNEL_POP,
odp_tnl_port);
} else {
+ struct ovs_action_output *output;
+
/* Tunnel push-pop action is not compatible with
* IPFIX action. */
compose_ipfix_action(ctx, out_port);
- nl_msg_put_odp_port(ctx->odp_actions,
- OVS_ACTION_ATTR_OUTPUT,
- out_port);
+
+ output = nl_msg_put_unspec_uninit(ctx->odp_actions,
+ OVS_ACTION_ATTR_OUTPUT,
+ sizeof *output);
+ output->port = out_port;
+ output->max_len = ctx->xbridge->support.output_max_len ?
+ max_len : 0;
}
}
}
@@ -3205,9 +3212,9 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
static void
compose_output_action(struct xlate_ctx *ctx, ofp_port_t ofp_port,
- const struct xlate_bond_recirc *xr)
+ uint16_t max_len, const struct xlate_bond_recirc *xr)
{
- compose_output_action__(ctx, ofp_port, xr, true);
+ compose_output_action__(ctx, ofp_port, max_len, xr, true);
}
static void
@@ -3567,9 +3574,9 @@ flood_packets(struct xlate_ctx *ctx, bool all)
}
if (all) {
- compose_output_action__(ctx, xport->ofp_port, NULL, false);
+ compose_output_action__(ctx, xport->ofp_port, 0, NULL, false);
} else if (!(xport->config & OFPUTIL_PC_NO_FLOOD)) {
- compose_output_action(ctx, xport->ofp_port, NULL);
+ compose_output_action(ctx, xport->ofp_port, 0, NULL);
}
}
@@ -3865,7 +3872,7 @@ xlate_output_action(struct xlate_ctx *ctx,
switch (port) {
case OFPP_IN_PORT:
- compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL);
+ compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, 0, NULL);
break;
case OFPP_TABLE:
xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
@@ -3892,7 +3899,7 @@ xlate_output_action(struct xlate_ctx *ctx,
case OFPP_LOCAL:
default:
if (port != ctx->xin->flow.in_port.ofp_port) {
- compose_output_action(ctx, port, NULL);
+ compose_output_action(ctx, port, max_len, NULL);
} else {
xlate_report(ctx, "skipping output to input port");
}
@@ -3951,7 +3958,7 @@ xlate_enqueue_action(struct xlate_ctx *ctx,
/* Add datapath actions. */
flow_priority = ctx->xin->flow.skb_priority;
ctx->xin->flow.skb_priority = priority;
- compose_output_action(ctx, ofp_port, NULL);
+ compose_output_action(ctx, ofp_port, 0, NULL);
ctx->xin->flow.skb_priority = flow_priority;
/* Update NetFlow output port. */
@@ -5341,7 +5348,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
&& xbridge->has_in_band
&& in_band_must_output_to_local_port(flow)
&& !actions_output_to_local_port(&ctx)) {
- compose_output_action(&ctx, OFPP_LOCAL, NULL);
+ compose_output_action(&ctx, OFPP_LOCAL, 0, NULL);
}
if (user_cookie_offset) {
@@ -1189,6 +1189,50 @@ check_max_mpls_depth(struct dpif_backer *backer)
return n;
}
+/* Tests whether 'backer''s datapath supports output to non-controller
+ * with max_len packet size in OVS_ACTION_ATTR_OUTPUT. */
+static bool
+check_output_max_len(struct dpif_backer *backer)
+{
+ struct eth_header *eth;
+ struct ofpbuf action;
+ struct dpif_execute execute;
+ struct dp_packet packet;
+ struct ovs_action_output *output;
+ int error;
+
+ /* Compose an action with output:1(max_len=OVS_ACTION_OUTPUT_MIN). */
+ ofpbuf_init(&action, 64);
+ output = nl_msg_put_unspec_uninit(&action,
+ OVS_ACTION_ATTR_OUTPUT, sizeof *output);
+ output->port = 1;
+ output->max_len = OVS_ACTION_OUTPUT_MIN;
+
+ /* Compose a dummy ethernet packet. */
+ dp_packet_init(&packet, ETH_HEADER_LEN);
+ eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN);
+ eth->eth_type = htons(0x1234);
+
+ execute.actions = action.data;
+ execute.actions_len = action.size;
+ execute.packet = &packet;
+ execute.needs_help = false;
+ execute.probe = true;
+ execute.mtu = 0;
+
+ error = dpif_execute(backer->dpif, &execute);
+
+ dp_packet_uninit(&packet);
+ ofpbuf_uninit(&action);
+
+ if (error) {
+ /* Output max_len is not supported. */
+ VLOG_INFO("%s: datapath does not support output max_len feature.",
+ dpif_name(backer->dpif));
+ }
+ return !error;
+}
+
/* Tests whether 'backer''s datapath supports masked data in
* OVS_ACTION_ATTR_SET actions. We need to disable some features on older
* datapaths that don't support this feature. */
@@ -1292,6 +1336,7 @@ check_support(struct dpif_backer *backer)
backer->support.masked_set_action = check_masked_set_action(backer);
backer->support.ufid = check_ufid(backer);
backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif);
+ backer->support.output_max_len = check_output_max_len(backer);
backer->support.odp.ct_state = check_ct_state(backer);
backer->support.odp.ct_zone = check_ct_zone(backer);
@@ -90,6 +90,10 @@ struct dpif_backer_support {
/* True if the datapath supports OVS_FLOW_ATTR_UFID. */
bool ufid;
+ /* True if the datapath supports OVS_ACTION_ATTR_OUTPUT with max_len
+ * for non-controller output. */
+ bool output_max_len;
+
/* Each member represents support for related OVS_KEY_ATTR_* fields. */
struct odp_support odp;
};
@@ -1332,9 +1332,9 @@ ca da ad d6 0d 37 80 00 0a 02 08 00 80 00 10 01 \
05 dc 00 00 00 00 00 00 \
"], [0], [dnl
OFPST_FLOW reply (OF1.2) (xid=0x2):
- cookie=0x0, duration=3.023s, table=0, n_packets=1, n_bytes=98, ip,metadata=0,in_port=2,dl_dst=ca:da:ad:d6:0d:37,nw_tos=0 actions=output:2
- cookie=0x0, duration=4.545s, table=0, n_packets=2, n_bytes=140, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2
- cookie=0x0, duration=4.548s, table=0, n_packets=1, n_bytes=42, ip,metadata=0,in_port=2,dl_dst=52:54:00:97:00:69,nw_tos=0 actions=output:2
+ cookie=0x0, duration=3.023s, table=0, n_packets=1, n_bytes=98, ip,metadata=0,in_port=2,dl_dst=ca:da:ad:d6:0d:37,nw_tos=0 actions=output:2(max_len=1500)
+ cookie=0x0, duration=4.545s, table=0, n_packets=2, n_bytes=140, ip,metadata=0,in_port=2,dl_dst=52:54:00:c3:00:89,nw_tos=0 actions=output:2(max_len=1500)
+ cookie=0x0, duration=4.548s, table=0, n_packets=1, n_bytes=42, ip,metadata=0,in_port=2,dl_dst=52:54:00:97:00:69,nw_tos=0 actions=output:2(max_len=1500)
])
AT_CLEANUP
@@ -5309,6 +5309,59 @@ PORTNAME
portName=p2
])])
+
+AT_SETUP([ofproto-dpif - OUTPUT action with max_len])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3 4 5
+
+AT_CHECK([ovs-vsctl -- set Interface p1 type=dummy options:pcap=p1.pcap])
+AT_CHECK([ovs-vsctl -- set Interface p2 type=dummy options:pstream=punix:p2.sock])
+AT_CHECK([ovs-vsctl -- set Interface p3 type=dummy options:stream=unix:p2.sock])
+AT_CHECK([ovs-vsctl -- set Interface p4 type=dummy options:pstream=punix:p4.sock])
+AT_CHECK([ovs-vsctl -- set Interface p5 type=dummy options:stream=unix:p4.sock])
+
+dnl output:2(max_len=32) shows here as truncated size
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=3,actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=5,actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=1,actions=output:2(max_len=64),output:4'])
+
+dnl An 170 byte packet
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f'])
+
+AT_CHECK([ovs-ofctl parse-pcap p1.pcap], [0], [dnl
+icmp,in_port=ANY,vlan_tci=0x0000,dl_src=00:50:56:c0:00:08,dl_dst=00:0c:29:c8:a0:a4,nw_src=192.168.218.1,nw_dst=192.168.218.100,nw_tos=0,nw_ecn=0,nw_ttl=64,icmp_type=8,icmp_code=0
+])
+dnl packet with truncated size
+AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=64\).*/\1/p'], [0], [dnl
+n_bytes=64
+])
+dnl dnl packet with original size
+AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=170\).*/\1/p'], [0], [dnl
+n_bytes=170
+])
+
+dnl More complicated case
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=3,actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=5,actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=1,actions=output:2(max_len=64),output:2(max_len=128),output:4(max_len=60),output:2,output:4'])
+
+dnl An 170 byte packet
+AT_CHECK([ovs-appctl netdev-dummy/receive p1 '000c29c8a0a4005056c0000808004500009cb4a6000040019003c0a8da01c0a8da640800cb5fa762000556f431ad0009388e08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f'])
+
+sleep 1
+dnl packet size: 64 + 128 + 170 = 362
+AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=362\).*/\1/p'], [0], [dnl
+n_bytes=362
+])
+dnl packet size: 60 + 170 = 230
+AT_CHECK([ovs-ofctl dump-flows br0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=230\).*/\1/p'], [0], [dnl
+n_bytes=230
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - sFlow packet sampling - IPv4 collector])
CHECK_SFLOW_SAMPLING_PACKET([127.0.0.1])
AT_CLEANUP
@@ -49,6 +49,69 @@ NS_CHECK_EXEC([at_ns0], [ping -s 3200 -q -c 3 -i 0.3 -w 2 10.2.2.2 | FORMAT_PING
OVS_TRAFFIC_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([datapath - output action with max_len])
+OVS_TRAFFIC_VSWITCHD_START()
+
+dnl Create p0 and ovs-p0(1)
+ADD_NAMESPACES(at_ns0)
+ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
+NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11])
+NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22])
+
+dnl Create p1(3) and ovs-p1(2), packets received from ovs-p1 will appear in p1
+AT_CHECK([ip link add p1 type veth peer name ovs-p1])
+AT_CHECK([ip link set dev ovs-p1 up])
+AT_CHECK([ip link set dev p1 up])
+AT_CHECK([ovs-vsctl add-port br0 ovs-p1 -- set interface ovs-p1 ofport_request=2])
+dnl Use p1 to check the truncated packet
+AT_CHECK([ovs-vsctl add-port br0 p1 -- set interface p1 ofport_request=3])
+
+dnl Create p2(5) and ovs-p2(4)
+AT_CHECK([ip link add p2 type veth peer name ovs-p2])
+AT_CHECK([ip link set dev ovs-p2 up])
+AT_CHECK([ip link set dev p2 up])
+AT_CHECK([ovs-vsctl add-port br0 ovs-p2 -- set interface ovs-p2 ofport_request=4])
+dnl Use p1 to check the truncated packet
+AT_CHECK([ovs-vsctl add-port br0 p2 -- set interface p2 ofport_request=5])
+
+dnl test1: basic
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output:2(max_len=100),output:4'])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -s 1024 -w 1 10.1.1.2 | FORMAT_PING], [0], [dnl
+1 packets transmitted, 0 received, 100% packet loss, time 0ms
+])
+AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=100\).*/\1/p'], [0], [dnl
+n_bytes=100
+])
+dnl packet with original size
+AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=1066\).*/\1/p'], [0], [dnl
+n_bytes=1066
+])
+
+dnl test2: more complicated output actions
+AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=3 dl_dst=e6:66:c1:22:22:22 actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=5 dl_dst=e6:66:c1:22:22:22 actions=drop'])
+AT_CHECK([ovs-ofctl add-flow br0 'in_port=1 dl_dst=e6:66:c1:22:22:22 actions=output:2(max_len=100),output:4,output:2(max_len=100),output:4(max_len=100),output:2'])
+
+NS_CHECK_EXEC([at_ns0], [ping -q -c 1 -s 1024 -w 1 10.1.1.2 | FORMAT_PING], [0], [dnl
+1 packets transmitted, 0 received, 100% packet loss, time 0ms
+])
+dnl 100 + 100 + 1066 = 1266
+AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=3" | sed -n 's/.*\(n\_bytes=1266\).*/\1/p'], [0], [dnl
+n_bytes=1266
+])
+dnl 1066 + 100 = 1166
+AT_CHECK([ovs-ofctl dump-flows br0 table=0 | grep "in_port=5" | sed -n 's/.*\(n\_bytes=1166\).*/\1/p'], [0], [dnl
+n_bytes=1166
+])
+
+OVS_TRAFFIC_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([datapath - ping6 between two ports])
OVS_TRAFFIC_VSWITCHD_START()
Before, OpenFlow specification defines 'max_len' in struct ofp_action_output as the max number of bytes to send when port is OFPP_CONTROLLER. A max_len of zero means no bytes of the packet should be sent, and max_len of OFPCML_NO_BUFFER means the complete packet is sent to the controller. It is left undefined of max_len, when output port is not OFPP_CONTROLLER. The patch extends the use of max_len when output is non-controller. 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. The patch proposes adding a '(max_len=<N>)' after the output action. An example use case is below as well as shown in the tests/: - 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:1(max_len=100)' - The scope of max_len is limited to output action itself. The following output:1 and output:2 will be the original packet size. # ovs-ofctl add-flow br0 'actions=output:1(max_len=100),output:1,output:2' Implementation/Limitaions: - Userspace and kernel datapath is supported, no Windows support. - The minimum value of max_len is 60 byte (minimum Ethernet frame size). This is defined in OVS_ACTION_OUTPUT_MIN. - Since 'max_len' is undefined in OpenFlow spec, the controller might accidentally place a garbage value in max_len and send to OVS. - For compatibility, if the kernel datapath is not supported, set max_len to zero. - OUTPUT_REG with max_len is not supported. - actions=1(max_len=100) is not supported, must specify as 'output:1'. - Only output:[0-9]*(max_len=<N>) is supported. Output to IN_PORT, TABLE, NORMAL, FLOOD, ALL, and LOCAL are not supported. Signed-off-by: William Tu <u9012063@gmail.com> --- datapath/actions.c | 19 +++++-- datapath/datapath.h | 1 + datapath/flow_netlink.c | 10 ++-- datapath/linux/compat/include/linux/openvswitch.h | 7 +++ datapath/vport.c | 6 +++ lib/dp-packet.c | 1 + lib/dp-packet.h | 1 + lib/dpctl.c | 19 ++++--- lib/dpif-netdev.c | 20 ++++++- lib/netdev.c | 8 +++ lib/netlink.h | 1 + lib/odp-util.c | 27 ++++++++-- lib/ofp-actions.c | 41 +++++++++++++++ lib/ofp-actions.h | 4 +- ofproto/ofproto-dpif-xlate.c | 33 +++++++----- ofproto/ofproto-dpif.c | 45 ++++++++++++++++ ofproto/ofproto-dpif.h | 4 ++ tests/ofp-print.at | 6 +-- tests/ofproto-dpif.at | 53 +++++++++++++++++++ tests/system-traffic.at | 63 +++++++++++++++++++++++ 20 files changed, 330 insertions(+), 39 deletions(-)