@@ -184,28 +184,152 @@ netdev_tc_flow_dump_create(struct netdev *netdev)
{
struct netdev_flow_dump *dump = xzalloc(sizeof *dump);
+ memset(dump, 0, sizeof *dump);
+ dump->nl_dump = xzalloc(sizeof *dump->nl_dump);
dump->netdev = netdev_ref(netdev);
+ tc_dump_flower_start(netdev_get_ifindex(netdev), dump->nl_dump);
return dump;
}
int
netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
{
+ nl_dump_done(dump->nl_dump);
netdev_close(dump->netdev);
+ free(dump->nl_dump);
free(dump);
+ return 0;
+}
+
+static int
+parse_tc_flow_to_match(struct tc_flow *tc_flow,
+ struct match *match,
+ struct nlattr **actions,
+ struct dpif_flow_stats *stats,
+ struct ofpbuf *buf) {
+ size_t act_off;
+ struct tc_flow_key *key = &tc_flow->key;
+ struct tc_flow_key *mask = &tc_flow->mask;
+
+ match_init_catchall(match);
+ match_set_dl_type(match, key->eth_type);
+ match_set_dl_src_masked(match, key->src_mac, mask->src_mac);
+ match_set_dl_dst_masked(match, key->dst_mac, mask->dst_mac);
+ if (key->vlan_id || key->vlan_prio) {
+ match_set_dl_vlan(match, ntohs(key->vlan_id));
+ match_set_dl_vlan_pcp(match, ntohs(key->vlan_prio));
+ match_set_dl_type(match, key->encap_eth_type);
+ }
+
+ if (key->ip_proto &&
+ (key->eth_type == htons(ETH_P_IP)
+ || key->eth_type == htons(ETH_P_IPV6))) {
+ match_set_nw_proto(match, key->ip_proto);
+ }
+ match_set_nw_src_masked(match, key->ipv4.ipv4_src, mask->ipv4.ipv4_src);
+ match_set_nw_dst_masked(match, key->ipv4.ipv4_dst, mask->ipv4.ipv4_dst);
+
+ match_set_tp_dst_masked(match, key->dst_port, mask->dst_port);
+ match_set_tp_src_masked(match, key->src_port, mask->src_port);
+
+ if (tc_flow->tunnel.tunnel) {
+ match_set_tun_id(match, tc_flow->tunnel.id);
+ match_set_tun_src(match, tc_flow->tunnel.ipv4_src);
+ match_set_tun_dst(match, tc_flow->tunnel.ipv4_dst);
+ match_set_tp_dst(match, tc_flow->tunnel.tp_dst);
+ }
+
+ act_off = nl_msg_start_nested(buf, OVS_FLOW_ATTR_ACTIONS);
+ {
+ if (tc_flow->vlan_pop)
+ nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN);
+
+ if (tc_flow->vlan_push_id || tc_flow->vlan_push_prio) {
+ struct ovs_action_push_vlan *push;
+ push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_VLAN,
+ sizeof *push);
+
+ push->vlan_tpid = ntohs(ETH_TYPE_VLAN);
+ push->vlan_tci = ntohs(tc_flow->vlan_push_id
+ | (tc_flow->vlan_push_prio << 13)
+ | VLAN_CFI);
+ }
+
+ if (tc_flow->ifindex_out > 0) {
+ int ifx = netdev_hmap_port_get_byifidx(tc_flow->ifindex_out);
+
+ nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, ifx ? ifx : 0xFF);
+ }
+
+ if (tc_flow->set.set) {
+ size_t set_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_SET);
+ size_t tunnel_offset = nl_msg_start_nested(buf, OVS_KEY_ATTR_TUNNEL);
+
+ nl_msg_put_be64(buf, OVS_TUNNEL_KEY_ATTR_ID, tc_flow->set.id);
+ nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tc_flow->set.ipv4_src);
+ nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tc_flow->set.ipv4_dst);
+ nl_msg_put_be16(buf, OVS_TUNNEL_KEY_ATTR_TP_DST, tc_flow->set.tp_dst);
+
+ nl_msg_end_nested(buf, tunnel_offset);
+ nl_msg_end_nested(buf, set_offset);
+ }
+ }
+ nl_msg_end_nested(buf, act_off);
+
+ *actions = ofpbuf_at_assert(buf, act_off, sizeof(struct nlattr));
+
+ if (stats) {
+ memset(stats, 0, sizeof *stats);
+ stats->n_packets = get_32aligned_u64(&tc_flow->stats.n_packets);
+ stats->n_bytes = get_32aligned_u64(&tc_flow->stats.n_bytes);
+ stats->used = tc_flow->lastused;
+ }
return 0;
}
bool
-netdev_tc_flow_dump_next(struct netdev_flow_dump *dump OVS_UNUSED,
- struct match *match OVS_UNUSED,
- struct nlattr **actions OVS_UNUSED,
- struct dpif_flow_stats *stats OVS_UNUSED,
- ovs_u128 *ufid OVS_UNUSED,
- struct ofpbuf *rbuffer OVS_UNUSED,
- struct ofpbuf *wbuffer OVS_UNUSED)
+netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
+ struct match *match,
+ struct nlattr **actions,
+ struct dpif_flow_stats *stats,
+ ovs_u128 *ufid,
+ struct ofpbuf *rbuffer,
+ struct ofpbuf *wbuffer)
{
+ struct ofpbuf nl_flow;
+
+ for (;;) {
+ if (nl_dump_next(dump->nl_dump, &nl_flow, rbuffer)) {
+ struct tc_flow tc_flow;
+ struct flow *mask = &match->wc.masks;
+ ovs_u128 *uf;
+
+ if (parse_tc_flow(&nl_flow, &tc_flow) == EAGAIN) {
+ continue;
+ }
+
+ parse_tc_flow_to_match(&tc_flow, match, actions, stats, wbuffer);
+
+ uf = find_ufid(tc_flow.prio, tc_flow.handle, dump->netdev);
+ if (!uf) {
+ VLOG_DBG("unmatched flow dumped: %d, %d %d, creating ufid",
+ tc_flow.prio, tc_flow.handle,
+ netdev_get_ifindex(dump->netdev));
+ dpif_flow_hash(NULL, &match->flow, sizeof match->flow, ufid);
+ add_ufid_tc_mapping(ufid, tc_flow.prio, tc_flow.handle,
+ dump->netdev);
+ } else {
+ *ufid = *uf;
+ }
+
+ match_set_in_port(match, dump->port);
+ memset(&mask->in_port, 0xFF, sizeof mask->in_port);
+
+ return true;
+ }
+ break;
+ }
return false;
}