dmroeder / pylogix

Read/Write data from Allen Bradley Compact/Control Logix PLC's
Apache License 2.0
598 stars 182 forks source link

Scan PLCs on Another Subnet #93

Open AndrewStuhr opened 4 years ago

AndrewStuhr commented 4 years ago

Hello

Should the Discover() function be able to find devices on a different subnet than the device running Pylogix?

I'm trying to scan PLCs on a different subnet. I can ping them, get their tags using GetTagList(), and read their tags using Read(). However, Discover() is not returning them. Is this expected behavior?

Thanks

UPDATE:

I was able to discover devices on another subnet by switching to a static IP and expanding the subnet mask from 255.255.252.0 to 255.255.0.0. I'm still curious if you know why Read() & GetTagList() work by default using DHCP, but why Discover() required an increased subnet mask.

Thanks again

dmroeder commented 4 years ago

I'm not 100% sure why you see this behavior, it most likely has to do with the fact that Discover() uses a UDP broadcast packet and certain network configurations block broadcast packets. This may be the nature of your configuration, the broadcast packet might not be reaching the devices.

One thing I'd be curious of, with the configuration where you were not seeing all of the devices, does RSLinx see them? I would suspect that it would not, I'm using the same method that RSLinx's Ethernet I/P driver uses. To know for sure, you would want to get your network configured, then setup a new driver. RSLinx has a tendency to "remember" devices and not purge them from the driver when they are not actively being detected.

AndrewStuhr commented 4 years ago

Thanks for the response. I've learned that our router has specific rules for allowing communication across subnets. It's likely that UDP broadcasts have not been added as a rule.

Interestingly, RSLinx does discover devices on the different subnet. I added a new driver like you mentioned. Here's screenshots of the test:

DHCP IP: adapter

PC on the .100 network: ip

RS Linx discovers devices on the .50 network: RSLinx

Pylogix not discovering devices on the .50 network: discover

Pylogix Code (example 20) code

TheFern2 commented 4 years ago

Can you do the following on cmd prompt, and post results:

route print
AndrewStuhr commented 4 years ago

routePrint

TheFern2 commented 4 years ago

If you can't ping the .50 network when your ip is set to 192.168.100.132, then do the following cmd:

Basically you are routing data from the .50 thorough the .100 gateway, and the -p is to make it permanent, don't use -p if you want route to be gone on reboot.

route -p add 192.168.50.0 mask 255.255.255.0 192.168.100.1

Then try discovery.py again once you can ping a device on the .50

psalwasser commented 4 years ago

What you're describing sounds like the devices configured at 192.168.50.X and 192.168.100.X are on the same LAN or VLAN as the 192.168.100.0/22 DHCP. EtherNet/IP drivers utilize broadcasts for discovery, and since you're on the same Broadcast domain, the devices respond using a layer 2 protocol and Linx receives those responses and populates the info into the tree.

This is simply conjecture, but I would imagine the reason that they aren't listed out in the results from discover.py is more due to the way Python handles networking, and not so much with the configuration of pylogix. Since the addresses are not in the same subnet (when configured with the /22 mask) Python may be discarding those response frames. However, when you open up the subnet mask to be wider, they are now in the same subnet and are valid.

As far as why Reads and GetTagList would work when using the /22 subnet from DHCP - I'd need to know more specifics on your network configuration, but this traffic is going to be unicast, and would require your router to be configured with multiple interfaces and route traffic on the same broadcast domain. So from your DHCP given address, your NIC sees that the destination IP is for 192.168.50.X, sends the packet to its default gateway (router), then router forwards the packet out the same interface from its subinterface configured at 192.168.50.X, and then to the PLC. This assumes that the PLC has the router's 192.168.50.X subinterface configured as its default gateway.

TheFern2 commented 4 years ago

What you're describing sounds like the devices configured at 192.168.50.X and 192.168.100.X are on the same LAN or VLAN as the 192.168.100.0/22 DHCP. EtherNet/IP drivers utilize broadcasts for discovery, and since you're on the same Broadcast domain, the devices respond using a layer 2 protocol and Linx receives those responses and populates the info into the tree.

