Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Adam Osuchowski
Kea
Commits
697a0229
Commit
697a0229
authored
Dec 13, 2011
by
Tomek Mrugalski
🛰
Browse files
[1237] Test for interface detection on Linux implemented.
parent
d1a22e48
Changes
2
Hide whitespace changes
Inline
Side-by-side
src/lib/dhcp/iface_mgr.cc
View file @
697a0229
...
...
@@ -97,9 +97,6 @@ IfaceMgr::IfaceMgr()
detectIfaces
();
if
(
!
openSockets6
())
{
isc_throw
(
Unexpected
,
"Failed to open/bind sockets."
);
}
}
catch
(
const
std
::
exception
&
ex
)
{
cout
<<
"IfaceMgr creation failed:"
<<
ex
.
what
()
<<
endl
;
...
...
@@ -209,20 +206,21 @@ IfaceMgr::printIfaces(std::ostream& out /*= std::cout*/) {
out
<<
"Detected interface "
<<
iface
->
getFullName
()
<<
", hwtype="
<<
iface
->
hardware_type_
<<
", maclen="
<<
iface
->
mac_len_
<<
", mac="
<<
iface
->
getPlainMac
()
<<
endl
;
out
<<
"flags="
<<
hex
<<
iface
->
flags_
<<
dec
<<
"("
<<
", mac="
<<
iface
->
getPlainMac
();
out
<<
"
,
flags="
<<
hex
<<
iface
->
flags_
<<
dec
<<
"("
<<
(
iface
->
flag_loopback_
?
"LOOPBACK "
:
""
)
<<
(
iface
->
flag_up_
?
"UP "
:
""
)
<<
(
iface
->
flag_running_
?
"RUNNING "
:
""
)
<<
(
iface
->
flag_multicast_
?
"MULTICAST "
:
""
)
<<
(
iface
->
flag_broadcast_
?
"BROADCAST "
:
""
)
<<
")"
<<
endl
;
out
<<
" "
<<
iface
->
addrs_
.
size
()
<<
" addr(s):"
<<
endl
;
out
<<
" "
<<
iface
->
addrs_
.
size
()
<<
" addr(s):"
;
for
(
Addr6Lst
::
const_iterator
addr
=
iface
->
addrs_
.
begin
();
addr
!=
iface
->
addrs_
.
end
();
++
addr
)
{
out
<<
" "
<<
addr
->
toText
()
<<
endl
;
out
<<
" "
<<
addr
->
toText
();
}
out
<<
endl
;
}
}
...
...
src/lib/dhcp/tests/iface_mgr_unittest.cc
View file @
697a0229
...
...
@@ -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
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment