mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	sfc: support TC decap rules matching on enc_src_port
Allow efx_tc_encap_match entries to include a udp_sport and a udp_sport_mask. As with enc_ip_tos, use pseudos to enforce that all encap matches within a given <src_ip,dst_ip,udp_dport> tuple have the same udp_sport_mask. Note that since we use a single layer of pseudos for both fields, two matches that differ in (say) udp_sport value aren't permitted to have different ip_tos_mask, even though this would technically be safe. Current userland TC does not support setting enc_src_port; this patch was tested with an iproute2 patched to support it. Signed-off-by: Edward Cree <ecree.xilinx@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3c9561c0a5
								
							
						
					
					
						commit
						b6583d5e9e
					
				
					 4 changed files with 41 additions and 16 deletions
				
			
		| 
						 | 
					@ -485,7 +485,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
 | 
				
			||||||
 * MAE.  All the fields are exact-match, except possibly ENC_IP_TOS.
 | 
					 * MAE.  All the fields are exact-match, except possibly ENC_IP_TOS.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
 | 
					int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
 | 
				
			||||||
				   u8 ip_tos_mask,
 | 
									   u8 ip_tos_mask, __be16 udp_sport_mask,
 | 
				
			||||||
				   struct netlink_ext_ack *extack)
 | 
									   struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	u8 *supported_fields = efx->tc->caps->outer_rule_fields;
 | 
						u8 *supported_fields = efx->tc->caps->outer_rule_fields;
 | 
				
			||||||
| 
						 | 
					@ -506,6 +506,14 @@ int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
 | 
				
			||||||
	if (CHECK(ENC_L4_DPORT) ||
 | 
						if (CHECK(ENC_L4_DPORT) ||
 | 
				
			||||||
	    CHECK(ENC_IP_PROTO))
 | 
						    CHECK(ENC_IP_PROTO))
 | 
				
			||||||
		return rc;
 | 
							return rc;
 | 
				
			||||||
 | 
						typ = classify_mask((const u8 *)&udp_sport_mask, sizeof(udp_sport_mask));
 | 
				
			||||||
 | 
						rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_L4_SPORT],
 | 
				
			||||||
 | 
										 typ);
 | 
				
			||||||
 | 
						if (rc) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s",
 | 
				
			||||||
 | 
									       mask_type_name(typ), "enc_src_port");
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	typ = classify_mask(&ip_tos_mask, sizeof(ip_tos_mask));
 | 
						typ = classify_mask(&ip_tos_mask, sizeof(ip_tos_mask));
 | 
				
			||||||
	rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_IP_TOS],
 | 
						rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_ENC_IP_TOS],
 | 
				
			||||||
					 typ);
 | 
										 typ);
 | 
				
			||||||
| 
						 | 
					@ -1011,6 +1019,10 @@ int efx_mae_register_encap_match(struct efx_nic *efx,
 | 
				
			||||||
				encap->udp_dport);
 | 
									encap->udp_dport);
 | 
				
			||||||
	MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
 | 
						MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
 | 
				
			||||||
				~(__be16)0);
 | 
									~(__be16)0);
 | 
				
			||||||
 | 
						MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE,
 | 
				
			||||||
 | 
									encap->udp_sport);
 | 
				
			||||||
 | 
						MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK,
 | 
				
			||||||
 | 
									encap->udp_sport_mask);
 | 
				
			||||||
	MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP);
 | 
						MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, IPPROTO_UDP);
 | 
				
			||||||
	MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0);
 | 
						MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, ~0);
 | 
				
			||||||
	MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS,
 | 
						MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,7 +82,7 @@ int efx_mae_match_check_caps(struct efx_nic *efx,
 | 
				
			||||||
			     const struct efx_tc_match_fields *mask,
 | 
								     const struct efx_tc_match_fields *mask,
 | 
				
			||||||
			     struct netlink_ext_ack *extack);
 | 
								     struct netlink_ext_ack *extack);
 | 
				
			||||||
int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
 | 
					int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6,
 | 
				
			||||||
				   u8 ip_tos_mask,
 | 
									   u8 ip_tos_mask, __be16 udp_sport_mask,
 | 
				
			||||||
				   struct netlink_ext_ack *extack);
 | 
									   struct netlink_ext_ack *extack);
 | 
				
			||||||
int efx_mae_check_encap_type_supported(struct efx_nic *efx,
 | 
					int efx_mae_check_encap_type_supported(struct efx_nic *efx,
 | 
				
			||||||
				       enum efx_encap_type typ);
 | 
									       enum efx_encap_type typ);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -377,6 +377,7 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
					    enum efx_encap_type type,
 | 
										    enum efx_encap_type type,
 | 
				
			||||||
					    enum efx_tc_em_pseudo_type em_type,
 | 
										    enum efx_tc_em_pseudo_type em_type,
 | 
				
			||||||
					    u8 child_ip_tos_mask,
 | 
										    u8 child_ip_tos_mask,
 | 
				
			||||||
 | 
										    __be16 child_udp_sport_mask,
 | 
				
			||||||
					    struct netlink_ext_ack *extack)
 | 
										    struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct efx_tc_encap_match *encap, *old, *pseudo = NULL;
 | 
						struct efx_tc_encap_match *encap, *old, *pseudo = NULL;
 | 
				
			||||||
| 
						 | 
					@ -425,11 +426,7 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
		NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port");
 | 
							NL_SET_ERR_MSG_MOD(extack, "Egress encap match is not exact on dst UDP port");
 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
							return -EOPNOTSUPP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (match->mask.enc_sport) {
 | 
						if (match->mask.enc_sport || match->mask.enc_ip_tos) {
 | 
				
			||||||
		NL_SET_ERR_MSG_MOD(extack, "Egress encap match on src UDP port not supported");
 | 
					 | 
				
			||||||
		return -EOPNOTSUPP;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (match->mask.enc_ip_tos) {
 | 
					 | 
				
			||||||
		struct efx_tc_match pmatch = *match;
 | 
							struct efx_tc_match pmatch = *match;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (em_type == EFX_TC_EM_PSEUDO_MASK) { /* can't happen */
 | 
							if (em_type == EFX_TC_EM_PSEUDO_MASK) { /* can't happen */
 | 
				
			||||||
| 
						 | 
					@ -438,9 +435,12 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		pmatch.value.enc_ip_tos = 0;
 | 
							pmatch.value.enc_ip_tos = 0;
 | 
				
			||||||
		pmatch.mask.enc_ip_tos = 0;
 | 
							pmatch.mask.enc_ip_tos = 0;
 | 
				
			||||||
 | 
							pmatch.value.enc_sport = 0;
 | 
				
			||||||
 | 
							pmatch.mask.enc_sport = 0;
 | 
				
			||||||
		rc = efx_tc_flower_record_encap_match(efx, &pmatch, type,
 | 
							rc = efx_tc_flower_record_encap_match(efx, &pmatch, type,
 | 
				
			||||||
						      EFX_TC_EM_PSEUDO_MASK,
 | 
											      EFX_TC_EM_PSEUDO_MASK,
 | 
				
			||||||
						      match->mask.enc_ip_tos,
 | 
											      match->mask.enc_ip_tos,
 | 
				
			||||||
 | 
											      match->mask.enc_sport,
 | 
				
			||||||
						      extack);
 | 
											      extack);
 | 
				
			||||||
		if (rc)
 | 
							if (rc)
 | 
				
			||||||
			return rc;
 | 
								return rc;
 | 
				
			||||||
| 
						 | 
					@ -452,7 +452,8 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
		goto fail_pseudo;
 | 
							goto fail_pseudo;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos, extack);
 | 
						rc = efx_mae_check_encap_match_caps(efx, ipv6, match->mask.enc_ip_tos,
 | 
				
			||||||
 | 
										    match->mask.enc_sport, extack);
 | 
				
			||||||
	if (rc)
 | 
						if (rc)
 | 
				
			||||||
		goto fail_pseudo;
 | 
							goto fail_pseudo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -472,6 +473,9 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
	encap->ip_tos = match->value.enc_ip_tos;
 | 
						encap->ip_tos = match->value.enc_ip_tos;
 | 
				
			||||||
	encap->ip_tos_mask = match->mask.enc_ip_tos;
 | 
						encap->ip_tos_mask = match->mask.enc_ip_tos;
 | 
				
			||||||
	encap->child_ip_tos_mask = child_ip_tos_mask;
 | 
						encap->child_ip_tos_mask = child_ip_tos_mask;
 | 
				
			||||||
 | 
						encap->udp_sport = match->value.enc_sport;
 | 
				
			||||||
 | 
						encap->udp_sport_mask = match->mask.enc_sport;
 | 
				
			||||||
 | 
						encap->child_udp_sport_mask = child_udp_sport_mask;
 | 
				
			||||||
	encap->type = em_type;
 | 
						encap->type = em_type;
 | 
				
			||||||
	encap->pseudo = pseudo;
 | 
						encap->pseudo = pseudo;
 | 
				
			||||||
	old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht,
 | 
						old = rhashtable_lookup_get_insert_fast(&efx->tc->encap_match_ht,
 | 
				
			||||||
| 
						 | 
					@ -493,9 +497,9 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
			NL_SET_ERR_MSG_MOD(extack, "Pseudo encap match conflicts with existing direct entry");
 | 
								NL_SET_ERR_MSG_MOD(extack, "Pseudo encap match conflicts with existing direct entry");
 | 
				
			||||||
			return -EEXIST;
 | 
								return -EEXIST;
 | 
				
			||||||
		case EFX_TC_EM_PSEUDO_MASK:
 | 
							case EFX_TC_EM_PSEUDO_MASK:
 | 
				
			||||||
			/* old EM is protecting a ToS-qualified filter, so may
 | 
								/* old EM is protecting a ToS- or src port-qualified
 | 
				
			||||||
			 * only be shared with another pseudo for the same
 | 
								 * filter, so may only be shared with another pseudo
 | 
				
			||||||
			 * ToS mask.
 | 
								 * for the same ToS and src port masks.
 | 
				
			||||||
			 */
 | 
								 */
 | 
				
			||||||
			if (em_type != EFX_TC_EM_PSEUDO_MASK) {
 | 
								if (em_type != EFX_TC_EM_PSEUDO_MASK) {
 | 
				
			||||||
				NL_SET_ERR_MSG_FMT_MOD(extack,
 | 
									NL_SET_ERR_MSG_FMT_MOD(extack,
 | 
				
			||||||
| 
						 | 
					@ -510,6 +514,13 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx,
 | 
				
			||||||
						       old->child_ip_tos_mask);
 | 
											       old->child_ip_tos_mask);
 | 
				
			||||||
				return -EEXIST;
 | 
									return -EEXIST;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								if (child_udp_sport_mask != old->child_udp_sport_mask) {
 | 
				
			||||||
 | 
									NL_SET_ERR_MSG_FMT_MOD(extack,
 | 
				
			||||||
 | 
											       "Pseudo encap match for UDP src port mask %#x conflicts with existing pseudo(MASK) entry for mask %#x",
 | 
				
			||||||
 | 
											       child_udp_sport_mask,
 | 
				
			||||||
 | 
											       old->child_udp_sport_mask);
 | 
				
			||||||
 | 
									return -EEXIST;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		default: /* Unrecognised pseudo-type.  Just say no */
 | 
							default: /* Unrecognised pseudo-type.  Just say no */
 | 
				
			||||||
			NL_SET_ERR_MSG_FMT_MOD(extack,
 | 
								NL_SET_ERR_MSG_FMT_MOD(extack,
 | 
				
			||||||
| 
						 | 
					@ -704,7 +715,7 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rc = efx_tc_flower_record_encap_match(efx, &match, type,
 | 
							rc = efx_tc_flower_record_encap_match(efx, &match, type,
 | 
				
			||||||
						      EFX_TC_EM_DIRECT, 0,
 | 
											      EFX_TC_EM_DIRECT, 0, 0,
 | 
				
			||||||
						      extack);
 | 
											      extack);
 | 
				
			||||||
		if (rc)
 | 
							if (rc)
 | 
				
			||||||
			goto release;
 | 
								goto release;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,11 +84,11 @@ static inline bool efx_tc_match_is_encap(const struct efx_tc_match_fields *mask)
 | 
				
			||||||
 * @EFX_TC_EM_DIRECT: real HW entry in Outer Rule table; not a pseudo.
 | 
					 * @EFX_TC_EM_DIRECT: real HW entry in Outer Rule table; not a pseudo.
 | 
				
			||||||
 *	Hardware index in &struct efx_tc_encap_match.fw_id is valid.
 | 
					 *	Hardware index in &struct efx_tc_encap_match.fw_id is valid.
 | 
				
			||||||
 * @EFX_TC_EM_PSEUDO_MASK: registered by an encap match which includes a
 | 
					 * @EFX_TC_EM_PSEUDO_MASK: registered by an encap match which includes a
 | 
				
			||||||
 *	match on an optional field (currently only ip_tos), to prevent an
 | 
					 *	match on an optional field (currently ip_tos and/or udp_sport),
 | 
				
			||||||
 *	overlapping encap match _without_ optional fields.
 | 
					 *	to prevent an overlapping encap match _without_ optional fields.
 | 
				
			||||||
 *	The pseudo encap match may be referenced again by an encap match
 | 
					 *	The pseudo encap match may be referenced again by an encap match
 | 
				
			||||||
 *	with a different ip_tos value, but all ip_tos_mask must match the
 | 
					 *	with different values for these fields, but all masks must match the
 | 
				
			||||||
 *	first (stored in our child_ip_tos_mask).
 | 
					 *	first (stored in our child_* fields).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
enum efx_tc_em_pseudo_type {
 | 
					enum efx_tc_em_pseudo_type {
 | 
				
			||||||
	EFX_TC_EM_DIRECT,
 | 
						EFX_TC_EM_DIRECT,
 | 
				
			||||||
| 
						 | 
					@ -99,10 +99,12 @@ struct efx_tc_encap_match {
 | 
				
			||||||
	__be32 src_ip, dst_ip;
 | 
						__be32 src_ip, dst_ip;
 | 
				
			||||||
	struct in6_addr src_ip6, dst_ip6;
 | 
						struct in6_addr src_ip6, dst_ip6;
 | 
				
			||||||
	__be16 udp_dport;
 | 
						__be16 udp_dport;
 | 
				
			||||||
 | 
						__be16 udp_sport, udp_sport_mask;
 | 
				
			||||||
	u8 ip_tos, ip_tos_mask;
 | 
						u8 ip_tos, ip_tos_mask;
 | 
				
			||||||
	struct rhash_head linkage;
 | 
						struct rhash_head linkage;
 | 
				
			||||||
	enum efx_encap_type tun_type;
 | 
						enum efx_encap_type tun_type;
 | 
				
			||||||
	u8 child_ip_tos_mask;
 | 
						u8 child_ip_tos_mask;
 | 
				
			||||||
 | 
						__be16 child_udp_sport_mask;
 | 
				
			||||||
	refcount_t ref;
 | 
						refcount_t ref;
 | 
				
			||||||
	enum efx_tc_em_pseudo_type type;
 | 
						enum efx_tc_em_pseudo_type type;
 | 
				
			||||||
	u32 fw_id; /* index of this entry in firmware encap match table */
 | 
						u32 fw_id; /* index of this entry in firmware encap match table */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue