+dpif_linux_flow_get__(const struct dpif *dpif_,
+ const struct nlattr *key, size_t key_len,
+ struct dpif_linux_flow *reply, struct ofpbuf **bufp)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_linux_flow request;
+
+ dpif_linux_flow_init(&request);
+ request.cmd = OVS_FLOW_CMD_GET;
+ request.dp_ifindex = dpif->dp_ifindex;
+ request.key = key;
+ request.key_len = key_len;
+ return dpif_linux_flow_transact(&request, reply, bufp);
+}
+
+static int
+dpif_linux_flow_get(const struct dpif *dpif_,
+ const struct nlattr *key, size_t key_len,
+ struct ofpbuf **actionsp, struct dpif_flow_stats *stats)
+{
+ struct dpif_linux_flow reply;
+ struct ofpbuf *buf;
+ int error;
+
+ error = dpif_linux_flow_get__(dpif_, key, key_len, &reply, &buf);
+ if (!error) {
+ if (stats) {
+ dpif_linux_flow_get_stats(&reply, stats);
+ }
+ if (actionsp) {
+ buf->data = (void *) reply.actions;
+ buf->size = reply.actions_len;
+ *actionsp = buf;
+ } else {
+ ofpbuf_delete(buf);
+ }
+ }
+ return error;
+}
+
+static int
+dpif_linux_flow_put(struct dpif *dpif_, enum dpif_flow_put_flags flags,
+ const struct nlattr *key, size_t key_len,
+ const struct nlattr *actions, size_t actions_len,
+ struct dpif_flow_stats *stats)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_linux_flow request, reply;
+ struct nlattr dummy_action;
+ struct ofpbuf *buf;
+ int error;
+
+ dpif_linux_flow_init(&request);
+ request.cmd = flags & DPIF_FP_CREATE ? OVS_FLOW_CMD_NEW : OVS_FLOW_CMD_SET;
+ request.dp_ifindex = dpif->dp_ifindex;
+ request.key = key;
+ request.key_len = key_len;
+ /* Ensure that OVS_FLOW_ATTR_ACTIONS will always be included. */
+ request.actions = actions ? actions : &dummy_action;
+ request.actions_len = actions_len;
+ if (flags & DPIF_FP_ZERO_STATS) {
+ request.clear = true;
+ }
+ request.nlmsg_flags = flags & DPIF_FP_MODIFY ? 0 : NLM_F_CREATE;
+ error = dpif_linux_flow_transact(&request,
+ stats ? &reply : NULL,
+ stats ? &buf : NULL);
+ if (!error && stats) {
+ dpif_linux_flow_get_stats(&reply, stats);
+ ofpbuf_delete(buf);
+ }
+ return error;
+}
+
+static int
+dpif_linux_flow_del(struct dpif *dpif_,
+ const struct nlattr *key, size_t key_len,
+ struct dpif_flow_stats *stats)
+{
+ struct dpif_linux *dpif = dpif_linux_cast(dpif_);
+ struct dpif_linux_flow request, reply;
+ struct ofpbuf *buf;
+ int error;
+
+ dpif_linux_flow_init(&request);
+ request.cmd = OVS_FLOW_CMD_DEL;
+ request.dp_ifindex = dpif->dp_ifindex;
+ request.key = key;
+ request.key_len = key_len;
+ error = dpif_linux_flow_transact(&request,
+ stats ? &reply : NULL,
+ stats ? &buf : NULL);
+ if (!error && stats) {
+ dpif_linux_flow_get_stats(&reply, stats);
+ ofpbuf_delete(buf);
+ }
+ return error;
+}
+
+struct dpif_linux_flow_state {
+ struct nl_dump dump;
+ struct dpif_linux_flow flow;
+ struct dpif_flow_stats stats;
+ struct ofpbuf *buf;
+};
+
+static int
+dpif_linux_flow_dump_start(const struct dpif *dpif_, void **statep)