* Returns 0 if successful. If no flow matches, returns ENOENT. On other
* failure, returns a positive errno value.
*
- * If 'actionsp' is nonnull, then on success '*actionsp' will be set to an
- * ofpbuf owned by the caller that contains the Netlink attributes for the
- * flow's actions. The caller must free the ofpbuf (with ofpbuf_delete()) when
- * it is no longer needed.
+ * On success, '*bufp' will be set to an ofpbuf owned by the caller that
+ * contains the response for 'maskp' and 'actionsp'. The caller must supply
+ * a valid pointer, and must free the ofpbuf (with ofpbuf_delete()) when it
+ * is no longer needed.
+ *
+ * If 'maskp' is nonnull, then on success '*maskp' will point to the
+ * Netlink attributes for the flow's mask, stored in '*bufp'. '*mask_len'
+ * will be set to the length of the mask attributes.
+ *
+ * If 'actionsp' is nonnull, then on success '*actionsp' will point to the
+ * Netlink attributes for the flow's actions, stored in '*bufp'.
+ * '*actions_len' will be set to the length of the actions attributes.
*
* If 'stats' is nonnull, then on success it will be updated with the flow's
* statistics. */
int
dpif_flow_get(const struct dpif *dpif,
- const struct nlattr *key, size_t key_len,
- struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
+ const struct nlattr *key, size_t key_len, struct ofpbuf **bufp,
+ struct nlattr **maskp, size_t *mask_len,
+ struct nlattr **actionsp, size_t *actions_len,
+ struct dpif_flow_stats *stats)
{
int error;
COVERAGE_INC(dpif_flow_get);
- error = dpif->dpif_class->flow_get(dpif, key, key_len, actionsp, stats);
+ *bufp = NULL;
+ error = dpif->dpif_class->flow_get(dpif, key, key_len, bufp,
+ maskp, mask_len,
+ actionsp, actions_len,
+ stats);
if (error) {
if (actionsp) {
*actionsp = NULL;
+ *actions_len = 0;
+ }
+ if (maskp) {
+ *maskp = NULL;
+ *mask_len = 0;
}
if (stats) {
memset(stats, 0, sizeof *stats);
}
+ ofpbuf_delete(*bufp);
}
if (should_log_flow_message(error)) {
const struct nlattr *actions;
- size_t actions_len;
+ size_t acts_len;
if (!error && actionsp) {
- actions = ofpbuf_data(*actionsp);
- actions_len = ofpbuf_size(*actionsp);
+ actions = *actionsp;
+ acts_len = *actions_len;
} else {
actions = NULL;
- actions_len = 0;
+ acts_len = 0;
}
log_flow_message(dpif, error, "flow_get", key, key_len,
- NULL, 0, stats, actions, actions_len);
+ NULL, 0, stats, actions, acts_len);
}
return error;
}
const struct nlattr *action, bool may_steal OVS_UNUSED)
{
struct dpif_execute_helper_aux *aux = aux_;
- struct dpif_execute execute;
int type = nl_attr_type(action);
switch ((enum ovs_action_attr)type) {
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_USERSPACE:
- execute.actions = action;
- execute.actions_len = NLA_ALIGN(action->nla_len);
+ case OVS_ACTION_ATTR_RECIRC: {
+ struct dpif_execute execute;
+ struct ofpbuf execute_actions;
+ uint64_t stub[256 / 8];
+
+ if (md->tunnel.ip_dst) {
+ /* The Linux kernel datapath throws away the tunnel information
+ * that we supply as metadata. We have to use a "set" action to
+ * supply it. */
+ ofpbuf_use_stub(&execute_actions, stub, sizeof stub);
+ odp_put_tunnel_action(&md->tunnel, &execute_actions);
+ ofpbuf_put(&execute_actions, action, NLA_ALIGN(action->nla_len));
+
+ execute.actions = ofpbuf_data(&execute_actions);
+ execute.actions_len = ofpbuf_size(&execute_actions);
+ } else {
+ execute.actions = action;
+ execute.actions_len = NLA_ALIGN(action->nla_len);
+ }
+
execute.packet = packet;
execute.md = *md;
execute.needs_help = false;
aux->error = aux->dpif->dpif_class->execute(aux->dpif, &execute);
+
+ if (md->tunnel.ip_dst) {
+ ofpbuf_uninit(&execute_actions);
+ }
break;
+ }
+ case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_PUSH_VLAN:
case OVS_ACTION_ATTR_POP_VLAN:
case OVS_ACTION_ATTR_PUSH_MPLS:
case OVS_ACTION_ATTR_SET:
case OVS_ACTION_ATTR_SAMPLE:
case OVS_ACTION_ATTR_UNSPEC:
- case OVS_ACTION_ATTR_RECIRC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}