Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.20)
project(OpenProfinet)
set(CMAKE_PROJECT_VERSION_MAJOR 0)
set(CMAKE_PROJECT_VERSION_MINOR 8)
set(CMAKE_PROJECT_VERSION_PATCH 1)
set(CMAKE_PROJECT_VERSION_MINOR 9)
set(CMAKE_PROJECT_VERSION_PATCH 2)

set(CMAKE_CXX_STANDARD 20)

Expand All @@ -20,6 +20,9 @@ set(CPACK_GENERATOR DEB)
set(CPACK_DEBIAN_PACKAGE_NAME OpenProfinet)
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Nareshkumar Rao <contact@nareshkumarrao.com>")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpcap0.8-dev")
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "OpenProfinet is a collection of tools for working with Profinet on Linux")
set(CPACK_DEBIAN_FILE_NAME OpenProfinet-${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}-${CMAKE_PROJECT_VERSION_PATCH}.deb)
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "
OpenProfinet is a collection of tools for working with Profinet on Linux
https://github.com/naresh97/OpenProfinet
")
set(CPACK_DEBIAN_FILE_NAME OpenProfinet-${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}.deb)
include(CPack)
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
A collection of tools for configuring Profinet devices on Linux based systems.
Uses libpcap library for creating Ethernet frames.

## Usage
## Usage: pntool

### pntool search
```
Search for Profinet devices on the network.
Usage: pntool search [OPTIONS]
Expand All @@ -16,6 +17,24 @@ Options:

```

### pntool configure
```
Configure Profinet devices on the network.
Usage: pntool configure [OPTIONS] device

Positionals:
device REQUIRED The current name of the device to configure

Options:
-h,--help Print this help message and exit
-t,--timeout INT Time to search for devices in milliseconds
-n,--name TEXT Set a new name for the device
-i,--ip TEXT New IP Address
-s,--subnet TEXT New Subnet Mask
-g,--gateway TEXT New Gateway Address

```

## Building

Uses CMake as build system.
Expand All @@ -29,4 +48,5 @@ make

## Licensing

This software is open-source and free to use as specified in the GPLv3 license. However, commercial use of this software is only allowed with explicit permission from the author.
This software is open-source and free to use as specified in the GPLv3 license for **non-commercial** use only.
Permission for commercial use require explicit permission of the author.
90 changes: 72 additions & 18 deletions src/ProfinetTool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,35 @@

#include <thread>
#include <iostream>
#include <cstring>
#include "ProfinetTool.h"

