mirror of
https://gitlab.com/etherlab.org/ethercat.git
synced 2026-02-06 03:41:52 +08:00
246 lines
7.6 KiB
C++
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);
|
|
}
|
|
|
|
/****************************************************************************/
|