All of lore.kernel.org
 help / color / mirror / Atom feed
* Expressive limitation: (daddr,dport) <--> (daddr',dport')
@ 2020-06-07  5:08 Rick van Rein
  2020-06-07 22:08 ` Pablo Neira Ayuso
  0 siblings, 1 reply; 6+ messages in thread
From: Rick van Rein @ 2020-06-07  5:08 UTC (permalink / raw)
  To: netfilter-devel

Hello,

I seem to be running into an expressive limitation of nft while trying
to do stateless translation.  I prefer statelessness because it it is
clearer for bidirectionality / peering, and saves lookup times.

After nat64, I have a small set of IPv6 addresses and I would like to
map their (daddr,dport) or better even (daddr,proto,dport) tuples to
outgoing (daddr',dport').  Effectively, port forwarding for IPv6.

Individual rules work, like this one side of a bidir portmap:

nft add rule ip6 raw prerouting \
   ip6 daddr $PREFIX::64:75 \
   tcp dport 8080 \
   ip6 daddr set $PREFIX::100:20 \
   tcp dport set 80 \
   notrack

I have problems doing this with the map construct, presumably because it
does not atomically replace (daddr,dport) by (daddr',dport') but instead
does two assignments with intermediate alterede state.  This is bound to
work in many cases, but it can give undesired crossover behaviours
[namely between incoming IPs if they map to the same daddr' while coming
from the same dport]:

nft add rule ip6 raw prerouting \
   ip6 daddr set \
      ip6 daddr . tcp dport \
         map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
   tcp dport set \
      ip6 daddr . tcp dport \
         map { $PREFIX::100:20 . 8080 : 80 } \
   notrack

So now I am wondering,

 0. Is there a way to use maps as atomic setter for (daddr,dport)?
 1. Can I reach back to the original value of a just-modified value?
 2. Is there a variable, or stack, to prepare with the old value?

Without this, I need to work around an expressive limitation,

 * Fan out from a few IPv6 to many first to minimise rule clashes
 * Make separate maps and rules and maps for each of the IPv6 addresses

Both sound to me like a lack of expressiveness, or that I missed how.

Thanks!
 -Rick

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Expressive limitation: (daddr,dport) <--> (daddr',dport')
  2020-06-07  5:08 Expressive limitation: (daddr,dport) <--> (daddr',dport') Rick van Rein
@ 2020-06-07 22:08 ` Pablo Neira Ayuso
  2020-06-08 10:02   ` Rick van Rein
  0 siblings, 1 reply; 6+ messages in thread
From: Pablo Neira Ayuso @ 2020-06-07 22:08 UTC (permalink / raw)
  To: Rick van Rein; +Cc: netfilter-devel

Hi Rick,

On Sun, Jun 07, 2020 at 07:08:50AM +0200, Rick van Rein wrote:
> Hello,
> 
> I seem to be running into an expressive limitation of nft while trying
> to do stateless translation.  I prefer statelessness because it it is
> clearer for bidirectionality / peering, and saves lookup times.
> 
> After nat64, I have a small set of IPv6 addresses and I would like to
> map their (daddr,dport) or better even (daddr,proto,dport) tuples to
> outgoing (daddr',dport').  Effectively, port forwarding for IPv6.
> 
> Individual rules work, like this one side of a bidir portmap:
> 
> nft add rule ip6 raw prerouting \
>    ip6 daddr $PREFIX::64:75 \
>    tcp dport 8080 \
>    ip6 daddr set $PREFIX::100:20 \
>    tcp dport set 80 \
>    notrack
> 
> I have problems doing this with the map construct, presumably because it
> does not atomically replace (daddr,dport) by (daddr',dport') but instead
> does two assignments with intermediate alterede state.

Right.

> This is bound to work in many cases, but it can give undesired
> crossover behaviours [namely between incoming IPs if they map to the
> same daddr' while coming from the same dport]:
> 
> nft add rule ip6 raw prerouting \
>    ip6 daddr set \
>       ip6 daddr . tcp dport \
>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
>    tcp dport set \
>       ip6 daddr . tcp dport \
>          map { $PREFIX::100:20 . 8080 : 80 } \

So, you would consolidate this in one single rule? So there is one
single lookup to obtain the IP address and the destination port for
the stateless packet mangling.

>    notrack
> 
> So now I am wondering,
> 
>  0. Is there a way to use maps as atomic setter for (daddr,dport)?

Not yet.

>  1. Can I reach back to the original value of a just-modified value?

You mean, the original header field that was just mangled? Like
matching on the former IP address before the mangling?

>  2. Is there a variable, or stack, to prepare with the old value?

But this is to achieve the atomic mangling that you describe above or
you have something else in mind? You would like to store the former IP
daddr in some scratchpad area that can be accessed later on, right?

Thanks.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Expressive limitation: (daddr,dport) <--> (daddr',dport')
  2020-06-07 22:08 ` Pablo Neira Ayuso
@ 2020-06-08 10:02   ` Rick van Rein
  2020-06-08 10:31     ` Pablo Neira Ayuso
  0 siblings, 1 reply; 6+ messages in thread
