We're using DPDK (version 20.08 on ubuntu 20.04, c++ application) to receive UDP packets with a high throughput (>2 Mpps). We use a Mellanox ConnectX-5 NIC (and a Mellanox ConnectX-3 in an older system, would be great if the solution worked there aswell).
Contrary, since we only need to send a few configuration messages, we send messages through the default network stack. This way, we can use lots of readily available tools to send configuration messages; however, since all the received data is consumed by DPDK, these tools do not get back any messages.
The most prominent issue arises with ARP negotiation: the host tries to resolve addresses, the clients also do respond properly, however, these responses are all consumed by DPDK such that the host cannot resolve the addresses and refuses to send the actual UDP packets.
Our idea would be to filter out the high throughput packets on our application and somehow "forward" everything else (e.g. ARP responses) to the default network stack. Does DPDK have a built-in solution for that? I unfortunatelly coulnd't find anything in the examples.
I've recently heard about the packet function which allows to inject packets into SOCK_DGRAM sockets which may be a possible solution. I also couldn't find a sample implementation for our use-case, though. Any help is greatly appreciated.
Theoretically, if the NIC in question supports the embedded switch feature, it should be possible to intercept the packets of interest in the hardware and redirect them to a virtual function (VF) associated with the physical function (PF), with the PF itself receiving everything else.
The user configures SR-IOV feature on the NIC / host as well as virtualisation support;
For a given NIC PF, the user adds a VF and binds it to the corresponding Linux driver;
The DPDK application is run with the PF ethdev and a representor ethdev for the VF;
To handle the packets in question, the application adds the corresponding flow rules.
The PF (ethdev 0) and the VF representor (ethdev 1) have to be explicitly specified by the corresponding EAL argument in the application: -a [pci:dbdf],representor=vf0.
As for the flow rules, there should be a pair of such.
The first rule's components are as follows:
Attribute transfer (demands that matching packets be handled in the embedded switch);
Pattern item REPRESENTED_PORT with port_id = 0 (instructs the NIC to intercept packets coming to the embedded switch from the network port represented by the PF ethdev);
Pattern items matching on network headers (these provide narrower match criteria);
Action REPRESENTED_PORT with port_id = 1 (redirects packets to the VF).
In the second rule, item REPRESENTED_PORT has port_id = 1, and action REPRESENTED_PORT has port_id = 0 (that is, this rule is inverse). Everything else should remain the same.
It is important to note that some drivers do not support item REPRESENTED_PORT at the moment. Instead, they expect that the rules be added via the corresponding ethdevs. This way, for the provided example: the first rule goes to ethdev 0, the second one goes to ethdev 1.
As per the OP update, the adapter in question might indeed support the embedded switch feature. However, as noted above, item REPRESENTED_PORT might not be supported. The rules should be inserted via specific ethdevs. Also, one more attribute, ingress, might need to be specified.
In order to check whether this scheme works, one should be able to deploy a VF (as described above) and run testpmd with the aforementioned EAL argument. In the command line of the application, the two flow rules can be tested as follows:
flow create 0 ingress transfer pattern eth type is 0x0806 / end actions represented_port ethdev_port_id 1 / end
flow create 1 ingress transfer pattern eth type is 0x0806 / end actions represented_port ethdev_port_id 0 / end
Once done, that should pass ARP packets to the VF (thus, to the network interface) in question. The rest of packets should be seen by testpmd in active forwarding mode (start command).
NOTE: it is recommended to switch to the most recent DPDK release.
For the current use case, the best option is to make use of DPDK TAP PMD (which is part of LINUX DPDK). You can use Software or Hardware to filter the specific packets then sent it desired TAP interface.
A simple example to demonstrate the same would be making use DPDK skeleton example.
build the DPDK example via cd [root folder]/example/skeleton; make static
pass the desired Physical DPDK PMD NIC using DPDK eal options ./build/basicfwd -l 1 -w [pcie id of DPDK NIC] --vdev=net_tap0;iface=dpdkTap
In second terminal execute ifconfig dpdkTap 0.0.0.0 promisc up
Use tpcudmp to capture Ingress and Egress packets using tcpdump -eni dpdkTap -Q in and tcpdump -enu dpdkTap -Q out respectively.
Note: you can configure ip address, setup TC on dpdkTap. Also you can run your custom socket programs too. You do not need to invest time on TLDP, ANS, VPP as per your requirement you just need an mechanism to inject and receive packet from Kernel network stack.
Related
I would like to use Beckhoff Twin CAT 3 TCP Modbus module to make registers in a PC which is running as a PLC readable via Modbus.
I have downloaded the function Modbus TCP from the Backhoff website. I have followed the example in the Manual TF6250 TwinCAT 3 | Modbus TCP page 55. When I try to read the register at address 0x3000 with a modbus client I get an invalid address error.
The code looks as follows:
PROGRAM MAIN
VAR
ipAddr : STRING(15) := '';
nValue AT%MB0 : ST_EM_Ausgangsdaten_Float;
fbWriteRegs : FB_MBWriteRegs;
bWriteRegs : BOOL;
END_VAR
IF NOT bWriteRegs THEN
nValue.BlindleistungL1 := nValue.BlindleistungL1+1;
nValue.BlindleistungL2 := nValue.BlindleistungL2+1;
nValue.BlindleistungL3 := nValue.BlindleistungL3+1;
bWriteRegs :=TRUE;
fbWriteRegs.sIPAddr :=ipAddr;
fbWriteRegs.nQuantity := 1;
fbWriteRegs.nMBAddr := 16#3000;
fbWriteRegs.cbLength := SIZEOF(nValue);
fbWriteRegs.pSrcAddr := ADR(nValue);
fbWriteRegs.tTimeout := T#5S;
fbWriteRegs(bExecute:=TRUE);
ELSE
IF NOT fbWriteRegs.bBUSY THEN
bWriteRegs :=FALSE;
END_IF
fbWriteRegs(bExecute:=FALSE);
END_IF
Could someone point me to the direction of how to read a variable in a PLC via Modbus.
If I understand your question correctly you are wanting run a Modbus TCP Server (and from your comments it sounds like you have already got something running, but you might not understand exactly why).
I am sure that you know this, but Modbus TCP works by Clients issuing Modbus commands to Read/Write data to/from a Modbus Server and the server responds with the data (or success). The TF6250 communication module allows you to do this in a few ways.
The first issue you have is that the sample code on page 55 you implemented is for the "FB_MBWriteRegs" function. This is a function where your program is acting as a Modbus client (and not a server). It is trying to connect to a remote server and write data to the Modbus address on that server. The description in the manual probably isn't the best and I can see how it may be misleading.
In your case (as it is in the sample code) the STRING ipAddr is empty. I wouldn't be surprised if your fbWriteRegs is reporting an error. You could check this by inspecting the value of the fbWriteRegs.bError and fbWriteRegs.nErrId tags.
For this code to work you would need to connect to an existing Modbus TCP Server and populate the correct IP address.
Additionally, I don't know what data type "ST_EM_Ausgangsdaten_Float" is, but given that this function is for writing to output registers, I wouldn't be surprised if there were issues there as well.
In any case, this isn't what you are wanting to do. I think you will find that if you remove/delete this code and leave your variables mapped as globals it will still 'work'.
What you are probably interested in, is section 4.2 and 4.3.
TF6250 installs a Windows Application that acts as a Modbus TCP server. This server acts as a Modbus to ADS converter which maps values from Modus registers to PLC memory areas via ADS.
You can access the configuration of the Modbus TCP server and the mapping from the TwinCAT Modbus TCP Configuration Tool. For windows this is usually located in the "C:\TwinCAT3\Functions\TF6250-Modbus-TCP" directory. (For Twicat/BSD it is a different procedure all together).
The config app looks like this;
If you click "Get Configuration" - wait a while until it loads, and then "Export Configuration" you can save the mapping/config in a XML file.
The Default mapping is shown on page 19 in section 4.3, which is how I suspect yours is currently working.
If you want to map directly to memory areas rather than via global you will need to know your IndexGroup and Index Offset available here and here. Note: I understand mapping this way improves performance for larger amounts of data but I haven't tested it.
You can manipulate the XML file for the mapping you require. However if you are able to choose whatever memory area you like, I would leave the default configuration for what you want to do and delete the rest of the config, then I would map my data to the appropriate TwinCAT memory area, but that is entirely up to you.
After you have modified your XML file, you can use the Config tool to "Import Configuration" select your modified XML file, and then "Set Configuration" to update the mapping.
You should then be able to use a Modbus Client to connect to your modbus server and know EXACTLY what data is being written to what Modbus address and thus memory area (%M, %Q, %I etc...)
Good luck!
I need to strip the rport parameter from the Via field of SIP messages being generated by Yate (for compatibility with a broken peer). Can I use the scripting capabilities of Yate to do this? How do I intercept and modify outgoing SIP messages?
I discovered, by reading the code, that Yate already supports this feature. In accfile.conf, in the section defining the sip server to register to, place the following line:
xsip_flags=1
This prevents the rport parameter from being placed in SIP messages. This may break routing if you are behind a NAT, so beware.
An example config would therefore be:
[sip_service]
enabled=yes
protocol=sip
description=sip_service
username=user
domain=somewhere.com
authname=auth
password=secret
server=somewhere.com
xsip_flags=1
I want to only capture the traffic sent or destined to my local machine (no promiscuous mode). Nevertheless, broadcast traffic should also be captured.
So, the question is how to open the adapter? Which flags should be used? There is no specific flag for this kind of capture. I only found the following flags:
#define PCAP_OPENFLAG_PROMISCUOUS 1
// Defines if the adapter has to go in promiscuous mode.
#define PCAP_OPENFLAG_DATATX_UDP 2
// Defines if the data trasfer (in case of a remote capture) has to be done with UDP protocol.
#define PCAP_OPENFLAG_NOCAPTURE_RPCAP 4
// Defines if the remote probe will capture its own generated traffic.
#define PCAP_OPENFLAG_NOCAPTURE_LOCAL 8
// Defines if the local adapter will capture its own generated traffic.
#define PCAP_OPENFLAG_MAX_RESPONSIVENESS 16
// This flag configures the adapter for maximum responsiveness.
So, should I open the adapter in promiscuous mode and set an appropriate filter? Or is there a better possibility to achieve this goal (better in terms of less processing by the WinPCAP capture driver)?
Thanks for clarification!
jonas
I want to only capture the traffic sent or destined to my local machine (no promiscuous mode).
Then don't turn promiscuous mode on.
Nevertheless, broadcast traffic should also be captured.
Broadcast traffic will always be captured (unless you specify a filter, such as !broadcast, that explicitly filters it out).
I'm writing a SIP stack, and I need to insert an ip address in the message. This address needs to be the one used for sending the message. I know the destination IP and need to determine the NIC (its address) that will be used to send the message....
To expand a bit on Remy Lebeau's comment, GetBestInterfaceEx() is your best bet, if you're on Windows XP or newer. That will work for both IPv4 and IPv6 addresses.
GetBestInterface/GetBestInterfaceEx return the index (call it IDX) of the most appropriate interface to use to contact some address.
Then you can map that index into a local IP address by getting your interface<->IP address mapping using GetIpAddrTable or GetAdaptersAddresses if you're dual-stacking (supporting both IPv6 and IPv4).
Iterate over that table and find the interface with the dwIndex (or IfIndex, in the case of GetAdaptersAddresses) matching IDX.
It's usually best to allow the IP address your SIP stack will operate on to be set as an adjustable configuration option. It means the user will need to set a configuration option but at least your stack will know the IP address it's operating on.
If that's not feasible then an approach you could use is to send out the SIP request on all IP addresses using a dummy value in the Via header such as 0.0.0.0 and set the interface you get a response back on as the default one. This approach alos as the advantage that the SIP response will tell you the public IP address the request was received from which can be useful if your SIP stack is behind a NAT.
Over TCP, I think you can get the address of the local side of the socket after connect(). I don't know if the same is true for UDP (I suspect not), but it might be worth a try.
The socket will allow you to Bind to a local endpoint before calling connect (both UDP and TCP).
That is all ok if you know the port. However, if you want the port to be ephemeral (e.g. some random port number) then you must come up with your own algorithm to do so and robust code to handle the cases where the port is exclusivly taken by another application.
I am writing a UDP test client/server and i want to get it through firewall. Supposedly all i need to do is have both sides send to the correct IP and server. Getting an IP is not a problem but how do i have the client pick a random free port and report it to the user? I eventually would want it to connect to a matchmaker server but right now i need a simple working prototype and i would like to cout the port number so my friend/tester can send me the # via IM so we can test.
How do i get the port number?
sorry for the long desc. I notice people tell me not to do what i am asking when i dont give a desc :(
To use the highly technical term, this is actually a pretty icky problem or even a pair of icky problems. Depending on the configuration of the firewall, it will usually allow responses from another endpoint on the IP endpoint as the request came from. So... if you friend receives the UDP datagram using something like the recvfrom() system call, the address parameter will receive the IP endpoint information to respond to. So the other end should be able to respond with a sendto() using the same addressing information. Something like:
/* initiator */
struct sockaddr_in hisaddr;
memset(&hisaddr, 0, sizeof(hisaddr));
hisaddr.sin_addr.s_addr = htonl(target_ip);
hisaddr.sin_port = htons(target_port);
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&hisaddr, sizeof(hisaddr));
/* receiver */
struct sockaddr_in peeraddr;
socklen_t peer_sz = sizeof(peeraddr);
recvfrom(sd, buf_ptr, buf_sz, 0, (struct sockaddr*)&peeraddr, &peer_sz);
/* build response */
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&peeraddr, peer_sz);
The peeraddr on the other side will be your external address or, more correctly, the IP address of your firewall and the port number that it chose to use. The port number that you specify in your code may be completely different than the port that your friend would have to send data to. Ultimately, it might not matter what port you choose to use since the firewall might be sending and receiving on an entirely different port - this is what Network Address Translation is all about. I would recommend reading RFC3235 for some tips on how to overcome that hurdle.
The best approach IMHO is to:
Let the OS choose a port by either calling bind() with a zero port number or skipping the bind altogether
Having the client receive the address information from the socket layer (e.g., the fifth and sixth arguments to recvfrom())
The client sends response to the endpoint retrieved in the previous step
Tweak the firewall configurations until the previous steps work
Of course, all of the magic is in the last step. If you can disable NAT or ensure that the firewall is never going to switch ports, then nailing down a port number and bind-ing to it will work as well. You might want to take a look at %WINDIR%\system32\drivers\etc\services (or /etc/services depending on your OS inclination) to get an idea of what port numbers are reserved or generally in use.
bind() the socket before you send your data. Specify port 0 to bind(), and the OS will pick an unused port for you. You can then use getsockname() to find out what port wsa chosen.
Generally speaking - you - as the developer - choose the port. You can set your application to read the port from a config file or user input - but no magic firewall is going to tell you what port to use...
If I'm understanding your question correctly, I'm not sure there's a way to do what you want programatically (and even if there is, I don't think it's the right approach). I think you need to find a port that isn't in use on the server machine (and perhaps a different or the same port on the client machine, if communication is bi-directional) AND that port must be able to pass through your firewall. I assume since you say "getting an IP is not a problem", you've already configured your firewall to forward some or all ports to a specific computer inside the firewall? If so, the port you seek is one of the ones you forwarded. You can just pick an arbitrary one, as long as no other service is running on that port. Ports below 1024 are reserved, so you probably want to pick a higher number than that. You can use a simple portscanning tool such as nmap to see which services are running on your computer on which ports and pick a different one. Note that nmap can be fooled by firewalls and various bind rules when sockets are created.
I think you're better off picking a fixed port rather than relying on the random port number chosen by the O/S.
If you use a random port you'd have to change your firewall settings each and every time you run the program.
If you're using WINSOCK check this link:
http://msdn.microsoft.com/en-us/library/aa280717(VS.60).aspx
Basically you have 2 choices set the port to 0 and let the system assign you one or chose a random one try to open the socket if it doesn't work try another (be sure to steer clear of reserved ports)