4

I am using a client library (https://github.com/Beckhoff/ADS) to connect to a PLC from a Linux machine via TCP. However, the library is only able to perform a single connection between a source IP and the destination IP address of the PLC. My goal is to connect to the remote IP/PLC with multiple clients, i.e. the remote PLC must be able to distinguish the client connections based on the (different) IP address of the client.

I was hoping to accomplish this with some clever iptables rules; alternatively, maybe Linux network namespaces could work, too.

For the iptables route (pun?), my plan is to assign multiple IP addresses to the same NIC on the Linux machine. Since the remote service listens on a single port (48898) I cannot simply use different destination ports in the iptables rules. Therefore, I was thinking something like this:

Remote PLC: 192.168.1.10/24
Linux PC: 192.168.1.20/24 (ip addr add 192.168.1.20/24 dev enp2s0)
Linux PC: 192.168.1.21/24 (ip addr add 192.168.1.21/24 dev enp2s0)

  • Client-A simply connects from 192.168.1.20 to 192.168.1.10
  • Client-A simply connects from 192.168.1.21 to (virtual) 192.168.1.11

By using a "virtual" IP address for the remote PLC I want to be able to distinguish TCP connections, so I know what packets originate from Client-A and what packets originate from Client-B. Now I need to do some iptables magic along the lines:

  1. outgoing: if destination IP address == 192.168.1.11 1.1 -> rewrite to destination IP address 192.168.1.10 1.1 -> rewrite/ensure source IP address is 192.168.1.21

  2. incoming: if destination IP address == 192.168.1.21 2.1 -> rewrite source IP address to 192.168.1.11 (virtual IP of remote PLC)

Maybe it is even possible to "tag" TCP connections so it would be easier to distinguish which is which (step 2).

1 Answer 1

5

For this case, conntrack's NAT is enough to alter, tag and handle reply traffic correctly for the connection. One could use both DNAT and SNAT and bind their use together or use a mix of DNAT and adequate routes.

DNAT

This target is only valid in the nat table, in the PREROUTING and OUTPUT chains, and user-defined chains which are only called from those chains. It specifies that the destination address of the packet should be modified (and all future packets in this connection will also be mangled), and rules should cease being examined. [...]

SNAT

This target is only valid in the nat table, in the POSTROUTING and INPUT chains, and user-defined chains which are only called from those chains. It specifies that the source address of the packet should be modified (and all future packets in this connection will also be mangled), and rules should cease being examined. [...]

iptables -t nat -A OUTPUT -d 192.168.1.11 -j DNAT --to-destination 192.168.1.10
iptables -t nat -A OUTPUT -d 192.168.1.12 -j DNAT --to-destination 192.168.1.10
iptables -t nat -A OUTPUT -d 192.168.1.13 -j DNAT --to-destination 192.168.1.10
iptables -t nat -A OUTPUT -d 192.168.1.14 -j DNAT --to-destination 192.168.1.10

set the additional source addresses on the client:

ip address add 192.168.1.21/24 dev enp2s0
ip address add 192.168.1.22/24 dev enp2s0
ip address add 192.168.1.23/24 dev enp2s0
ip address add 192.168.1.24/24 dev enp2s0
  • and either bind the SNAT action conditionally to the initial destination address (before DNAT was done), using an adequate filter with iptables' conntrack match:

    iptables -t nat -A POSTROUTING -m conntrack --ctorigdst 192.168.1.11 -j SNAT --to-source 192.168.1.21
    iptables -t nat -A POSTROUTING -m conntrack --ctorigdst 192.168.1.12 -j SNAT --to-source 192.168.1.22
    iptables -t nat -A POSTROUTING -m conntrack --ctorigdst 192.168.1.13 -j SNAT --to-source 192.168.1.23
    iptables -t nat -A POSTROUTING -m conntrack --ctorigdst 192.168.1.14 -j SNAT --to-source 192.168.1.24
    
  • or instead of SNAT, much cleaner, use a route hinting directly the intended source address for each destination (even if nat/OUTPUT reroutes, the source address won't change anymore once set). This will allow the client and the ss -tn dport == 48898 to know and display the correct source address. This becomes:

    ip route add 192.168.1.11/32 dev enp2s0 src 192.168.1.21
    ip route add 192.168.1.12/32 dev enp2s0 src 192.168.1.22
    ip route add 192.168.1.13/32 dev enp2s0 src 192.168.1.23
    ip route add 192.168.1.14/32 dev enp2s0 src 192.168.1.24
    

This has to be done like this: one address at a time (DNAT could be simplified/factorized, but not the second part dealing with source).

A connection to the actual 192.168.1.10 address is left unchanged.

1
  • many thanks @a-b - excellent solution and explanation - with this approach I managed to make everything work!!
    – juwalter
    Jan 28 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.