From: Rick van Rein @ 2020-06-08 10:02 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Hi Pablo / NFT-dev,

>> This is bound to work in many cases, but it can give undesired
>> crossover behaviours [namely between incoming IPs if they map to the
>> same daddr' while coming from the same dport]:
>>
>> nft add rule ip6 raw prerouting \
>>    ip6 daddr set \
>>       ip6 daddr . tcp dport \
>>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
>>    tcp dport set \
>>       ip6 daddr . tcp dport \
>>          map { $PREFIX::100:20 . 8080 : 80 } \
> 
> So, you would consolidate this in one single rule? So there is one
> single lookup to obtain the IP address and the destination port for
> the stateless packet mangling.

It already is a single rule, but a single mapping, or one that appears
like one.  In reality, I use dynamic map @refs, of course.

A single lookup would avoid the problem that the key has changed in the
second lookup.

I played around, trying if I could "ip6 daddr . tcp dport set" and
perhaps have a map with elements like "{ $PREFIX::64:75 . 8080 :
$PREFIX::100:20 . 80 }" but did not find a syntax.  [I've been missing a
formal syntax, it's all examples so I wasn't sure if this was possible
at all.]  It'd look like

new_nft add rule ip6 raw prerouting \
   ip6 daddr . tcp dport set \
      ip6 daddr . tcp dport \
         map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 . 80 }


>>  0. Is there a way to use maps as atomic setter for (daddr,dport)?
> 
> Not yet.

Ah, you spotted the problem too.  No surprise ;-)

>>  1. Can I reach back to the original value of a just-modified value?
> 
> You mean, the original header field that was just mangled? Like
> matching on the former IP address before the mangling?

Yes, exactly.  That way, I can use two maps but find the right
combination of addr/port without intermediate key changes.

>>  2. Is there a variable, or stack, to prepare with the old value?
> 
> But this is to achieve the atomic mangling that you describe above or
> you have something else in mind? You would like to store the former IP
> daddr in some scratchpad area that can be accessed later on, right?

It is another possible way to get to the old value so I can make the
same mapping.

I could imagine storing the old daddr in daddr2 then mapping daddr and
using daddr2 in the second map looking to find the matching port.  That
might look like

new_nft add rule ip6 raw prerouting \
   rulevar set ip6 daddr \
   ip6 daddr set \
      ip6 daddr . tcp dport \
         map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
   tcp dport set \
      rulevar . tcp dport \
         map { $PREFIX::100:20 . 8080 : 80 } \

If the language internally uses a stack, I could imagine pushing the old
value(s) to prepare for the second map, then perform the first map and
continue with the second.  That might look like

new_nft add rule ip6 raw prerouting \
   ip6 daddr push \
   ip6 daddr set \
      ip6 daddr . tcp dport \
         map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
   tcp dport set \
      pop . tcp dport \
         map { $PREFIX::100:20 . 8080 : 80 } \


The examples are just three syntaxes I can think of.


Thanks,
 -Rick

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Expressive limitation: (daddr,dport) <--> (daddr',dport')
  2020-06-08 10:02   ` Rick van Rein
@ 2020-06-08 10:31     ` Pablo Neira Ayuso
  2020-06-08 11:01       ` Rick van Rein
  0 siblings, 1 reply; 6+ messages in thread
From: Pablo Neira Ayuso @ 2020-06-08 10:31 UTC (permalink / raw)
  To: Rick van Rein; +Cc: netfilter-devel

Hi Rick,

On Mon, Jun 08, 2020 at 12:02:03PM +0200, Rick van Rein wrote:
> Hi Pablo / NFT-dev,
> 
> >> This is bound to work in many cases, but it can give undesired
> >> crossover behaviours [namely between incoming IPs if they map to the
> >> same daddr' while coming from the same dport]:
> >>
> >> nft add rule ip6 raw prerouting \
> >>    ip6 daddr set \
> >>       ip6 daddr . tcp dport \
> >>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
> >>    tcp dport set \
> >>       ip6 daddr . tcp dport \
> >>          map { $PREFIX::100:20 . 8080 : 80 } \
> > 
> > So, you would consolidate this in one single rule? So there is one
> > single lookup to obtain the IP address and the destination port for
> > the stateless packet mangling.
> 
> It already is a single rule, but a single mapping, or one that appears
> like one.  In reality, I use dynamic map @refs, of course.

Right, one single mapping.

> A single lookup would avoid the problem that the key has changed in the
> second lookup.
> 
> I played around, trying if I could "ip6 daddr . tcp dport set" and
> perhaps have a map with elements like "{ $PREFIX::64:75 . 8080 :
> $PREFIX::100:20 . 80 }" but did not find a syntax.  [I've been missing a
> formal syntax, it's all examples so I wasn't sure if this was possible
> at all.]  It'd look like
> 
> new_nft add rule ip6 raw prerouting \
>    ip6 daddr . tcp dport set \
>       ip6 daddr . tcp dport \
>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 . 80 }

I see.

> >>  0. Is there a way to use maps as atomic setter for (daddr,dport)?
> > 
> > Not yet.
> 
> Ah, you spotted the problem too.  No surprise ;-)
> 
> >>  1. Can I reach back to the original value of a just-modified value?
> > 
> > You mean, the original header field that was just mangled? Like
> > matching on the former IP address before the mangling?
> 
> Yes, exactly.  That way, I can use two maps but find the right
> combination of addr/port without intermediate key changes.
> 
> >>  2. Is there a variable, or stack, to prepare with the old value?
> > 
> > But this is to achieve the atomic mangling that you describe above or
> > you have something else in mind? You would like to store the former IP
> > daddr in some scratchpad area that can be accessed later on, right?
> 
> It is another possible way to get to the old value so I can make the
> same mapping.
> 
> I could imagine storing the old daddr in daddr2 then mapping daddr and
> using daddr2 in the second map looking to find the matching port.  That
> might look like
> 
> new_nft add rule ip6 raw prerouting \
>    rulevar set ip6 daddr \
>    ip6 daddr set \
>       ip6 daddr . tcp dport \
>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
>    tcp dport set \
>       rulevar . tcp dport \
>          map { $PREFIX::100:20 . 8080 : 80 } \
> 
> If the language internally uses a stack, I could imagine pushing the old
> value(s) to prepare for the second map, then perform the first map and
> continue with the second.  That might look like
> 
> new_nft add rule ip6 raw prerouting \
>    ip6 daddr push \
>    ip6 daddr set \
>       ip6 daddr . tcp dport \
>          map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
>    tcp dport set \
>       pop . tcp dport \
>          map { $PREFIX::100:20 . 8080 : 80 } \
> 
> 
> The examples are just three syntaxes I can think of.

OK, but you only need this "stack" idea is alternative proposal to get
the "one single mapping" idea working, correct?

Thanks.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: Expressive limitation: (daddr,dport) <--> (daddr',dport')
  2020-06-08 10:31     ` Pablo Neira Ayuso