This is simply conjecture, but I would imagine the reason that they aren't listed out in the results from discover.py is more due to the way Python handles networking, and not so much with the configuration of pylogix. Since the addresses are not in the same subnet (when configured with the /22 mask) Python may be discarding those response frames. However, when you open up the subnet mask to be wider, they are now in the same subnet and are valid.

As far as why Reads and GetTagList would work when using the /22 subnet from DHCP - I'd need to know more specifics on your network configuration, but this traffic is going to be unicast, and would require your router to be configured with multiple interfaces and route traffic on the same broadcast domain. So from your DHCP given address, your NIC sees that the destination IP is for 192.168.50.X, sends the packet to its default gateway (router), then router forwards the packet out the same interface from its subinterface configured at 192.168.50.X, and then to the PLC. This assumes that the PLC has the router's 192.168.50.X subinterface configured as its default gateway.

Yeah I agree it might have something to do with python socket lib, might be a way to fix it. I'd really like to know if the route add worked though.

AndrewStuhr commented 4 years ago

kodaman2: The PC with ip 192.168.100.132 has always been able to ping devices on .50. Is it still worth trying route add?

psalwasser: Do you know how to verify whether Python is discarding response frames from another subnet, and/or how to keep these frames?

Thanks for the all replies. I'll be looking into this more Monday.

AndrewStuhr commented 4 years ago

dmroeder: When you said Pylogix uses the same driver as RSLinx's Ethernet I/P, did that mean RSLinx uses UDP? I believe our router blocks UDP traffic, and since RSLinx was able to scan devices on .50, I would guess that RSLinx does not use UDP.

I'm wondering if the issue is Pylogix uses UDP and RSLinx uses TCP.

TheFern2 commented 4 years ago

@AndrewStuhr I would add the route for sure it takes one second, if it doesn't do anything just do route remove command. If you don't do the p parameter it will be automatically removed on next boot up too.

Btw if you download TCPView from the sysinternals suite you'd be able to see what rslinx is doing in regards to udp/tcp connections.

dmroeder commented 4 years ago

dmroeder: When you said Pylogix uses the same driver as RSLinx's Ethernet I/P, did that mean RSLinx uses UDP? I believe our router blocks UDP traffic, and since RSLinx was able to scan devices on .50, I would guess that RSLinx does not use UDP.

I'm wondering if the issue is Pylogix uses UDP and RSLinx uses TCP.

I'm not using anything from RSLinx, what I was attempting to say is that I'm using the same object as RSLinx (ListIdentity). Discover sends out a broadcast packet to all devices on the network, essentially asking "does anyone speak Ethernet I/P". All devices respond with some basic data about themselves. Only UDP is capable of doing this, not TCP.

If you had Wireshark running and were viewing packets when RSLinx and pylogix are both discovering devices, the packets would look the same. Other than I have a few bytes containing values that allow me to distinguish between the replies that are intended for pylogix to get from the ones that are responses to RSLinx.

BTW, all of your devices in the I/O tree talking unicast are utilizing UDP. Your network might block UDP packets coming from the outside, but not packets internal to the network.

kyle-github commented 4 years ago

There is sometimes a security restriction placed on full broadcast packets, 255.255.255.255, vs. network broadcast packets, 192.168.100.255/24. I don't know if that is always the case, but I seem to remember that on some systems, your program has to have admin/root/system privs to be able to send a full broadcast packet.

It is also possible that there is some sort of TTL involved.

dmroeder commented 4 years ago

There is sometimes a security restriction placed on full broadcast packets, 255.255.255.255, vs. network broadcast packets, 192.168.100.255/24. I don't know if that is always the case, but I seem to remember that on some systems, your program has to have admin/root/system privs to be able to send a full broadcast packet.

It is also possible that there is some sort of TTL involved.

Hmm, thanks for the info Kyle. I wonder if allowing the user to pass the broadcast address might be helpful in this situation. I'll have to figure out how to test this case...