+ struct nlattr *a[ARRAY_SIZE(odp_flow_policy)];
+ struct odp_header *odp_header;
+ struct nlmsghdr *nlmsg;
+ struct genlmsghdr *genl;
+ struct ofpbuf b;
+
+ dpif_linux_flow_init(flow);
+
+ ofpbuf_use_const(&b, buf->data, buf->size);
+ nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);
+ genl = ofpbuf_try_pull(&b, sizeof *genl);
+ odp_header = ofpbuf_try_pull(&b, sizeof *odp_header);
+ if (!nlmsg || !genl || !odp_header
+ || nlmsg->nlmsg_type != odp_flow_family
+ || !nl_policy_parse(&b, 0, odp_flow_policy, a,
+ ARRAY_SIZE(odp_flow_policy))) {
+ return EINVAL;
+ }
+
+ flow->nlmsg_flags = nlmsg->nlmsg_flags;
+ flow->dp_ifindex = odp_header->dp_ifindex;
+ flow->key = nl_attr_get(a[ODP_FLOW_ATTR_KEY]);
+ flow->key_len = nl_attr_get_size(a[ODP_FLOW_ATTR_KEY]);
+ if (a[ODP_FLOW_ATTR_ACTIONS]) {
+ flow->actions = nl_attr_get(a[ODP_FLOW_ATTR_ACTIONS]);
+ flow->actions_len = nl_attr_get_size(a[ODP_FLOW_ATTR_ACTIONS]);
+ }
+ if (a[ODP_FLOW_ATTR_STATS]) {
+ flow->stats = nl_attr_get(a[ODP_FLOW_ATTR_STATS]);
+ }
+ if (a[ODP_FLOW_ATTR_TCP_FLAGS]) {
+ flow->tcp_flags = nl_attr_get(a[ODP_FLOW_ATTR_TCP_FLAGS]);
+ }
+ if (a[ODP_FLOW_ATTR_USED]) {
+ flow->used = nl_attr_get(a[ODP_FLOW_ATTR_USED]);
+ }
+ return 0;
+}
+
+/* Appends to 'buf' (which must initially be empty) a "struct odp_header"
+ * followed by Netlink attributes corresponding to 'flow'. */
+static void
+dpif_linux_flow_to_ofpbuf(const struct dpif_linux_flow *flow,
+ struct ofpbuf *buf)
+{
+ struct odp_header *odp_header;
+
+ nl_msg_put_genlmsghdr(buf, 0, odp_flow_family,
+ NLM_F_REQUEST | NLM_F_ECHO | flow->nlmsg_flags,
+ flow->cmd, 1);
+
+ odp_header = ofpbuf_put_uninit(buf, sizeof *odp_header);
+ odp_header->dp_ifindex = flow->dp_ifindex;
+
+ if (flow->key_len) {
+ nl_msg_put_unspec(buf, ODP_FLOW_ATTR_KEY, flow->key, flow->key_len);
+ }
+
+ if (flow->actions || flow->actions_len) {
+ nl_msg_put_unspec(buf, ODP_FLOW_ATTR_ACTIONS,
+ flow->actions, flow->actions_len);