|
@@ -211,7 +211,8 @@ TEST_F(IfaceMgrTest, getIface) {
|
|
|
delete ifacemgr;
|
|
|
}
|
|
|
|
|
|
-TEST_F(IfaceMgrTest, detectIfaces) {
|
|
|
+#if !defined(OS_LINUX)
|
|
|
+TEST_F(IfaceMgrTest, detectIfaces_stub) {
|
|
|
|
|
|
// test detects that interfaces can be detected
|
|
|
// there is no code for that now, but interfaces are
|
|
@@ -240,6 +241,7 @@ TEST_F(IfaceMgrTest, detectIfaces) {
|
|
|
|
|
|
delete ifacemgr;
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
// TODO: disabled due to other naming on various systems
|
|
|
// (lo in Linux, lo0 in BSD systems)
|
|
@@ -364,4 +366,266 @@ TEST_F(IfaceMgrTest, DISABLED_sendReceive) {
|
|
|
delete ifacemgr;
|
|
|
}
|
|
|
|
|
|
+/// @brief parses text representation of MAC address
|
|
|
+///
|
|
|
+/// This function parses text representation of a MAC address and stores
|
|
|
+/// it in binary format. Text format is expecte to be separate with
|
|
|
+/// semicolons, e.g. f4:6d:04:96:58:f2
|
|
|
+///
|
|
|
+/// TODO: IfaceMgr::Iface::mac_ uses uint8_t* type, should be vector<uint8_t>
|
|
|
+///
|
|
|
+/// @param textMac string with MAC address to parse
|
|
|
+/// @param mac pointer to output buffer
|
|
|
+/// @param macLen length of output buffer
|
|
|
+///
|
|
|
+/// @return number of bytes filled in output buffer
|
|
|
+size_t parse_mac(const std::string& textMac, uint8_t* mac, size_t macLen) {
|
|
|
+ stringstream tmp(textMac);
|
|
|
+ tmp.flags(ios::hex);
|
|
|
+ int i = 0;
|
|
|
+ uint8_t octet = 0; // output octet
|
|
|
+ uint8_t byte; // parsed charater from text representation
|
|
|
+ while (!tmp.eof()) {
|
|
|
+
|
|
|
+ tmp >> byte; // hex value
|
|
|
+ if (byte == ':') {
|
|
|
+ mac[i++] = octet;
|
|
|
+
|
|
|
+ if (i == macLen) {
|
|
|
+ // parsing aborted. We hit output buffer size
|
|
|
+ return(i);
|
|
|
+ }
|
|
|
+ octet = 0;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (isalpha(byte)) {
|
|
|
+ byte = toupper(byte) - 'A' + 10;
|
|
|
+ } else if (isdigit(byte)) {
|
|
|
+ byte -= '0';
|
|
|
+ } else {
|
|
|
+ // parse error. Let's return what we were able to parse so far
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ octet <<= 4;
|
|
|
+ octet += byte;
|
|
|
+ }
|
|
|
+ mac[i++] = octet;
|
|
|
+
|
|
|
+ return (i);
|
|
|
+}
|
|
|
+
|
|
|
+#if defined(OS_LINUX)
|
|
|
+
|
|
|
+/// @brief Parses 'ifconfig -a' output and creates list of interfaces
|
|
|
+///
|
|
|
+/// This method tries to parse ifconfig output. Note that there are some
|
|
|
+/// oddities in recent versions of ifconfig, like putting extra spaces
|
|
|
+/// after MAC address, inconsistent naming and spacing between inet and inet6.
|
|
|
+/// This is an attempt to find a balance between tight parsing of every piece
|
|
|
+/// of text that ifconfig prints and robustness to handle slight differences
|
|
|
+/// in ifconfig output.
|
|
|
+///
|
|
|
+/// @param textFile name of a text file that holds output of ifconfig -a
|
|
|
+/// @param ifaces empty list of interfaces to be filled
|
|
|
+void parse_ifconfig(const std::string textFile, IfaceMgr::IfaceLst& ifaces) {
|
|
|
+ fstream f(textFile.c_str());
|
|
|
+
|
|
|
+ bool first_line = true;
|
|
|
+ IfaceMgr::IfaceLst::iterator iface;
|
|
|
+ while (!f.eof()) {
|
|
|
+ string line;
|
|
|
+ getline(f, line);
|
|
|
+
|
|
|
+ // interfaces are separated by empty line
|
|
|
+ if (line.length() == 0) {
|
|
|
+ first_line = true;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // uncomment this for ifconfig output debug
|
|
|
+ // cout << "line[" << line << "]" << endl;
|
|
|
+
|
|
|
+ // this is first line of a new interface
|
|
|
+ if (first_line) {
|
|
|
+ first_line = false;
|
|
|
+
|
|
|
+ size_t offset;
|
|
|
+ offset = line.find_first_of(" ");
|
|
|
+ if (offset == string::npos) {
|
|
|
+ isc_throw(BadValue, "Malformed output of ifconfig");
|
|
|
+ }
|
|
|
+ string name = line.substr(0, offset);
|
|
|
+
|
|
|
+ // sadly, ifconfig does not return ifindex
|
|
|
+ ifaces.push_back(IfaceMgr::Iface(name, 0));
|
|
|
+ iface = ifaces.end();
|
|
|
+ --iface; // points to the last element
|
|
|
+
|
|
|
+ offset = line.find(string("HWaddr"));
|
|
|
+
|
|
|
+ string mac = "";
|
|
|
+ if (offset != string::npos) { // some interfaces don't have MAC (e.g. lo)
|
|
|
+ offset += 7;
|
|
|
+ mac = line.substr(offset, string::npos);
|
|
|
+ mac = mac.substr(0, mac.find_first_of(" "));
|
|
|
+
|
|
|
+ iface->mac_len_ = parse_mac(mac, iface->mac_, IfaceMgr::MAX_MAC_LEN);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (line.find("inet6") != string::npos) {
|
|
|
+ // IPv6 address
|
|
|
+ string addr = line.substr(line.find("inet6")+12, string::npos);
|
|
|
+ addr = addr.substr(0, addr.find("/"));
|
|
|
+ IOAddress a(addr);
|
|
|
+ iface->addrs_.push_back(a);
|
|
|
+ } else if(line.find("inet") != string::npos) {
|
|
|
+ // IPv4 address
|
|
|
+ string addr = line.substr(line.find("inet")+10, string::npos);
|
|
|
+ addr = addr.substr(0, addr.find_first_of(" "));
|
|
|
+ IOAddress a(addr);
|
|
|
+ iface->addrs_.push_back(a);
|
|
|
+ } else if(line.find("Metric")) {
|
|
|
+ // flags
|
|
|
+ if (line.find("UP") != string::npos) {
|
|
|
+ iface->flag_up_ = true;
|
|
|
+ }
|
|
|
+ if (line.find("LOOPBACK") != string::npos) {
|
|
|
+ iface->flag_loopback_ = true;
|
|
|
+ }
|
|
|
+ if (line.find("RUNNING") != string::npos) {
|
|
|
+ iface->flag_running_ = true;
|
|
|
+ }
|
|
|
+ if (line.find("BROADCAST") != string::npos) {
|
|
|
+ iface->flag_broadcast_ = true;
|
|
|
+ }
|
|
|
+ if (line.find("MULTICAST") != string::npos) {
|
|
|
+ iface->flag_multicast_ = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// This test compares implemented detection routines to output of "ifconfig -a" command.
|
|
|
+// It is far from perfect, but it is able to verify that interface names, flags,
|
|
|
+// MAC address, IPv4 and IPv6 addresses are detected properly. Interface list completeness
|
|
|
+// (check that each interface is reported, i.e. no missing or extra interfaces) and
|
|
|
+// address completeness is verified.
|
|
|
+//
|
|
|
+// Things that are not tested:
|
|
|
+// - ifindex (ifconfig does not print it out)
|
|
|
+// - address scopes and lifetimes (we don't need it, so it is not implemented in IfaceMgr)
|
|
|
+TEST_F(IfaceMgrTest, detectIfaces_linux) {
|
|
|
+
|
|
|
+ NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
|
|
|
+ IfaceMgr::IfaceLst& detectedIfaces = ifacemgr->getIfacesLst();
|
|
|
+
|
|
|
+ const std::string textFile = "ifconfig.txt";
|
|
|
+
|
|
|
+ unlink(textFile.c_str());
|
|
|
+ int result = system( ("/sbin/ifconfig -a > " + textFile).c_str());
|
|
|
+
|
|
|
+ ASSERT_EQ(0, result);
|
|
|
+
|
|
|
+ // list of interfaces parsed from ifconfig
|
|
|
+ IfaceMgr::IfaceLst parsedIfaces;
|
|
|
+
|
|
|
+ EXPECT_NO_THROW(
|
|
|
+ parse_ifconfig(textFile, parsedIfaces);
|
|
|
+ );
|
|
|
+ unlink(textFile.c_str());
|
|
|
+
|
|
|
+ cout << "------Parsed interfaces---" << endl;
|
|
|
+ for (IfaceMgr::IfaceLst::iterator i = parsedIfaces.begin();
|
|
|
+ i != parsedIfaces.end(); ++i) {
|
|
|
+ cout << i->name_ << ": ifindex=" << i->ifindex_ << ", mac=" << i->getPlainMac();
|
|
|
+ cout << ", flags:";
|
|
|
+ if (i->flag_up_) {
|
|
|
+ cout << " UP";
|
|
|
+ }
|
|
|
+ if (i->flag_running_) {
|
|
|
+ cout << " RUNNING";
|
|
|
+ }
|
|
|
+ if (i->flag_multicast_) {
|
|
|
+ cout << " MULTICAST";
|
|
|
+ }
|
|
|
+ if (i->flag_broadcast_) {
|
|
|
+ cout << " BROADCAST";
|
|
|
+ }
|
|
|
+ cout << ", addrs:";
|
|
|
+ for (IfaceMgr::Addr6Lst::iterator a= i->addrs_.begin();
|
|
|
+ a != i->addrs_.end(); ++a) {
|
|
|
+ cout << a->toText() << " ";
|
|
|
+ }
|
|
|
+ cout << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ok, now we have 2 lists of interfaces. Need to compare them
|
|
|
+ ASSERT_EQ(detectedIfaces.size(), parsedIfaces.size());
|
|
|
+
|
|
|
+ // TODO: This could could probably be written simple with find()
|
|
|
+ for (IfaceMgr::IfaceLst::iterator detected = detectedIfaces.begin();
|
|
|
+ detected != detectedIfaces.end(); ++detected) {
|
|
|
+ // let's find out if this interface is
|
|
|
+
|
|
|
+ bool found = false;
|
|
|
+ for (IfaceMgr::IfaceLst::iterator i = parsedIfaces.begin();
|
|
|
+ i != parsedIfaces.end(); ++i) {
|
|
|
+ if (detected->name_ != i->name_) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ found = true;
|
|
|
+
|
|
|
+ cout << "Checking interface " << detected->name_ << endl;
|
|
|
+
|
|
|
+ // start with checking flags
|
|
|
+ EXPECT_EQ(detected->flag_loopback_, i->flag_loopback_);
|
|
|
+ EXPECT_EQ(detected->flag_up_, i->flag_up_);
|
|
|
+ EXPECT_EQ(detected->flag_running_, i->flag_running_);
|
|
|
+ EXPECT_EQ(detected->flag_multicast_, i->flag_multicast_);
|
|
|
+ EXPECT_EQ(detected->flag_broadcast_, i->flag_broadcast_);
|
|
|
+
|
|
|
+ // skip MAC comparison for loopback as netlink returns MAC
|
|
|
+ // 00:00:00:00:00:00 for lo
|
|
|
+ if (!detected->flag_loopback_) {
|
|
|
+ ASSERT_EQ(detected->mac_len_, i->mac_len_);
|
|
|
+ EXPECT_EQ(0, memcmp(detected->mac_, i->mac_, i->mac_len_));
|
|
|
+ }
|
|
|
+
|
|
|
+ EXPECT_EQ(detected->addrs_.size(), i->addrs_.size());
|
|
|
+
|
|
|
+ // now compare addresses
|
|
|
+ for (IfaceMgr::Addr6Lst::iterator addr = detected->addrs_.begin();
|
|
|
+ addr != detected->addrs_.end(); ++addr) {
|
|
|
+ bool addr_found = false;
|
|
|
+
|
|
|
+ for (IfaceMgr::Addr6Lst::iterator a = i->addrs_.begin();
|
|
|
+ a != i->addrs_.end(); ++a) {
|
|
|
+ if (*addr != *a) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ addr_found = true;
|
|
|
+ }
|
|
|
+ if (!addr_found) {
|
|
|
+ cout << "ifconfig does not seem to report " << addr->toText()
|
|
|
+ << " address on " << detected->getFullName() << " interface." << endl;
|
|
|
+ FAIL();
|
|
|
+ }
|
|
|
+ cout << "Address " << addr->toText() << " on iterface " << detected->getFullName()
|
|
|
+ << " matched with 'ifconfig -a' output." << endl;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ delete ifacemgr;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
}
|