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