extern "C"{
extern "C" {
#include "pcapInterface.h"
}

std::string getDefaultInterface(){
#define LISTENING_THREAD_STARTUP_DELAY 500

std::string getDefaultInterface() {
char interface[256];
get_default_interface(interface);
return interface;
}

std::string MACToString(std::array<uint8_t, 6> mac){
std::string MACToString(std::array<uint8_t, 6> mac) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return macStr;
}

std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinetPacketArray){
std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinetPacketArray) {
profinet_device devices[PROFINET_DEVICE_LIST_SIZE];
int count;

std::vector<ProfinetDevice> devicesVector;
get_profinet_devices(&profinetPacketArray, devices, &count);
for(int i = 0; i < count; ++i){
for (int i = 0; i < count; ++i) {
auto device = devices[i];
char ipAddress[INET_ADDRSTRLEN];
char subnet[INET_ADDRSTRLEN];
Expand All @@ -41,11 +44,11 @@ std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinet
inet_ntop(AF_INET, &device.gateway, gateway, INET_ADDRSTRLEN);

ProfinetDevice newDevice{
.deviceName = device.stationName,
.deviceType = device.deviceType,
.ipAddress = ipAddress,
.subnetMask = subnet,
.gateway = gateway,
.deviceName = device.deviceName,
.deviceType = device.deviceType,
.ipAddress = ipAddress,
.subnetMask = subnet,
.gateway = gateway,
};
std::copy(std::begin(device.macAddress), std::end(device.macAddress), std::begin(newDevice.deviceMAC));

Expand All @@ -54,23 +57,74 @@ std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinet
return devicesVector;
}

void ProfinetTool::searchForDevices() {
profinet_packet_array profinetPacketArray{};
auto listeningThread = std::thread([this, &profinetPacketArray](){
std::thread ProfinetTool::listenForPackets() {
auto listeningThread = std::thread([this]() {
profinet_listen(interface.c_str(), &profinetPacketArray, searchTimeout);
});
std::this_thread::sleep_for(std::chrono::milliseconds(LISTENING_THREAD_STARTUP_DELAY));
return listeningThread;
}

std::vector<ProfinetDevice> ProfinetTool::searchForDevices(bool printFoundDevices) {
profinetPacketArray = {.packets = {}, .size = 0};

auto listeningThread = listenForPackets();

discovery_request(interface.c_str());

std::cout << "Searching..." << std::endl;
std::cout << "Searching for devices..." << std::endl;

listeningThread.join();

auto devices = getDevicesFromPackets(profinetPacketArray);
for(const auto &device : devices){
using namespace std;
cout << "Device Name: " << device.deviceName << " - IP: " << device.ipAddress << ", Subnet Mask: " << device.subnetMask
<< ", Gateway: " << device.gateway << ", Type: " << device.deviceType << endl;

if (printFoundDevices) {
for (const auto &device: devices) {
using namespace std;
cout << "Device Name: " << device.deviceName << " - IP: " << device.ipAddress << ", Subnet Mask: "
<< device.subnetMask
<< ", Gateway: " << device.gateway << ", Type: " << device.deviceType << endl;
}
}

return devices;
}

void ProfinetTool::configureDevices(const std::string &deviceName, const std::string &newName, const std::string &newIP,
const std::string &newSubnet, const std::string &newGateway) {
auto devices = searchForDevices(false);

ProfinetDevice device;
bool found = false;
for (auto const &loopDevice: devices) {
if (loopDevice.deviceName == deviceName) {
found = true;
device = loopDevice;
break;
}
}

if (!found) throw std::runtime_error("Device does not exist on network. Use 'search' to check the name.");

if (!newName.empty()) device.deviceName = newName;
if (!newIP.empty()) device.ipAddress = newIP;
if (!newSubnet.empty()) device.subnetMask = newSubnet;
if (!newGateway.empty()) device.gateway = newGateway;

profinet_device device_p{};
strcpy(device_p.deviceName, device.deviceName.c_str());
strcpy(device_p.deviceType, device.deviceType.c_str());
memcpy(device_p.macAddress, device.deviceMAC.data(), 6);
inet_pton(AF_INET, device.ipAddress.c_str(), &device_p.ipAddress);
inet_pton(AF_INET, device.subnetMask.c_str(), &device_p.subnetMask);
inet_pton(AF_INET, device.gateway.c_str(), &device_p.gateway);

auto success = set_device_configuration(interface.c_str(), &device_p);
if (success)
std::cout << "Device Configuration: Success!" << std::endl << std::endl;
else
throw std::runtime_error("Configuration Failure. Did not receive response from the device.");
searchForDevices(true);
}

ProfinetTool::ProfinetTool(const std::string &interface, int timeout) : interface(interface), searchTimeout(timeout) {}
Expand Down
16 changes: 13 additions & 3 deletions src/ProfinetTool.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
#include <string>
#include <vector>
#include <array>
#include <thread>
#include "profinetTypes.h"

struct ProfinetDevice{
struct ProfinetDevice {
std::string deviceName;
std::string deviceType;
std::string ipAddress;
Expand All @@ -21,14 +23,22 @@ struct ProfinetDevice{
class ProfinetTool {
public:
explicit ProfinetTool(int timeout = 5000);

ProfinetTool(const std::string &interface, int timeout);

void searchForDevices();
void setDeviceProperties();
// Commands
std::vector<ProfinetDevice> searchForDevices(bool printFoundDevices = true);

void configureDevices(const std::string &deviceName, const std::string &newName, const std::string &newIP,
const std::string &newSubnet, const std::string &newGateway);

private:
std::thread listenForPackets();

std::string interface;
int searchTimeout;

profinet_packet_array profinetPacketArray{};
};


Expand Down
37 changes: 31 additions & 6 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,45 @@
int main(int argc, char **argv) {

CLI::App app{"pntool is part of the OpenProfinet project. It is used to configure Profinet networks."};
app.require_subcommand();

CLI::App *search = app.add_subcommand("search", "Search for Profinet devices on the network.");
app.require_subcommand();
CLI::Option *interface = search->add_option("-i,--interface", "Interface to use");
int timeout = 2000;
int timeout = 1000;
search->add_option("-t,--timeout", timeout, "Time to search for devices in milliseconds");

CLI::App *configure = app.add_subcommand("configure", "Configure Profinet devices on the network.");
CLI::Option *device = configure->add_option("device", "The current name of the device to configure")
->required(true);
configure->add_option("-t,--timeout", timeout, "Time to search for devices in milliseconds");

std::string newName;
configure->add_option("-n,--name", newName, "Set a new name for the device");

std::string newIP;
configure->add_option("-i,--ip", newIP, "New IP Address");
std::string newSubnet;
configure->add_option("-s,--subnet", newSubnet, "New Subnet Mask");
std::string newGateway;
configure->add_option("-g,--gateway", newGateway, "New Gateway Address");

CLI11_PARSE(app, argc, argv);

if(*search){
ProfinetTool profinetTool(timeout);
if(!interface->empty()) profinetTool = ProfinetTool(interface->as<std::string>(), timeout);
profinetTool.searchForDevices();
try {
if (*search) {
ProfinetTool profinetTool(timeout);
if (!interface->empty()) profinetTool = ProfinetTool(interface->as<std::string>(), timeout);
profinetTool.searchForDevices();
} else if (*configure) {
ProfinetTool profinetTool(timeout);
profinetTool.configureDevices(device->as<std::string>(), newName, newIP, newSubnet,
newGateway);
}
} catch (const std::runtime_error &e) {
std::cerr << "Could not run command: " << e.what() << std::endl;
return EXIT_FAILURE;
}


return 0;
}
Loading