@ 2020-06-08 11:01       ` Rick van Rein
  0 siblings, 0 replies; 6+ messages in thread
From: Rick van Rein @ 2020-06-08 11:01 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Hi Pablo,

Thanks for following up on this :)

>> The examples are just three syntaxes I can think of.
>
> OK, but you only need this "stack" idea is alternative proposal to get
> the "one single mapping" idea working, correct?

Yes, indeed.

-Rick

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Expressive limitation: (daddr,dport) <--> (daddr',dport')
@ 2020-06-01 16:08 Rick van Rein
  0 siblings, 0 replies; 6+ messages in thread
From: Rick van Rein @ 2020-06-01 16:08 UTC (permalink / raw)
  To: netfilter

Hello,

I seem to be running into an expressive limitation of nft while trying
to do stateless translation.  I prefer statelessness because it has no
direction, and should support of peer-to-peer mechanisms better than
stateful dnat/snat.  I also suspect it is more efficient.

After nat64, I have a small set of IPv6 addresses and I would like to
map their (daddr,dport) or better even (daddr,proto,dport) tuples to
outgoing (daddr',dport').  Effectively, this is port forwarding.

If I specify single rules for single mappings (one direction only for
now) I can see the expected response to the connection attempt:

nft add rule ip6 raw prerouting \
   ip6 daddr $PREFIX::64:75 \
   tcp dport 8080 \
   ip6 daddr set $PREFIX::100:20 \
   tcp dport set 80 \
   notrack

I have problems doing this with the map construct, presumably because it
does not atomically replace (daddr,dport) by (daddr',dport') but instead
does two assignments with intermediate alterede state.  This is bound to
work in many cases, but it can give undesired crossover behaviours
[namely between incoming IPs if they map to the same daddr' while coming
from the same dport]:

nft add rule ip6 raw prerouting \
   ip6 daddr set \
      ip6 daddr . tcp dport \
         map { $PREFIX::64:75 . 8080 : $PREFIX::100:20 } \
   tcp dport set \
      ip6 daddr . tcp dport \
         map { $PREFIX::100:20 . 8080 : 80 } \
   notrack

So now I am wondering,

 0. Is there a way to use maps as atomic setter for (daddr,dport)?
 1. Can I reach back to the original value of a just-modified value?

If this won't work, I can still make separate rules for each of the few
values for daddr, but I wanted to ask just to be sure that this is
something that cannot be expressed by nft.

That aside, I am making the switch, and I am pleased by the logic of
nft; just the syntax takes some getting used to; I've seen ":" between
actions, and wondered if that could be an atomic composer?


Thanks!
 -Rick

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-06-08 11:01 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-07  5:08 Expressive limitation: (daddr,dport) <--> (daddr',dport') Rick van Rein
2020-06-07 22:08 ` Pablo Neira Ayuso
2020-06-08 10:02   ` Rick van Rein
2020-06-08 10:31     ` Pablo Neira Ayuso
2020-06-08 11:01       ` Rick van Rein
  -- strict thread matches above, loose matches on Subject: below --
2020-06-01 16:08 Rick van Rein

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.