* [Noob Q.:] UDP, complementary DNAT+SNAT unicast->multicast ==> uh oh, conntrack hurdle...
@ 2015-09-29 16:00 Frantisek Rysanek
0 siblings, 0 replies; only message in thread
From: Frantisek Rysanek @ 2015-09-29 16:00 UTC (permalink / raw)
To: netfilter
Dear polite people of the netfilter community,
I have a newbie question about the NAT and conntrack on UDP traffic.
Let me start with the practical scenario of what I'm trying to
achieve:
This is a story from the world of SCADA, serial fieldbusses etc.
Industrial process control. There's a SCADA machine, acting as a
single master for some miscellaneous serial multi-exit fieldbus
(request+response style).
Traditionally, the master would talk straight to the serial bus,
running along all the slaves.
In our case, in the legacy system, the SCADA machine already uses
unicast UDP to send its requests to a "terminal server" (232<-->UDP
relay box), from where the serial bus runs a long way to all the
telemetry slaves, allegedly across further WAN bridges and whatnot.
The task at hand is to move more of this serial bus onto IP
infrastructure. To take the UDP traffic further along Ethernet, and
only convert to serial at some more focused "satellite" locations.
The key element in this plan is to use multicasting.
The Ethernet switching inftrastructure is there, with VLAN's,
IGMP snooping, even a querier (if the switch vendor is right in his
bold claims).
And the key constraint is, that the venerable SCADA machine
must not notice the change - that it must not require a
reconfiguration. Possibly it isn't all that easy to reconfigure the
software to send multicast traffic while awaiting unicast responses.
An obvious answer would be: do your homework, schedule some
downtime, hire a relevant SCADA consultant and just reconfigure the
darn old box.
Yes of course... but corporate politics aside, it is an interesting
academic exercise if nothing else :-) And it almost works...
=> at the level of UDP and IP addressing, what it apparently boils
down to is a requirement to:
1) DNAT the legacy unicast UDP packets (requests) to a new multicast
destination (IP address / mcast group), and
2) to SNAT the response traffic coming back, to look like it's coming
from the original "unicast request destination" = IP addr and UDP
port. The response traffic comes from a handful of unicast origins
(our choice of numbering = some nice subnet per multicast group = per
emulated fieldbus segment).
Original IP networking topology:
[SCADA] ---UDP unicast--- [legacy terminal server]
New topology, mimicking the old one for the SCADA box:
[SCADA] ---UDP uni--- [NAT router] ---UDP multi--- [dest.boxes]
On the requests, I need to DNAT both the destination IP address and
UDP port, and on the responses, I need to SNAT the source IP addr and
UDP port back to the legacy addr:port of the one legacy unicast
serial gateway.
I've actually tried configuring the two simple complementary NAT
rules. Modulo some subplots around multicast forwarding, the need for
a multicast routing daemon, a querier etc. - I'll add more details
further below.
Here I am, I have my two NAT rules in place, I've handled several
prerequisites (gotchas for the unwary), I have tcpdump running on the
two router interfaces, and it still doesn't work, for some reason...
=> now for the hurdle:
The request packets do get DNATted and forwarded just fine.
The response traffic gets dropped by the SNAT rule - probably because
the particular "touple" is already allocated by conntrack to the DNAT
session. The ephemeral conntrack table entry, conjured by the DNAT
rule upon the arrival of a first UDP packet in the "request"
direction, remains forever "unreplied" and thus relatively
short-lived, yet capable enough of preventing response traffic coming
back, from using the original "touple", which is now "owned" by the
DNAT conntrack ephemeral...
In /proc/net/ip_conntrack, I can see two ephemeral conntrack entries:
one for the DNAT rule (requests), one for the SNAT rule (responses).
=> the response direction actually earns a conntrack entry, but then
it gets dropped "per packet" anyway :-)
What I tried:
If I modify the SNAT rule, to make it "not perfectly complementary",
such as - I change the IP address *or* the UDP port in the SNAT
target, just by one unit, suddenly it works.
The way I've configured the NAT router, the SCADA is sending its UDP
requests to the NAT router's own IP address facing the SCADA on the
IP subnet they share = the requests are originally destined for the
INPUT chain on the router (before getting DNATted and forwarded) -
but I don't think it would make any difference, if I made the router
"step aside a little" and not mimick the original terminal server
(probably by some weird trick in the way of proxy-arp, as they're on
the same subnet in the first place anyway).
Based on the fact that DNAT happens in PREROUTING, I rest assured
that the NAT and conntrack would work exactly the same...
Clearly this is a result of the blessed marriage of NAT with
conntrack, within the netfilter framework. As far as I can tell, it's
impossible to create a "stateless" or one-way-only NAT rule.
There are no relevant targets in the mangle or raw tables.
If I exempt the request traffic from connection tracking via the raw
table's NOTRACK target, it doesn't get NATted either.
Netfilter NAT can only be connection-tracked, there's no other way.
Your return traffic shall be handled automagically by the conntrack
backend, period.
At this point I'd like to add a brief list of the "prerequisites"
that I went through, and of my environment, maybe for Google and
other apprentices coming after me:
I did set /proc/sys/net/ipv4/ip_forward to "1" manually.
As other sources point out, it's impossible to configure
/proc/sys/net/ipv4/conf/all/mc_forwarding
manually to "1" - the entry is inherently "read only",
and you need to install and run some multicast routing daemon to
enable multicast forwarding by some magical spell... Some use
smcroute, I've tried XORP. Packaged with the source code of DVByell,
you can find a basic config for XORP to work as a querier only, on a
single interface. In my practical case, I ended up using the default
XORP config generated by Debian Jessie, as it mentioned both of my
two Ethernet interfaces.
(I also found a Perl script called igmp-querier.pl, that did send
some v2 membership requests, but failed to provoke my IGMP client to
respond with a JOIN, which is something XORP has achieved right away
with its default v2 config.)
I'd like to admit to using the stock Debian Jessie kernel
(3.16.0-something).
The one thing why I don't think that this a distro-specific bug, or a
bug that got fixed in a more recent upstream kernel release, is that
the whole thing does *not* feel like a *bug*. The observed behavior
of connection tracking on UDP feels like the natural way to do it,
feels very correct to me, for the general case.
The point is, that what *I'm* trying to achieve is something
unobvious. To get my asymmetrical unicast-to-multicast-and-back NAT
setup to work, the NAT+conntrack engine needs additional knowledge of
my network numbering, and of my precise intentions - a special config
for a special case. The generic off-the-shelf tools available from
netfilter are not equipped to handle my special requreiments,
and the generic UDP connection tracking code sure has no way
of inferring my twisted intentions in all the required detail
(machines clearly haven't mastered telepathy just yet).
What to do about it:
If I absolutely insist on dragging this funny "special hybrid of all
legacy-preservation kludges" any further, I'd probably have to pull
off one of the following:
1) write my own "NAT helper" (like the ones already there for FTP,
H.323 and other protocols using multiple streams per user session)
2) enhance NAT and connection tracking on UDP in some generic way, to
allow for a configurable setup of such unholy asymmetrical multicast
traffic that I'm trying to achieve.
3) find a suitable Achilles heel in the stack of UDP NAT, generic
NAT, UDP conntrack and generic conntrack, where I could perform a
minimal focused hack, to prevent UDP SNAT from dropping my packets on
a touple already occupied by a previous ephemeral conntrack entry
belonging to DNAT. Again it feels like I would be mutilating
something that's perfectly correct in the first place. You *do* want
to prevent traffic from colliding on a touple already occupied.
In my special case, in the confined environment of an industrial
plant LAN, I can be pretty sure on that single router, that I'm not
going to NAT any other UDP traffic. All the rest of the traffic will
only get forwarded (TCP sessions for remote management etc.)
To be honest, I probably don't have the time (or balls, or both) for
1) or 2). I've barely had the time to fumble through the source code
to pick up some vocabulary that I'm merrily chucking around here :-)
=> if anyone would be so nice to suggest a particular place, where to
place a hack along the lines of 3), I'd love to get to know.
Either way might help other people do the same thing for different
reasons. The "traffic pattern" doesn't seem all that alien - except
for my special luxury, that the serial fieldbus protocol transported
over UDP uses its own addressing of the slave nodes, hence doesn't
care if responses from multiple slaves come from a common IP address.
Then again, it's also possible that I'm completely wrong about my
diagnosis of the problem = the gremlins are hiding elsewhere.
Or, possibly there's a way of configuring netfilter by the means
already available off the shelf, to achieve my lay man's quick job.
If you've read this far, thanks for your attention...
And have a nice day on whatever it is that you're busy with :-)
Frank Rysanek
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2015-09-29 16:00 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-29 16:00 [Noob Q.:] UDP, complementary DNAT+SNAT unicast->multicast ==> uh oh, conntrack hurdle Frantisek Rysanek
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.