Open kenghuaku opened 1 year ago
Perhaps the problem might lie in the incorrect calculation of length. As shown in the picture below, the correct value should be 24.
That length 20 is probably right. Because length should not include command id and length self. Command Id (0x006F): 2 Bytes Length: 2 Bytes So, length be 20 subtracted 4 bytes from total data 24 bytes.
I pushed a new commit e5d42d51f78ddb74d58e7d8ab2a520d48ec76b55 It commit mean you can add another CommandItem before exchange SendRRData.
https://github.com/gisellevonbingen/Giselle.Net.EtherNetIP/blob/e5d42d51f78ddb74d58e7d8ab2a520d48ec76b55/Giselle.Net.EtherNetIP/ENIP/ENIPCodec.cs#L147-L149 Try upper code like to below and test.
public DataProcessor GetAttribute(Stream stream, AttributePath path) => this.ExchangeSendRRData(stream,
this.CIPCodec.HandleGetAttribute, // This is response callback
// Array of CommandItem for send
new CommandItemNull(), this.CIPCodec.CreateGetAttribute(path));
I'm not currently in a situation where I can test, sorry.
else.. can you send screenshot showing length when success?
Success! Additionally, the link to the program I used for packet comparison is as follows: https://github.com/xl7dev/ICSecurity/blob/master/ethernetip.py
And now continue debug,Forward Open is failed, error package:
the OK package:
The image provided above is incorrect; it's the one for successful return. The correct one (python successful transmission) is shown below.
Fixed an issue where ForwardOpen's SendRRData wasn't passing the Originator's UDP port number right now. 45eca2e321fb01bd2d9c5152dd2d00eb179377b2 Pull and retry please.
public ForwardOpenResult ForwardOpen(Stream stream, ForwardOpenOptions options) => this.ExchangeSendRRData(stream,
response => this.CIPCodec.HandleForwardOpen(response, options),
new CommandItemNull(), this.CIPCodec.CreateForwardOpen(options));
-> cannot add new CommandItemNull() after new commit, and the CommandItemNull is necessary.
Oops, i forgot it.
In this code, https://github.com/gisellevonbingen/Giselle.Net.EtherNetIP/blob/45eca2e321fb01bd2d9c5152dd2d00eb179377b2/Giselle.Net.EtherNetIP/CIP/CIPCodec.cs#L54-L62 Try edit like this.
public IEnumerable<CommandItem> CreateForwardOpen(ForwardOpenOptions options)
{
// Add this line
yield return new CommandItemNull();
var udRequest = new CommandItemUnconnectedDataRequest();
Same error, CS1660, cannot convert Lambda to CommandItem
'yield return new CommandItemNull();' is will instead of it. Remove 'new CommandItemNull(), ' in picture
Currently everything is OK, but in my case, ConfigData must be added to ForwardOpen. Below is the Python implementation code:
path = struct.pack(">I",0x34040000) + struct.pack("I",0) + \ struct.pack(">I",0x00002004) + struct.pack("B",0x24) + struct.pack("B",configinst) + \ struct.pack("B",0x2c) + struct.pack("B",outputinst) + struct.pack("B",0x2c) + \ struct.pack("B",inputinst) plen = int(len(path) / 2) if configData != None:
if len(configData) > 512:
return 1
path += struct.pack("BB", 0x80, int(len(configData)/2) )
path += configData
plen = int(len(path) / 2)
how should I correspondingly add it to project?
P.S. The Configuration Assembly ID should be set in this location of the CreateForwardOpen method: ..... var connectionPath = new EPath { EPathSegmentLogical.FromClassID(options.ClassID), EPathSegmentLogical.FromInstanceID(0x66) // Configuration Assembly ID is 0x66 }; ......
Is it work?????? AWESOME. I know now python code created what data, but I don't understand why it works. Can you take a screenshot that from wireshark?
My BALLUFF EtherNet/IP Scanner for IO-Link (referred to by BALLUFF as EtherNet/IP IO-LINK Master by EIP) needs to go through a configuration process. Only after this, the TO/OT UDP data will carry information, otherwise, its content is empty. However, other communications are currently functioning normally. If I first connect it to a PLC (which has an EDS that can be viewed), and then go through the PLC's configuration process, the data can be normally obtained on the C# program side. I plan to capture packets on the PLC equipment, but I currently don't have a port mirroring switch, so you'll need to wait for me for a bit.
I want to add the following configData to connectionPath. How can I do that? (How to modify EPathSegmentLogical.cs?)"
int Configuration_Length = 194; byte[] configData = new byte[Configuration_Length + 2]; // 2: first: 0x80 for Simple Data Segment, second: word length, 2 bytes = 1 words configData[0] = 0x80; configData[1] = (byte)(Configuration_Length / 2); configData[2] = 0xff; configData[3] = 0xff;
The InstanceID of the Assembly you use for ForwardOpen is 0x64, 0x65?
yes, just like following code:
// Reserved reqProcessor.WriteByte(0x30); reqProcessor.WriteByte(0x30); reqProcessor.WriteByte(0x30);
reqProcessor.WriteUInt(options.O_T_Assembly.RequestPacketRate);
reqProcessor.WriteUShort(options.O_T_Assembly.Flags);
reqProcessor.WriteUInt(options.T_O_Assembly.RequestPacketRate);
reqProcessor.WriteUShort(options.T_O_Assembly.Flags);
// Transport Type/Trigger
reqProcessor.WriteByte(0x01);
var connectionPath = new EPath
{
EPathSegmentLogical.FromClassID(options.ClassID),
EPathSegmentLogical.FromInstanceID(0x66) // kenghua: Configuration Assembly ID
};
if (options.O_T_Assembly.ConnectionType != ConnectionType.Null)
{
connectionPath.Add(EPathSegmentLogical.FromConnectionPointID(options.O_T_Assembly.InstanceID));
}
if (options.T_O_Assembly.ConnectionType != ConnectionType.Null)
{
connectionPath.Add(EPathSegmentLogical.FromConnectionPointID(options.T_O_Assembly.InstanceID));
}
And all setting and data will define in EDS that PLC will be automatically configured, so there is no need for manual setup like we do.
I extended EPathSegmentLogical.cs by adding SimpleDataSegment = 0x80, and handling the value as byte[] (I added byte[] ValueByteArray). I also modified the WriteValue method to write byte[] values. Everything went well, and I successfully configured my EIP Scanner through the Configuration Assembly. Although the specification mentioned this part, it was quite vague, and few devices use this for configuration (usually, Explicit Messaging is used for configuration). In any case, I'm extremely grateful to gisellevonbingen; you are truly amazing bro!
Already complete? Wow.. you genius.. You've figured out exactly where to change it.
I been had push a new commit 818c125fb6f0a7ebfeedfc2f59412a62db3b209c just right now. The code changes were tested on my device(ControlTechniques Drive), and I confirmed that compatibility was kept. You just need to add the code as below.
var connectionPath = new EPath
{
EPathSegmentLogical.FromClassID(options.ClassID),
EPathSegmentLogical.FromInstanceID(0x01)
};
if (options.O_T_Assembly.ConnectionType != ConnectionType.Null)
{
connectionPath.Add(EPathSegmentLogical.FromConnectionPointID(options.O_T_Assembly.InstanceID));
}
if (options.T_O_Assembly.ConnectionType != ConnectionType.Null)
{
connectionPath.Add(EPathSegmentLogical.FromConnectionPointID(options.T_O_Assembly.InstanceID));
}
// Add this line
connectionPath.Add(new EPathSegmentData(KnownEPathDataSegmentType.Simple, new byte[] { your config data }));
connectionPath.Write(reqProcessor);
"Simple Data Segment" in Connection Path type is 0x80
Yes it is, in byte. in this picture, upper 3 bits mean segment type. => 0x04 (0b100) and remaining 5 bits are data type. => 0x00 (0b00000) They can be merge to 0x80. (0b10000000)
Wait.. i saw a typo...
Now i think it should work. c72f7313453d28eec745b0c2d620aa6bc485e331
please fix bug: public void WriteValue(DataProcessor processor) method: processor.WriteByte((byte)payload.Length); ----> processor.WriteByte((byte)(payload.Length / 2)); // kenghua: the first byte represents the data length, calculated in words. 2bytes = 1word.
AND... Do you have any suggestions for establishing connections with multiple machines? (All connections cannot bind same local port 2222)
How length does pass when length is odd? ceiling? flooring? I decided to 'ceil' for now. 7a835554eac55bf0e672fe36f575c6a759b4785c
I have plan for port change. I'll test it a bit more and let you know.
Can change PC port with T_O_UDPPort
319a02c17602ccf0dec9dd030e6ffc2089d13938
https://github.com/gisellevonbingen/Giselle.Net.EtherNetIP/blob/34acf84e0376dfc0849f9ac66c6a51def6ab7688/Giselle.Net.EtherNetIP.Test/ImplicitMessaging.cs#L90-L110
When change O_T_UDPPort to 1234
var openOptions = new ForwardOpenOptions();
openOptions.T_O_UDPPort = 1234; // Support alternate port, Default is 2222
A few questions are listed below for discussion, and I suggest you can commit back to the main branch, thank you:
This is the correct way, payload.Length don't need add 1, and the specifications require providing the length in Word, so there should not be definitions with an odd number of byte lengths: public void WriteValue(DataProcessor processor) { var payload = this.Payload; processor.WriteByte((byte)(payload.Length / 2)); processor.WriteBytes(payload); // Pad to words processor.WriteBytes(new byte[payload.Length % 2]); }
In ENIPCodec.cs, I suggest adding CommandItemNull according to our previous discussion, because when I capture PLC communication packets, they do the same. Could you test the compatibility of doing this on your equipment? If I don't add it on my side, the device will directly indicate a format error and refuse to OpenForward: public DataProcessor GetAttribute(Stream stream, AttributePath path) => this.ExchangeSendRRData(stream, this.CIPCodec.HandleGetAttribute, new CommandItemNull(), this.CIPCodec.CreateGetAttribute(path));
public byte SetAttribute(Stream stream, AttributePath path, byte[] values) => this.ExchangeSendRRData(stream,
this.CIPCodec.HandleSetAttribute,
new CommandItemNull(), this.CIPCodec.CreateSetAttribute(path, values));
public ForwardOpenResult ForwardOpen(Stream stream, ForwardOpenOptions options) => this.ExchangeSendRRData(stream,
response => this.CIPCodec.HandleForwardOpen(response, options),
this.CIPCodec.CreateForwardOpen(options));
public ForwardCloseResult ForwardClose(Stream stream, ForwardCloseOptions options) => this.ExchangeSendRRData(stream,
response => this.CIPCodec.HandleForwardClose(response, options),
new CommandItemNull(), this.CIPCodec.CreateForwardClose(options));
The issue with IPV6 exists on .net core, and I have already upgraded to use .net core. Along the line-map of Microsoft's .Net, I also suggest you do the same. I noticed that you have specifically implemented IPV4 enforcement for communication, but there are still some places that have been missed, leading to the capture of IPV6 internal IP addresses. The following areas need to be modified (I apologize, I kind of forgot what changes I've made), just like:
options.LocalAddress = ((IPEndPoint)this.TcpClient.Client.LocalEndPoint).Address; -> options.LocalAddress = ((IPEndPoint)this.TcpClient.Client.LocalEndPoint).Address.MapToIPv4();
search all "TcpClient(" and "UDPClient{",then add: new TcpClient(AddressFamily.InterNetwork) and new UdpClient(AddressFamily.InterNetwork)
This is to prevent users from accidentally entering odd-length arrays.
If sure to simple data's data type is word, It might be better to declare it as ushort[]
instead of byte[]
.
OK i will add it if it works on my test.
my device doesn't support IPv6, so maybe i can't test it. umm.. i will do that for now.
For users, byte[] will be more convenient, so I hold off for now.
Looks good work on my device, It added from 940f8dd9337d6297d1c98ea5f9ced51ee5f6e6af
I think can use IPv6 at PC internally. So... added map LocalEndPoint to IPv4 just before IPAddress be passed to device in here. 8c4232ca961839ce4e59ae306456f30e91c97a51 https://github.com/gisellevonbingen/Giselle.Net.EtherNetIP/blob/940f8dd9337d6297d1c98ea5f9ced51ee5f6e6af/Giselle.Net.EtherNetIP/CIP/CIPCodec.cs#L118-L126
In my case, sending get attribute single will get will get "incorrect data" RR, I compared this with other Python projects, and the packets sent out from them were successful. The differences between the two are shown in the two pictures below. In this case, there is one less CommandItemNull in the CommandItem. Could you guide me on how to add and test this?