Files
ethercat/tool/CommandIp.cpp
2024-05-15 13:04:34 +02:00

246 lines
7.6 KiB
C++

/*****************************************************************************
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* vim: expandtab
*
****************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
#include <algorithm>
using namespace std;
#include "CommandIp.h"
#include "MasterDevice.h"
/****************************************************************************/
CommandIp::CommandIp():
Command("ip", "Set EoE IP parameters.")
{
}
/****************************************************************************/
string CommandIp::helpString(const string &binaryBaseName) const
{
stringstream str;
str << binaryBaseName << " " << getName() << " [OPTIONS] <ARGS>" << endl
<< endl
<< getBriefDescription() << endl
<< endl
<< "This command requires a single slave to be selected." << endl
<< endl
<< "IP parameters can be appended as argument pairs:" << endl
<< endl
<< " ip_address <IPv4>[/prefix] IP address (optionally with" << endl
<< " decimal subnet prefix)" << endl
<< " mac_address <MAC> Link-layer address (may contain"
<< endl
<< " colons or hyphens)" << endl
<< " default_gateway <IPv4> Default gateway" << endl
<< " dns_address <IPv4> DNS server address" << endl
<< " hostname <hostname> Host name (max. 32 byte)" << endl
<< endl
<< "IPv4 adresses can be given either in dot notation or as" << endl
<< "hostnames, which will be automatically resolved." << endl
<< endl
<< "Command-specific options:" << endl
<< " --alias -a <alias>" << endl
<< " --position -p <pos> Slave selection. See the help of" << endl
<< " the 'slaves' command." << endl
<< endl
<< numericInfo();
return str.str();
}
/****************************************************************************/
void CommandIp::execute(const StringVector &args)
{
if (args.size() <= 0) {
return;
}
if (args.size() % 2) {
stringstream err;
err << "'" << getName() << "' needs an even number of arguments!";
throwInvalidUsageException(err);
}
ec_ioctl_eoe_ip_t io = {};
for (unsigned int argIdx = 0; argIdx < args.size(); argIdx += 2) {
string arg = args[argIdx];
string val = args[argIdx + 1];
std::transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
if (arg == "link" or arg == "mac_address") {
parseMac(io.mac_address, val);
io.mac_address_included = 1;
}
else if (arg == "addr" or arg == "ip_address") {
parseIpv4Prefix(&io, val);
io.ip_address_included = 1;
}
else if (arg == "default" or arg == "default_gateway") {
resolveIpv4(&io.gateway, val);
io.gateway_included = 1;
}
else if (arg == "dns" or arg == "dns_adress") {
resolveIpv4(&io.dns, val);
io.dns_included = 1;
}
else if (arg == "name" or arg == "hostname") {
if (val.size() > EC_MAX_HOSTNAME_SIZE - 1) {
stringstream err;
err << "Name too long!";
throwInvalidUsageException(err);
}
unsigned int i;
for (i = 0; i < val.size(); i++) {
io.name[i] = val[i];
}
io.name[i] = 0;
io.name_included = 1;
}
else {
stringstream err;
err << "Unknown argument '" << args[argIdx] << "'!";
throwInvalidUsageException(err);
}
}
MasterDevice m(getSingleMasterIndex());
m.open(MasterDevice::ReadWrite);
SlaveList slaves = selectedSlaves(m);
if (slaves.size() != 1) {
throwSingleSlaveRequired(slaves.size());
}
io.slave_position = slaves.front().position;
// execute actual request
try {
m.setIpParam(&io);
} catch (MasterDeviceException &e) {
throw e;
}
}
/****************************************************************************/
void CommandIp::parseMac(unsigned char mac[EC_ETH_ALEN], const string &str)
{
unsigned int pos = 0;
for (unsigned int i = 0; i < EC_ETH_ALEN; i++) {
if (pos + 2 > str.size()) {
stringstream err;
err << "Incomplete MAC address!";
throwInvalidUsageException(err);
}
string byteStr = str.substr(pos, 2);
pos += 2;
stringstream s;
s << byteStr;
unsigned int byteValue;
s >> hex >> byteValue;
if (s.fail() || !s.eof() || byteValue > 0xff) {
stringstream err;
err << "Invalid MAC address!";
throwInvalidUsageException(err);
}
mac[i] = byteValue;
while (pos < str.size() && (str[pos] == ':' || str[pos] == '-')) {
pos++;
}
}
}
/****************************************************************************/
void CommandIp::parseIpv4Prefix(ec_ioctl_eoe_ip_t *io,
const string &str)
{
size_t pos = str.find('/');
string host;
io->subnet_mask_included = pos != string::npos;
if (pos == string::npos) { // no prefix found
host = str;
}
else {
host = str.substr(0, pos);
string prefixStr = str.substr(pos + 1, string::npos);
stringstream s;
s << prefixStr;
unsigned int prefix;
s >> prefix;
if (s.fail() || !s.eof() || prefix > 32) {
stringstream err;
err << "Invalid prefix '" << prefixStr << "'!";
throwInvalidUsageException(err);
}
uint32_t mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;
io->subnet_mask.s_addr = htonl(mask);
}
resolveIpv4(&io->ip_address, host);
}
/****************************************************************************/
void CommandIp::resolveIpv4(struct in_addr *dst, const string &str)
{
struct addrinfo hints = {};
struct addrinfo *res;
hints.ai_family = AF_INET; // only IPv4
int ret = getaddrinfo(str.c_str(), NULL, &hints, &res);
if (ret) {
stringstream err;
err << "Lookup of '" << str << "' failed: "
<< gai_strerror(ret) << endl;
throwCommandException(err.str());
}
if (!res) { // returned list is empty
stringstream err;
err << "Lookup of '" << str << "' failed." << endl;
throwCommandException(err.str());
}
const struct sockaddr_in *in_addr =
(const struct sockaddr_in *) res->ai_addr;
*dst = in_addr->sin_addr;
freeaddrinfo(res);
}
/****************************************************************************/