forked from mirrors/linux
		
	net/sched: act_ct: Instantiate flow table entry actions
NF flow table API associate 5-tuple rule with an action list by calling the flow table type action() CB to fill the rule's actions. In action CB of act_ct, populate the ct offload entry actions with a new ct_metadata action. Initialize the ct_metadata with the ct mark, label and zone information. If ct nat was performed, then also append the relevant packet mangle actions (e.g. ipv4/ipv6/tcp/udp header rewrites). Drivers that offload the ft entries may match on the 5-tuple and perform the action list. Signed-off-by: Paul Blakey <paulb@mellanox.com> Reviewed-by: Jiri Pirko <jiri@mellanox.com> Reviewed-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									978703f425
								
							
						
					
					
						commit
						9c26ba9b1f
					
				
					 4 changed files with 235 additions and 23 deletions
				
			
		| 
						 | 
					@ -136,6 +136,7 @@ enum flow_action_id {
 | 
				
			||||||
	FLOW_ACTION_SAMPLE,
 | 
						FLOW_ACTION_SAMPLE,
 | 
				
			||||||
	FLOW_ACTION_POLICE,
 | 
						FLOW_ACTION_POLICE,
 | 
				
			||||||
	FLOW_ACTION_CT,
 | 
						FLOW_ACTION_CT,
 | 
				
			||||||
 | 
						FLOW_ACTION_CT_METADATA,
 | 
				
			||||||
	FLOW_ACTION_MPLS_PUSH,
 | 
						FLOW_ACTION_MPLS_PUSH,
 | 
				
			||||||
	FLOW_ACTION_MPLS_POP,
 | 
						FLOW_ACTION_MPLS_POP,
 | 
				
			||||||
	FLOW_ACTION_MPLS_MANGLE,
 | 
						FLOW_ACTION_MPLS_MANGLE,
 | 
				
			||||||
| 
						 | 
					@ -225,6 +226,10 @@ struct flow_action_entry {
 | 
				
			||||||
			int action;
 | 
								int action;
 | 
				
			||||||
			u16 zone;
 | 
								u16 zone;
 | 
				
			||||||
		} ct;
 | 
							} ct;
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								u32 mark;
 | 
				
			||||||
 | 
								u32 labels[4];
 | 
				
			||||||
 | 
							} ct_metadata;
 | 
				
			||||||
		struct {				/* FLOW_ACTION_MPLS_PUSH */
 | 
							struct {				/* FLOW_ACTION_MPLS_PUSH */
 | 
				
			||||||
			u32		label;
 | 
								u32		label;
 | 
				
			||||||
			__be16		proto;
 | 
								__be16		proto;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,29 @@ struct nf_flow_rule;
 | 
				
			||||||
struct flow_offload;
 | 
					struct flow_offload;
 | 
				
			||||||
enum flow_offload_tuple_dir;
 | 
					enum flow_offload_tuple_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nf_flow_key {
 | 
				
			||||||
 | 
						struct flow_dissector_key_meta			meta;
 | 
				
			||||||
 | 
						struct flow_dissector_key_control		control;
 | 
				
			||||||
 | 
						struct flow_dissector_key_basic			basic;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct flow_dissector_key_ipv4_addrs	ipv4;
 | 
				
			||||||
 | 
							struct flow_dissector_key_ipv6_addrs	ipv6;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						struct flow_dissector_key_tcp			tcp;
 | 
				
			||||||
 | 
						struct flow_dissector_key_ports			tp;
 | 
				
			||||||
 | 
					} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nf_flow_match {
 | 
				
			||||||
 | 
						struct flow_dissector	dissector;
 | 
				
			||||||
 | 
						struct nf_flow_key	key;
 | 
				
			||||||
 | 
						struct nf_flow_key	mask;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct nf_flow_rule {
 | 
				
			||||||
 | 
						struct nf_flow_match	match;
 | 
				
			||||||
 | 
						struct flow_rule	*rule;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct nf_flowtable_type {
 | 
					struct nf_flowtable_type {
 | 
				
			||||||
	struct list_head		list;
 | 
						struct list_head		list;
 | 
				
			||||||
	int				family;
 | 
						int				family;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,29 +23,6 @@ struct flow_offload_work {
 | 
				
			||||||
	struct flow_offload	*flow;
 | 
						struct flow_offload	*flow;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct nf_flow_key {
 | 
					 | 
				
			||||||
	struct flow_dissector_key_meta			meta;
 | 
					 | 
				
			||||||
	struct flow_dissector_key_control		control;
 | 
					 | 
				
			||||||
	struct flow_dissector_key_basic			basic;
 | 
					 | 
				
			||||||
	union {
 | 
					 | 
				
			||||||
		struct flow_dissector_key_ipv4_addrs	ipv4;
 | 
					 | 
				
			||||||
		struct flow_dissector_key_ipv6_addrs	ipv6;
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	struct flow_dissector_key_tcp			tcp;
 | 
					 | 
				
			||||||
	struct flow_dissector_key_ports			tp;
 | 
					 | 
				
			||||||
} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct nf_flow_match {
 | 
					 | 
				
			||||||
	struct flow_dissector	dissector;
 | 
					 | 
				
			||||||
	struct nf_flow_key	key;
 | 
					 | 
				
			||||||
	struct nf_flow_key	mask;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct nf_flow_rule {
 | 
					 | 
				
			||||||
	struct nf_flow_match	match;
 | 
					 | 
				
			||||||
	struct flow_rule	*rule;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define NF_FLOW_DISSECTOR(__match, __type, __field)	\
 | 
					#define NF_FLOW_DISSECTOR(__match, __type, __field)	\
 | 
				
			||||||
	(__match)->dissector.offset[__type] =		\
 | 
						(__match)->dissector.offset[__type] =		\
 | 
				
			||||||
		offsetof(struct nf_flow_key, __field)
 | 
							offsetof(struct nf_flow_key, __field)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,214 @@ static const struct rhashtable_params zones_params = {
 | 
				
			||||||
	.automatic_shrinking = true,
 | 
						.automatic_shrinking = true,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct flow_action_entry *
 | 
				
			||||||
 | 
					tcf_ct_flow_table_flow_action_get_next(struct flow_action *flow_action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i = flow_action->num_entries++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &flow_action->entries[i];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcf_ct_add_mangle_action(struct flow_action *action,
 | 
				
			||||||
 | 
									     enum flow_action_mangle_base htype,
 | 
				
			||||||
 | 
									     u32 offset,
 | 
				
			||||||
 | 
									     u32 mask,
 | 
				
			||||||
 | 
									     u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct flow_action_entry *entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry = tcf_ct_flow_table_flow_action_get_next(action);
 | 
				
			||||||
 | 
						entry->id = FLOW_ACTION_MANGLE;
 | 
				
			||||||
 | 
						entry->mangle.htype = htype;
 | 
				
			||||||
 | 
						entry->mangle.mask = ~mask;
 | 
				
			||||||
 | 
						entry->mangle.offset = offset;
 | 
				
			||||||
 | 
						entry->mangle.val = val;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The following nat helper functions check if the inverted reverse tuple
 | 
				
			||||||
 | 
					 * (target) is different then the current dir tuple - meaning nat for ports
 | 
				
			||||||
 | 
					 * and/or ip is needed, and add the relevant mangle actions.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_ct_flow_table_add_action_nat_ipv4(const struct nf_conntrack_tuple *tuple,
 | 
				
			||||||
 | 
									      struct nf_conntrack_tuple target,
 | 
				
			||||||
 | 
									      struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (memcmp(&target.src.u3, &tuple->src.u3, sizeof(target.src.u3)))
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP4,
 | 
				
			||||||
 | 
										 offsetof(struct iphdr, saddr),
 | 
				
			||||||
 | 
										 0xFFFFFFFF,
 | 
				
			||||||
 | 
										 be32_to_cpu(target.src.u3.ip));
 | 
				
			||||||
 | 
						if (memcmp(&target.dst.u3, &tuple->dst.u3, sizeof(target.dst.u3)))
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP4,
 | 
				
			||||||
 | 
										 offsetof(struct iphdr, daddr),
 | 
				
			||||||
 | 
										 0xFFFFFFFF,
 | 
				
			||||||
 | 
										 be32_to_cpu(target.dst.u3.ip));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_ct_add_ipv6_addr_mangle_action(struct flow_action *action,
 | 
				
			||||||
 | 
									   union nf_inet_addr *addr,
 | 
				
			||||||
 | 
									   u32 offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++)
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
 | 
				
			||||||
 | 
										 i * sizeof(u32) + offset,
 | 
				
			||||||
 | 
										 0xFFFFFFFF, be32_to_cpu(addr->ip6[i]));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_ct_flow_table_add_action_nat_ipv6(const struct nf_conntrack_tuple *tuple,
 | 
				
			||||||
 | 
									      struct nf_conntrack_tuple target,
 | 
				
			||||||
 | 
									      struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (memcmp(&target.src.u3, &tuple->src.u3, sizeof(target.src.u3)))
 | 
				
			||||||
 | 
							tcf_ct_add_ipv6_addr_mangle_action(action, &target.src.u3,
 | 
				
			||||||
 | 
											   offsetof(struct ipv6hdr,
 | 
				
			||||||
 | 
												    saddr));
 | 
				
			||||||
 | 
						if (memcmp(&target.dst.u3, &tuple->dst.u3, sizeof(target.dst.u3)))
 | 
				
			||||||
 | 
							tcf_ct_add_ipv6_addr_mangle_action(action, &target.dst.u3,
 | 
				
			||||||
 | 
											   offsetof(struct ipv6hdr,
 | 
				
			||||||
 | 
												    daddr));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_ct_flow_table_add_action_nat_tcp(const struct nf_conntrack_tuple *tuple,
 | 
				
			||||||
 | 
									     struct nf_conntrack_tuple target,
 | 
				
			||||||
 | 
									     struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__be16 target_src = target.src.u.tcp.port;
 | 
				
			||||||
 | 
						__be16 target_dst = target.dst.u.tcp.port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (target_src != tuple->src.u.tcp.port)
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP,
 | 
				
			||||||
 | 
										 offsetof(struct tcphdr, source),
 | 
				
			||||||
 | 
										 0xFFFF, be16_to_cpu(target_src));
 | 
				
			||||||
 | 
						if (target_dst != tuple->dst.u.tcp.port)
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP,
 | 
				
			||||||
 | 
										 offsetof(struct tcphdr, dest),
 | 
				
			||||||
 | 
										 0xFFFF, be16_to_cpu(target_dst));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					tcf_ct_flow_table_add_action_nat_udp(const struct nf_conntrack_tuple *tuple,
 | 
				
			||||||
 | 
									     struct nf_conntrack_tuple target,
 | 
				
			||||||
 | 
									     struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						__be16 target_src = target.src.u.udp.port;
 | 
				
			||||||
 | 
						__be16 target_dst = target.dst.u.udp.port;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (target_src != tuple->src.u.udp.port)
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP,
 | 
				
			||||||
 | 
										 offsetof(struct udphdr, source),
 | 
				
			||||||
 | 
										 0xFFFF, be16_to_cpu(target_src));
 | 
				
			||||||
 | 
						if (target_dst != tuple->dst.u.udp.port)
 | 
				
			||||||
 | 
							tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP,
 | 
				
			||||||
 | 
										 offsetof(struct udphdr, dest),
 | 
				
			||||||
 | 
										 0xFFFF, be16_to_cpu(target_dst));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct,
 | 
				
			||||||
 | 
										      enum ip_conntrack_dir dir,
 | 
				
			||||||
 | 
										      struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct nf_conn_labels *ct_labels;
 | 
				
			||||||
 | 
						struct flow_action_entry *entry;
 | 
				
			||||||
 | 
						u32 *act_ct_labels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entry = tcf_ct_flow_table_flow_action_get_next(action);
 | 
				
			||||||
 | 
						entry->id = FLOW_ACTION_CT_METADATA;
 | 
				
			||||||
 | 
					#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)
 | 
				
			||||||
 | 
						entry->ct_metadata.mark = ct->mark;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						act_ct_labels = entry->ct_metadata.labels;
 | 
				
			||||||
 | 
						ct_labels = nf_ct_labels_find(ct);
 | 
				
			||||||
 | 
						if (ct_labels)
 | 
				
			||||||
 | 
							memcpy(act_ct_labels, ct_labels->bits, NF_CT_LABELS_MAX_SIZE);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							memset(act_ct_labels, 0, NF_CT_LABELS_MAX_SIZE);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tcf_ct_flow_table_add_action_nat(struct net *net,
 | 
				
			||||||
 | 
										    struct nf_conn *ct,
 | 
				
			||||||
 | 
										    enum ip_conntrack_dir dir,
 | 
				
			||||||
 | 
										    struct flow_action *action)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple;
 | 
				
			||||||
 | 
						struct nf_conntrack_tuple target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (tuple->src.l3num) {
 | 
				
			||||||
 | 
						case NFPROTO_IPV4:
 | 
				
			||||||
 | 
							tcf_ct_flow_table_add_action_nat_ipv4(tuple, target,
 | 
				
			||||||
 | 
											      action);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case NFPROTO_IPV6:
 | 
				
			||||||
 | 
							tcf_ct_flow_table_add_action_nat_ipv6(tuple, target,
 | 
				
			||||||
 | 
											      action);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (nf_ct_protonum(ct)) {
 | 
				
			||||||
 | 
						case IPPROTO_TCP:
 | 
				
			||||||
 | 
							tcf_ct_flow_table_add_action_nat_tcp(tuple, target, action);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case IPPROTO_UDP:
 | 
				
			||||||
 | 
							tcf_ct_flow_table_add_action_nat_udp(tuple, target, action);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tcf_ct_flow_table_fill_actions(struct net *net,
 | 
				
			||||||
 | 
										  const struct flow_offload *flow,
 | 
				
			||||||
 | 
										  enum flow_offload_tuple_dir tdir,
 | 
				
			||||||
 | 
										  struct nf_flow_rule *flow_rule)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct flow_action *action = &flow_rule->rule->action;
 | 
				
			||||||
 | 
						int num_entries = action->num_entries;
 | 
				
			||||||
 | 
						struct nf_conn *ct = flow->ct;
 | 
				
			||||||
 | 
						enum ip_conntrack_dir dir;
 | 
				
			||||||
 | 
						int i, err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (tdir) {
 | 
				
			||||||
 | 
						case FLOW_OFFLOAD_DIR_ORIGINAL:
 | 
				
			||||||
 | 
							dir = IP_CT_DIR_ORIGINAL;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case FLOW_OFFLOAD_DIR_REPLY:
 | 
				
			||||||
 | 
							dir = IP_CT_DIR_REPLY;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = tcf_ct_flow_table_add_action_nat(net, ct, dir, action);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							goto err_nat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tcf_ct_flow_table_add_action_meta(ct, dir, action);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					err_nat:
 | 
				
			||||||
 | 
						/* Clear filled actions */
 | 
				
			||||||
 | 
						for (i = num_entries; i < action->num_entries; i++)
 | 
				
			||||||
 | 
							memset(&action->entries[i], 0, sizeof(action->entries[i]));
 | 
				
			||||||
 | 
						action->num_entries = num_entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct nf_flowtable_type flowtable_ct = {
 | 
					static struct nf_flowtable_type flowtable_ct = {
 | 
				
			||||||
 | 
						.action		= tcf_ct_flow_table_fill_actions,
 | 
				
			||||||
	.owner		= THIS_MODULE,
 | 
						.owner		= THIS_MODULE,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue