[dpdk-dev,v2,2/2] examples/ipsec-secgw: add target queues in flow actions

Message ID cd13bbdab7f86507e1928805a93c4e5c4491aa6d.1512396570.git.nelio.laranjeiro@6wind.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/Intel-compilation success Compilation OK

Commit Message

Nélio Laranjeiro Dec. 4, 2017, 2:11 p.m. UTC
  Mellanox INNOVA NIC needs to have final target queue actions to perform
inline crypto.

Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>

---

Changes in v2:

 * Test the rule by PASSTHRU/RSS/QUEUE and apply the first one validated.
---
 examples/ipsec-secgw/ipsec.c | 81 ++++++++++++++++++++++++++++++++++++++++----
 examples/ipsec-secgw/ipsec.h |  2 +-
 2 files changed, 76 insertions(+), 7 deletions(-)
  

Comments

Anoob Joseph Dec. 7, 2017, 9:47 a.m. UTC | #1
Hi Nelio,


On 12/04/2017 07:41 PM, Nelio Laranjeiro wrote:
> Mellanox INNOVA NIC needs to have final target queue actions to perform
> inline crypto.
>
> Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
>
> ---
>
> Changes in v2:
>
>   * Test the rule by PASSTHRU/RSS/QUEUE and apply the first one validated.
> ---
>   examples/ipsec-secgw/ipsec.c | 81 ++++++++++++++++++++++++++++++++++++++++----
>   examples/ipsec-secgw/ipsec.h |  2 +-
>   2 files changed, 76 insertions(+), 7 deletions(-)
>
> diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
> index 17bd7620d..f8823fb94 100644
> --- a/examples/ipsec-secgw/ipsec.c
> +++ b/examples/ipsec-secgw/ipsec.c
> @@ -142,6 +142,7 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   							rte_eth_dev_get_sec_ctx(
>   							sa->portid);
>   			const struct rte_security_capability *sec_cap;
> +			int ret = 0;
>   
>   			sa->sec_session = rte_security_session_create(ctx,
>   					&sess_conf, ipsec_ctx->session_pool);
> @@ -173,6 +174,10 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   				return -1;
>   			}
>   
> +			sa->attr.egress = (sa->direction ==
> +					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> +			sa->attr.ingress = (sa->direction ==
> +					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
>   			sa->ol_flags = sec_cap->ol_flags;
>   			sa->security_ctx = ctx;
>   			sa->pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
> @@ -201,15 +206,79 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   			sa->action[0].type = RTE_FLOW_ACTION_TYPE_SECURITY;
>   			sa->action[0].conf = sa->sec_session;
>   
> -			sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
> -
> -			sa->attr.egress = (sa->direction ==
> -					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> -			sa->attr.ingress = (sa->direction ==
> -					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
> +			if (sa->attr.ingress) {
> +				uint8_t rss_key[40];
> +				struct rte_eth_rss_conf rss_conf = {
> +					.rss_key = rss_key,
> +					.rss_key_len = 40,
> +				};
> +				struct rte_eth_dev *eth_dev;
> +				union {
> +					struct rte_flow_action_rss rss;
> +					struct {
> +					const struct rte_eth_rss_conf *rss_conf;
> +					uint16_t num;
> +					uint16_t queue[RTE_MAX_QUEUES_PER_PORT];
> +					} local;
> +				} action_rss;
> +				unsigned int i;
> +				unsigned int j;
> +
> +				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
> +				/*
> +				 * Try implicitly PASSTHRU, it can also be
> +				 * explicit.
> +				 */
May be we can get rid of this check. You can do the check with RSS and 
then QUEUE. That should be fine. SECURITY is terminating on Cavium 
hardware, but according to the spec it is a non-terminating meta action. 
We can stick to that. For Cavium hardware the PMD will give success to 
SECURITY+QUEUE. That should resolve the issue.
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (!ret)
> +					goto flow_create;
> +				/* Try RSS. */
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_RSS;
> +				sa->action[1].conf = &action_rss;
> +				eth_dev = ctx->device;
> +				rte_eth_dev_rss_hash_conf_get(sa->portid,
> +							      &rss_conf);
> +				for (i = 0, j = 0;
> +				     i < eth_dev->data->nb_rx_queues; ++i)
> +					if (eth_dev->data->rx_queues[i])
> +						action_rss.local.queue[j++] = i;
> +				action_rss.local.num = j;
> +				action_rss.local.rss_conf = &rss_conf;
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (!ret)
> +					goto flow_create;
> +				/* Try Queue. */
> +				for (i = 0;
> +				     i < eth_dev->data->nb_rx_queues; ++i)
> +					if (eth_dev->data->rx_queues[i])
> +						break;
> +				if (i != eth_dev->data->nb_rx_queues)
> +					return -1;
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_QUEUE;
> +				sa->action[1].conf =
> +					&(struct rte_flow_action_queue){
> +					.index = i,
> +				};
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (ret)
> +					goto flow_create_failure;
> +			} else {
> +				sa->action[1].type =
> +					RTE_FLOW_ACTION_TYPE_PASSTHRU;
> +				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
> +			}
> +flow_create:
>   			sa->flow = rte_flow_create(sa->portid,
>   				&sa->attr, sa->pattern, sa->action, &err);
>   			if (sa->flow == NULL) {
> +flow_create_failure:
>   				RTE_LOG(ERR, IPSEC,
>   					"Failed to create ipsec flow msg: %s\n",
>   					err.message);
> diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
> index 775b316ff..82ffc1c6d 100644
> --- a/examples/ipsec-secgw/ipsec.h
> +++ b/examples/ipsec-secgw/ipsec.h
> @@ -133,7 +133,7 @@ struct ipsec_sa {
>   	uint32_t ol_flags;
>   
>   #define MAX_RTE_FLOW_PATTERN (4)
> -#define MAX_RTE_FLOW_ACTIONS (2)
> +#define MAX_RTE_FLOW_ACTIONS (4)
>   	struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN];
>   	struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS];
>   	struct rte_flow_attr attr;
  
Nélio Laranjeiro Dec. 7, 2017, 12:22 p.m. UTC | #2
Hi Anoob,

On Thu, Dec 07, 2017 at 03:17:40PM +0530, Anoob wrote:
> Hi Nelio,
> 
> 
> On 12/04/2017 07:41 PM, Nelio Laranjeiro wrote:
> > Mellanox INNOVA NIC needs to have final target queue actions to perform
> > inline crypto.
> > 
> > Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
> > 
> > ---
> > 
> > Changes in v2:
> > 
> >   * Test the rule by PASSTHRU/RSS/QUEUE and apply the first one validated.
> > ---
> >   examples/ipsec-secgw/ipsec.c | 81 ++++++++++++++++++++++++++++++++++++++++----
> >   examples/ipsec-secgw/ipsec.h |  2 +-
> >   2 files changed, 76 insertions(+), 7 deletions(-)
> > 
> > diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
> > index 17bd7620d..f8823fb94 100644
> > --- a/examples/ipsec-secgw/ipsec.c
> > +++ b/examples/ipsec-secgw/ipsec.c
> > @@ -142,6 +142,7 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
> >   							rte_eth_dev_get_sec_ctx(
> >   							sa->portid);
> >   			const struct rte_security_capability *sec_cap;
> > +			int ret = 0;
> >   			sa->sec_session = rte_security_session_create(ctx,
> >   					&sess_conf, ipsec_ctx->session_pool);
> > @@ -173,6 +174,10 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
> >   				return -1;
> >   			}
> > +			sa->attr.egress = (sa->direction ==
> > +					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> > +			sa->attr.ingress = (sa->direction ==
> > +					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
> >   			sa->ol_flags = sec_cap->ol_flags;
> >   			sa->security_ctx = ctx;
> >   			sa->pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
> > @@ -201,15 +206,79 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
> >   			sa->action[0].type = RTE_FLOW_ACTION_TYPE_SECURITY;
> >   			sa->action[0].conf = sa->sec_session;
> > -			sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
> > -
> > -			sa->attr.egress = (sa->direction ==
> > -					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> > -			sa->attr.ingress = (sa->direction ==
> > -					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
> > +			if (sa->attr.ingress) {
> > +				uint8_t rss_key[40];
> > +				struct rte_eth_rss_conf rss_conf = {
> > +					.rss_key = rss_key,
> > +					.rss_key_len = 40,
> > +				};
> > +				struct rte_eth_dev *eth_dev;
> > +				union {
> > +					struct rte_flow_action_rss rss;
> > +					struct {
> > +					const struct rte_eth_rss_conf *rss_conf;
> > +					uint16_t num;
> > +					uint16_t queue[RTE_MAX_QUEUES_PER_PORT];
> > +					} local;
> > +				} action_rss;
> > +				unsigned int i;
> > +				unsigned int j;
> > +
> > +				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
> > +				/*
> > +				 * Try implicitly PASSTHRU, it can also be
> > +				 * explicit.
> > +				 */
> May be we can get rid of this check. You can do the check with RSS and then
> QUEUE. That should be fine. SECURITY is terminating on Cavium hardware, but
> according to the spec it is a non-terminating meta action. We can stick to
> that. For Cavium hardware the PMD will give success to SECURITY+QUEUE. That
> should resolve the issue.
<snip>

I'll remove it in a v3, I will send it tomorrow to let a little more
time for other people to review. 

Thanks,
  
Anoob Joseph Dec. 8, 2017, 2 p.m. UTC | #3
Hi Nelio,


On 12/04/2017 07:41 PM, Nelio Laranjeiro wrote:
> Mellanox INNOVA NIC needs to have final target queue actions to perform
> inline crypto.
>
> Signed-off-by: Nelio Laranjeiro <nelio.laranjeiro@6wind.com>
>
> ---
>
> Changes in v2:
>
>   * Test the rule by PASSTHRU/RSS/QUEUE and apply the first one validated.
> ---
>   examples/ipsec-secgw/ipsec.c | 81 ++++++++++++++++++++++++++++++++++++++++----
>   examples/ipsec-secgw/ipsec.h |  2 +-
>   2 files changed, 76 insertions(+), 7 deletions(-)
>
> diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
> index 17bd7620d..f8823fb94 100644
> --- a/examples/ipsec-secgw/ipsec.c
> +++ b/examples/ipsec-secgw/ipsec.c
> @@ -142,6 +142,7 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   							rte_eth_dev_get_sec_ctx(
>   							sa->portid);
>   			const struct rte_security_capability *sec_cap;
> +			int ret = 0;
>   
>   			sa->sec_session = rte_security_session_create(ctx,
>   					&sess_conf, ipsec_ctx->session_pool);
> @@ -173,6 +174,10 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   				return -1;
>   			}
>   
> +			sa->attr.egress = (sa->direction ==
> +					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> +			sa->attr.ingress = (sa->direction ==
> +					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
>   			sa->ol_flags = sec_cap->ol_flags;
>   			sa->security_ctx = ctx;
>   			sa->pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
> @@ -201,15 +206,79 @@ create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
>   			sa->action[0].type = RTE_FLOW_ACTION_TYPE_SECURITY;
>   			sa->action[0].conf = sa->sec_session;
>   
> -			sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
> -
> -			sa->attr.egress = (sa->direction ==
> -					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
> -			sa->attr.ingress = (sa->direction ==
> -					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
> +			if (sa->attr.ingress) {
> +				uint8_t rss_key[40];
> +				struct rte_eth_rss_conf rss_conf = {
> +					.rss_key = rss_key,
> +					.rss_key_len = 40,
> +				};
> +				struct rte_eth_dev *eth_dev;
> +				union {
> +					struct rte_flow_action_rss rss;
> +					struct {
> +					const struct rte_eth_rss_conf *rss_conf;
> +					uint16_t num;
> +					uint16_t queue[RTE_MAX_QUEUES_PER_PORT];
> +					} local;
> +				} action_rss;
> +				unsigned int i;
> +				unsigned int j;
> +
> +				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
> +				/*
> +				 * Try implicitly PASSTHRU, it can also be
> +				 * explicit.
> +				 */
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (!ret)
> +					goto flow_create;
> +				/* Try RSS. */
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_RSS;
> +				sa->action[1].conf = &action_rss;
> +				eth_dev = ctx->device;
> +				rte_eth_dev_rss_hash_conf_get(sa->portid,
> +							      &rss_conf);
> +				for (i = 0, j = 0;
> +				     i < eth_dev->data->nb_rx_queues; ++i)
> +					if (eth_dev->data->rx_queues[i])
> +						action_rss.local.queue[j++] = i;
> +				action_rss.local.num = j;
> +				action_rss.local.rss_conf = &rss_conf;
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (!ret)
> +					goto flow_create;
> +				/* Try Queue. */
> +				for (i = 0;
> +				     i < eth_dev->data->nb_rx_queues; ++i)
> +					if (eth_dev->data->rx_queues[i])
> +						break;
Is the following check correct?
> +				if (i != eth_dev->data->nb_rx_queues)
> +					return -1;
> +				sa->action[1].type = RTE_FLOW_ACTION_TYPE_QUEUE;
> +				sa->action[1].conf =
> +					&(struct rte_flow_action_queue){
> +					.index = i,
> +				};
> +				ret = rte_flow_validate(sa->portid, &sa->attr,
> +							sa->pattern, sa->action,
> +							&err);
> +				if (ret)
> +					goto flow_create_failure;
> +			} else {
> +				sa->action[1].type =
> +					RTE_FLOW_ACTION_TYPE_PASSTHRU;
> +				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
> +			}
> +flow_create:
>   			sa->flow = rte_flow_create(sa->portid,
>   				&sa->attr, sa->pattern, sa->action, &err);
>   			if (sa->flow == NULL) {
> +flow_create_failure:
>   				RTE_LOG(ERR, IPSEC,
>   					"Failed to create ipsec flow msg: %s\n",
>   					err.message);
> diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
> index 775b316ff..82ffc1c6d 100644
> --- a/examples/ipsec-secgw/ipsec.h
> +++ b/examples/ipsec-secgw/ipsec.h
> @@ -133,7 +133,7 @@ struct ipsec_sa {
>   	uint32_t ol_flags;
>   
>   #define MAX_RTE_FLOW_PATTERN (4)
> -#define MAX_RTE_FLOW_ACTIONS (2)
> +#define MAX_RTE_FLOW_ACTIONS (4)
>   	struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN];
>   	struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS];
>   	struct rte_flow_attr attr;
  
Nélio Laranjeiro Dec. 8, 2017, 2:40 p.m. UTC | #4
On Fri, Dec 08, 2017 at 07:30:03PM +0530, Anoob wrote:
> Hi Nelio,
> 
> 
[...]
> > +					goto flow_create;
> > +				/* Try Queue. */
> > +				for (i = 0;
> > +				     i < eth_dev->data->nb_rx_queues; ++i)
> > +					if (eth_dev->data->rx_queues[i])
> > +						break;
> Is the following check correct?
[...]

For an application, it seems not necessary.  The application knows which
queues are configured in the drivers has it has made the configuration.

Removing it in the v3.

Thanks,
  
Anoob Joseph Dec. 8, 2017, 4:40 p.m. UTC | #5
HI Nelio,


On 08-12-2017 20:10, Nelio Laranjeiro wrote:
> On Fri, Dec 08, 2017 at 07:30:03PM +0530, Anoob wrote:
>> Hi Nelio,
>>
>>
> [...]
>>> +					goto flow_create;
>>> +				/* Try Queue. */
>>> +				for (i = 0;
>>> +				     i < eth_dev->data->nb_rx_queues; ++i)
>>> +					if (eth_dev->data->rx_queues[i])
>>> +						break;
>> Is the following check correct?
> [...]
>
> For an application, it seems not necessary.  The application knows which
> queues are configured in the drivers has it has made the configuration.
>
> Removing it in the v3.
I think you misunderstood me here.

I was talking about the following line.

+				if (i != eth_dev->data->nb_rx_queues)
+					return -1;

Shouldn't it be?

+				if (i == eth_dev->data->nb_rx_queues)
+					return -1;

Thanks,
Anoob
  
Nélio Laranjeiro Dec. 11, 2017, 8:21 a.m. UTC | #6
Hi Anoob,

On Fri, Dec 08, 2017 at 10:10:28PM +0530, Anoob Joseph wrote:
> HI Nelio,
> 
> 
> On 08-12-2017 20:10, Nelio Laranjeiro wrote:
> > On Fri, Dec 08, 2017 at 07:30:03PM +0530, Anoob wrote:
> > > Hi Nelio,
> > > 
> > > 
> > [...]
> > > > +					goto flow_create;
> > > > +				/* Try Queue. */
> > > > +				for (i = 0;
> > > > +				     i < eth_dev->data->nb_rx_queues; ++i)
> > > > +					if (eth_dev->data->rx_queues[i])
> > > > +						break;
> > > Is the following check correct?
> > [...]
> > 
> > For an application, it seems not necessary.  The application knows which
> > queues are configured in the drivers has it has made the configuration.
> > 
> > Removing it in the v3.
> I think you misunderstood me here.

Indeed, I misunderstood,

> I was talking about the following line.
> 
> +				if (i != eth_dev->data->nb_rx_queues)
> +					return -1;
> 
> Shouldn't it be?
> 
> +				if (i == eth_dev->data->nb_rx_queues)
> +					return -1;

Yes it should.

Anyway, I don't thing it is necessary to keep this check, from what I
saw in the application source code, it initialise all Rx queues up to
nb_rx_queues without leaving any hole.
According to this, I'll just remove this verification,  is it okay for
you?

Thanks,
  
Anoob Joseph Dec. 11, 2017, 9 a.m. UTC | #7
Hi Nelio,


On 12/11/2017 01:51 PM, Nelio Laranjeiro wrote:
> Hi Anoob,
>
> On Fri, Dec 08, 2017 at 10:10:28PM +0530, Anoob Joseph wrote:
>> HI Nelio,
>>
>>
>> On 08-12-2017 20:10, Nelio Laranjeiro wrote:
>>> On Fri, Dec 08, 2017 at 07:30:03PM +0530, Anoob wrote:
>>>> Hi Nelio,
>>>>
>>>>
>>> [...]
>>>>> +					goto flow_create;
>>>>> +				/* Try Queue. */
>>>>> +				for (i = 0;
>>>>> +				     i < eth_dev->data->nb_rx_queues; ++i)
>>>>> +					if (eth_dev->data->rx_queues[i])
>>>>> +						break;
>>>> Is the following check correct?
>>> [...]
>>>
>>> For an application, it seems not necessary.  The application knows which
>>> queues are configured in the drivers has it has made the configuration.
>>>
>>> Removing it in the v3.
>> I think you misunderstood me here.
> Indeed, I misunderstood,
>
>> I was talking about the following line.
>>
>> +				if (i != eth_dev->data->nb_rx_queues)
>> +					return -1;
>>
>> Shouldn't it be?
>>
>> +				if (i == eth_dev->data->nb_rx_queues)
>> +					return -1;
> Yes it should.
>
> Anyway, I don't thing it is necessary to keep this check, from what I
> saw in the application source code, it initialise all Rx queues up to
> nb_rx_queues without leaving any hole.
> According to this, I'll just remove this verification,  is it okay for
> you?
I think you can just use Queue 0 here. So you can get rid of the checks 
etc. For real applications, we should have an entry in SA structure 
which will determine the Queue to be used. Even for RSS, something like 
that would be required.

Thanks,
Anoob
  

Patch

diff --git a/examples/ipsec-secgw/ipsec.c b/examples/ipsec-secgw/ipsec.c
index 17bd7620d..f8823fb94 100644
--- a/examples/ipsec-secgw/ipsec.c
+++ b/examples/ipsec-secgw/ipsec.c
@@ -142,6 +142,7 @@  create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
 							rte_eth_dev_get_sec_ctx(
 							sa->portid);
 			const struct rte_security_capability *sec_cap;
+			int ret = 0;
 
 			sa->sec_session = rte_security_session_create(ctx,
 					&sess_conf, ipsec_ctx->session_pool);
@@ -173,6 +174,10 @@  create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
 				return -1;
 			}
 
+			sa->attr.egress = (sa->direction ==
+					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
+			sa->attr.ingress = (sa->direction ==
+					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
 			sa->ol_flags = sec_cap->ol_flags;
 			sa->security_ctx = ctx;
 			sa->pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH;
@@ -201,15 +206,79 @@  create_session(struct ipsec_ctx *ipsec_ctx, struct ipsec_sa *sa)
 			sa->action[0].type = RTE_FLOW_ACTION_TYPE_SECURITY;
 			sa->action[0].conf = sa->sec_session;
 
-			sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
-
-			sa->attr.egress = (sa->direction ==
-					RTE_SECURITY_IPSEC_SA_DIR_EGRESS);
-			sa->attr.ingress = (sa->direction ==
-					RTE_SECURITY_IPSEC_SA_DIR_INGRESS);
+			if (sa->attr.ingress) {
+				uint8_t rss_key[40];
+				struct rte_eth_rss_conf rss_conf = {
+					.rss_key = rss_key,
+					.rss_key_len = 40,
+				};
+				struct rte_eth_dev *eth_dev;
+				union {
+					struct rte_flow_action_rss rss;
+					struct {
+					const struct rte_eth_rss_conf *rss_conf;
+					uint16_t num;
+					uint16_t queue[RTE_MAX_QUEUES_PER_PORT];
+					} local;
+				} action_rss;
+				unsigned int i;
+				unsigned int j;
+
+				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
+				/*
+				 * Try implicitly PASSTHRU, it can also be
+				 * explicit.
+				 */
+				sa->action[1].type = RTE_FLOW_ACTION_TYPE_END;
+				ret = rte_flow_validate(sa->portid, &sa->attr,
+							sa->pattern, sa->action,
+							&err);
+				if (!ret)
+					goto flow_create;
+				/* Try RSS. */
+				sa->action[1].type = RTE_FLOW_ACTION_TYPE_RSS;
+				sa->action[1].conf = &action_rss;
+				eth_dev = ctx->device;
+				rte_eth_dev_rss_hash_conf_get(sa->portid,
+							      &rss_conf);
+				for (i = 0, j = 0;
+				     i < eth_dev->data->nb_rx_queues; ++i)
+					if (eth_dev->data->rx_queues[i])
+						action_rss.local.queue[j++] = i;
+				action_rss.local.num = j;
+				action_rss.local.rss_conf = &rss_conf;
+				ret = rte_flow_validate(sa->portid, &sa->attr,
+							sa->pattern, sa->action,
+							&err);
+				if (!ret)
+					goto flow_create;
+				/* Try Queue. */
+				for (i = 0;
+				     i < eth_dev->data->nb_rx_queues; ++i)
+					if (eth_dev->data->rx_queues[i])
+						break;
+				if (i != eth_dev->data->nb_rx_queues)
+					return -1;
+				sa->action[1].type = RTE_FLOW_ACTION_TYPE_QUEUE;
+				sa->action[1].conf =
+					&(struct rte_flow_action_queue){
+					.index = i,
+				};
+				ret = rte_flow_validate(sa->portid, &sa->attr,
+							sa->pattern, sa->action,
+							&err);
+				if (ret)
+					goto flow_create_failure;
+			} else {
+				sa->action[1].type =
+					RTE_FLOW_ACTION_TYPE_PASSTHRU;
+				sa->action[2].type = RTE_FLOW_ACTION_TYPE_END;
+			}
+flow_create:
 			sa->flow = rte_flow_create(sa->portid,
 				&sa->attr, sa->pattern, sa->action, &err);
 			if (sa->flow == NULL) {
+flow_create_failure:
 				RTE_LOG(ERR, IPSEC,
 					"Failed to create ipsec flow msg: %s\n",
 					err.message);
diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
index 775b316ff..82ffc1c6d 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -133,7 +133,7 @@  struct ipsec_sa {
 	uint32_t ol_flags;
 
 #define MAX_RTE_FLOW_PATTERN (4)
-#define MAX_RTE_FLOW_ACTIONS (2)
+#define MAX_RTE_FLOW_ACTIONS (4)
 	struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN];
 	struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS];
 	struct rte_flow_attr attr;