/***************************************************************************** * * $Id$ * * 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 * * --- * * The license mentioned above concerns the source code only. Using the * EtherCAT technology and brand is only permitted in compliance with the * industrial property and similar rights of Beckhoff Automation GmbH. * * vim: expandtab * ****************************************************************************/ #include #include #include #include #include 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] " << 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 << " addr [/prefix] IP address (optionally with" << endl << " decimal subnet prefix)" << endl << " link Link-layer address (may contain" << endl << " colons or hyphens)" << endl << " default Default gateway" << endl << " dns DNS server" << endl << " name 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 " << endl << " --position -p 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_slave_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") { parseMac(io.mac_address, val); io.mac_address_included = 1; } else if (arg == "addr") { parseIpv4Prefix(&io, val); io.ip_address_included = 1; } else if (arg == "default") { resolveIpv4(&io.gateway, val); io.gateway_included = 1; } else if (arg == "dns") { resolveIpv4(&io.dns, val); io.dns_included = 1; } else if (arg == "name") { 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_slave_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 = 0; for (unsigned int bit = 0; bit < prefix; bit++) { mask |= (1 << (31 - bit)); } io->subnet_mask = htonl(mask); } resolveIpv4(&io->ip_address, host); } /*****************************************************************************/ void CommandIp::resolveIpv4(uint32_t *addr, 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()); } sockaddr_in *sin = (sockaddr_in *) res->ai_addr; for (unsigned int i = 0; i < 4; i++) { ((unsigned char *) addr)[i] = ((unsigned char *) &sin->sin_addr.s_addr)[i]; } freeaddrinfo(res); } /****************************************************************************/