BIND 10 #1528: Interface detection on Linux refactoring

BIND 10 Development do-not-reply at isc.org
Fri Mar 2 17:01:15 UTC 2012


#1528: Interface detection on Linux refactoring
-------------------------------------+-------------------------------------
                   Reporter:  tomek  |                 Owner:  tomek
                       Type:         |                Status:  reviewing
  enhancement                        |             Milestone:  Sprint-
                   Priority:  major  |  DHCP-20120305
                  Component:         |            Resolution:
  libdhcp                            |             Sensitive:  0
                   Keywords:         |           Sub-Project:  DHCP
            Defect Severity:  N/A    |  Estimated Difficulty:  0
Feature Depending on Ticket:         |           Total Hours:  0
        Add Hours to Ticket:  0      |
                  Internal?:  0      |
-------------------------------------+-------------------------------------
Changes (by stephen):

 * owner:  UnAssigned => tomek


Comment:

 '''src/lib/dhcp/iface_mgr_linux.cc'''[[BR]]
 Should update copyright to 2011-2012.

 Really needs an introduction explaining the logic of Linux interfaces,
 e.g. what is netlink, what messages are being used, what needs to happen
 etc.

 It is not clear why a vector of pointers is required to have many nlmsg
 structures pointing to the same header.  Can this comment be expanded?

 There is no need to use the keyword "struct" in the declaration of a
 vector of pointers to nlmsghdr objects.

 "Holds information about interface or address attributes."  This does not
 hold the information, it merely points to it. Also, what is the
 significance of "rt" in "rtaddtr", "RTattribs" and rtnl_* - Real-Time?
 (What I'm trying to get across is that the reader may not know much about
 the Netlink interface as it is not as widely-used as, say, the socket
 interface.  Adding comments to guide them makes it much easier to follow
 the logic and to spot errors.)

 Both !NetlinkMessages and RTattribs objects hold pointers to other
 objects.  This immediately raises the question of what is expected to free
 up the memory pointed to by them.  This should be documented.  (In fact,
 looking further on in the code I note that there is a special function
 that deletes the pointed-to objects.  In this case, why not declare the
 vectors as vectors of scoped_ptrs?  Then the objects will be automatically
 deleted when the vector is deleted.)

 Typo: "addres info", should be "address info".  Also "any code reuse"
 should be "any code reusable".

 The assumption that IFA_MAX is shorter than IFLA_MAX can be explicitly
 checked during compilation using BOOST_STATIC_ASSERT.

 No need to use "struct" in the declaration of rtnl_handle.

 Should document why !__u32 is used as a data type rather than uint32_t in
 rtnl_handle.

 sndbuf/rcvbuf.  These variables sound like send buffer and receive buffer,
 whereas in fact they are the size of the buffer.  SNDBUF_SIZE and
 RCVBUF_SIZE (or xxx_MAX) would be better names for them.

 ''rtnl_send_request''[[BR]]
 The members of "struct req" do not need to include the "struct"
 declaration in C++.

 In rtnl, two structs are declared with another one.  The enclosing struct
 is then sent via a sendto() call with sizeof(enclosing-struct) as the
 size.  Are we sure that the compiler is not aligning the member structs on
 natural boundaries? In other words, in some circumstances could there be
 unused bytes between the two?  Addition of an assertion to check that
 would be useful.

 Spaces around the binary operator "|" are required.

 A comment as to why handle.dump is set to ++handle.seq would be useful -
 that is non-obvious, as handle is just described as a "context structure".
 Also, I would explicitly put that assignment as a separate statement -
 it's easy to overlook.

 In the call to sendto(), the casting should be done using C++ constructs.

 ''rtnl_store_reply''[[BR]]
 "Appends nlmsg to a storage" should be "Appends a copy of a !NetLink
 message to the message store".

 Why the redundant parentheses in the call to "new"?

 No need to check if {{{copy == NULL}}} as the "new" operation would throw
 an exception if this were true.  (But if it didn't, the check should
 follow the memcpy() call, which would segfault if the destination were
 null.)

 ''parse_rtattr''[[BR]]
 Reading this raised the following question: why is a boost array used to
 store pointers to the rtattr structure rather than a vector (as is the
 case for a !NetlinkMessage pointer)?  (As it is, if the number of rtattr
 structures is too many for the array, the excess are silently ignored.)

 In the "if" block, braces are required even for single-line statements.

 What are the RTA_OK and RTA_NEXT macros?  Some comments are required here.

 ''ipaddrs_get''[[BR]]
 This parses addr_info - but addr_info is described as a collection of
 parsed netlink messages.  More information about what this function
 actually does is required.

 Why is addr declared with a length of 16?  A symbolic constant should be
 used.

 {{{struct ifaddrmsg *ifa"}}} should be {{{ifaddrmsg* ifa}}} (note the
 absence of "struct" and the position of the "*".)

 Should use C++ construct to cast to "ifaddrmsg*".

 Redundant space after "(" in the "if" statement.

 No need to use "struct rtattr*" in call to fill() - "rtattr*" should be
 sufficient.

 The two "if" blocks checking ifa->family are identical, with the exception
 of the address family in the call to IOAddress::from_bytes and the size of
 the data in the memcpy() call.  The common code should be abstracted into
 a separate function.  (Additionally: (a) The "if" blocks in this code
 should include braces, (b) C++ casts should be used in the call to memcpy
 (and the second argument should be cast to {{{const void*}}}) and (c)
 symbolic constants should be used as the length of data to be copied in
 the call to memcpy.

 ''rtnl_process_reply''[[BR]]
 It would be helpful if the header were to say what processing was going to
 be done. (And comments in the code to say that as well.)

 Using "rth" as the designation for "netlink parameters" seems a bit
 obscure.

 No need to use the "struct" keyword when declaring variables.

 Declaration of "status" should be at first use. (The same goes for the
 declaration of "header".)

 Does iov.iov_len vary between iterations of the loop?  If not, why not set
 it outside?

 The "if" statement checking for EINTR should have the body within braces.

 If recvmsg() returns a value less than zero, it does not automatically
 mean that there is an overrun - it could be any one of a number of errors
 (determined by errno).  Including errno in the exception thrown would be
 useful for diagnostic purposes.

 I notice that here, if recvmsg fails due to an interrupted system call
 status, the call is retried.  This does not happen in the recvmsg() calls
 in iface_mgr.cc.  If this is an omission, I would create a separate
 function that calls recvmsg and handles the case of an interrupted system
 call.

 Assignment of address of "buf" to "header" - use C++ casts.

 The comment "Why we received this anyway?" does not give much information.
 From the code, better would be something like "Received a message not
 addressed to our process, or not with a sequence number we are expecting.
 Ignore, and look at the next one."

 ''release_list''[[BR]]
 This would not be needed if the object were a vector of scoped_ptrs - a
 call to vector::clear() would handle the destruction of the pointed-to
 objects.

 ''detectIfaces''[[BR]]
 The comment "link info" on a variable named "link_info" is not really
 helpful (same for addr_info).

 Start comments with a capital letter.

 "len" can be declared within the main loop, at the point of first use.

 Within the main "for" loop, why not set "len" with a single statement.
 And what is "len" being set to?

 The "valgrind" report has to be bogus - iface_name is scope-limited to the
 "for" loop and takes a copy of its argument.  However, it is not clear
 what the string represents - a comment would be useful.  And are we sure
 the string is non-null?

 Looking at the declaration of iface:
  * The index is set in the constructor
  * The hardware type is set via a direct access to a member of Iface
  * The flags are set through a call to the method setFlagsx().
 This is inconsistent.

 The inconsistency is carried through to the setting of the mac_len_ and
 mac_ members: these should be zeroed in the the constructor.  Similarly
 the copying of the MAC from the attribs_table should be handled by a
 single call to an Iface method, passing to it the address of the data and
 length of the data.

 Note that if exceptions are thrown, the socket will not be closed.  This
 suggests that rtnl_handle should contain a destructor that closes the
 socket.  Also, since a socket is opened with rtnl_open_socket(), it would
 be symmetrical for there to be a rtnl_close_socket() call to avoid the
 need to expose handle.fd.  In fact, going further, I would suggest that
 rtnl_handle should be a fully-fledged class, and that the all the rtnl_xxx
 functions should be class methods.  This would give maximum information
 hiding.

 ''IfaceMgr::setFlags''[[BR]]
 Why are some members of !IfaceMgr public?  In particular, why have the
 flags_xxx_ members, which effectively duplicate the contents of flags_?

 '''src/lib/dhcp/tests/iface_mgr_unittest.cc'''[[BR]]
 When processing ifconfig output, you may want to consider use of
 isc::util::str::tokens, which splits output into separate tokens,
 regardless of the number of intervening spaces.  Processing tokens one at
 a time rather than searching through the string for the first space etc.
 may be easier. (This is a comment, no action required.)

 parse_ifconfig: looking at the output of ifconfig on both FreeBSD
 (bikeshed) and Solaris (sol-10), it might be safer to regard the output
 for an interface as starting at the line where the first character is not
 whitespace.

 Use spaces around binary operators (e.g. {{{line.find("inet")+5}}}).

 '''src/lib/dhcp/tests/pkt4_unittest.cc'''[[BR]]
 Test metaFields: other tests used boost::shared_ptr to store the newly-
 created Pkt4 object and to ensure destruction when the test exits, it
 would be logical for this to do so. (As an aside, use of shared_ptr in
 this context is an unnecessary overhead - if the pointer is not going to
 be shared, scoped_ptr would be better.)

-- 
Ticket URL: <http://bind10.isc.org/ticket/1528#comment:6>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development


More information about the bind10-tickets mailing list