BIND 10 #1959: Implement perfdhcp control logic
BIND 10 Development
do-not-reply at isc.org
Tue Sep 4 15:38:39 UTC 2012
#1959: Implement perfdhcp control logic
-------------------------------------+-------------------------------------
Reporter: | Owner: tomek
stephen | Status: reviewing
Type: | Milestone: Sprint-
enhancement | DHCP-20120917
Priority: | Resolution:
medium | Sensitive: 0
Component: | Sub-Project: DHCP
perfdhcp | Estimated Difficulty: 0
Keywords: | Total Hours: 0
Defect Severity: N/A |
Feature Depending on Ticket: |
Add Hours to Ticket: 0 |
Internal?: 0 |
-------------------------------------+-------------------------------------
Comment (by marcin):
Replying to [comment:9 tomek]:
> This review is based on commit-id
6efd035f6178974adb48ace599f6d2335c0afd7e (and all previous changes on
trac1959 that are not present on master).
>
> Generic comment: CommandOptions class should be renamed to
CommandParameters or CommandSwitches. Option has very specific meaning in
DHCP context - something different that CommandOptions convey. This
applies to many uses of that class, e.g. in sendRequest6() and similar
methods, "options" object is really for DHCP options. It should be named
"parameters" (or "params" if you prefer short names).
>
I am not fully convinced but I think we can do this as a part of code
cleanup (ticket #1960).
> '''tests/tools/perfdhcp/test_control.h'''
>
> Is the TestControlSocket class really needed? Why can't you use
IfaceMgr::SocketInfo? It holds all the data, except interface information.
If you need interface info, perhaps TestControlSocket could be derived
from IfaceMgr::SocketInfo? Its definition would be simpler.
I prefer classes with read-only access to variables and that's why I
started implementing my own class here. However I admit that it is cleaner
to reuse the existing code so I now derive from SocketInfo.
>
> Private default TestControlSocket constructor is not need. Since there
is another constructor explicitly defined, the implicit default
constructor with public access will not be created.
Ok, removed.
>
> ~TestControlSocket(): There's TODO outstanding there - to close only the
socket represented by this object, not all sockets in the whole IfaceMgr.
Fortunately, that is very easy to do. The following code should do the
trick:
>
> {{{
> IfaceMgr::Iface* iface = IfaceMgr::instance().getIface(ifindex_);
> if (iface) {
> iface->delSocket(socket_);
> }
> }}}
Ok, I changed the destructor as suggested.
>
> Also, TODO should be written as \TODO or @TODO to mark it up on Doxygen
todo list.
Fixed.
>
> HW_ETHER_LEN constant: it is ok to keep it as 6 bytes for now, but we
will need to extend the code one day to check if longer MAC addresses
cause problems. The longest link-layer address I'm aware of is used in
Infiniband. It is 20 bytes long. A simple @TODO will do the trick for now.
I added todo statement.
>
> TestControlSocket keeps both interface name (iface_) and its index
(ifindex_). Keeping both is redundant. If both are kept for the (minimal)
performance reasons, it should be commented. Otherwise it would be simpler
to keep a pointer to the interface object and use its getName() and
getIndex(). The added benefit is an access to getFullName() method that is
useful for logging purposes (it returns both in a readable format, e.g.
"eth0/2"). It is also more extensive, if you ever decide that more
information is needed in the future.
>
I left ifindex_ and now client class uses IfaceMgr::getIface to get the
interface name.
> The comment just after protected: It should be moved above protected.
The general approach is that the comment appears before the piece of code
that is being commented. It is a good point about using friends. I tried
to use that approach in one ticket, but I later removed it to follow
whatever others do. It might be useful to write a question about it to
bind10-dev asking if people are ok with using "friends" approach. As the
discussion on bind10-dev may be lengthy, carry on with this ticket. If the
discussion reaches a conclusion that fried is the way to go, we will have
lots of places to change the code anyway.
Comment moved. That's going to be hard job to change all classes to use
"friend" approach. Maybe this could be applied to new classes only.
>
> checkExitConditions() description: fulfiled => fulfilled.
Corrected.
>
> factoryElapsedTime6(): buf parameter should be described more throughly
- does it contain the whole option (including option header, 6 bytes in
total) or just content of the option(2 bytes)? This also applies to many
other factory methods.
Comment added.
>
> This factory is supposed to create *DHCPv6* option. Why does it take
universe parameter? If this parameter is necessary because of factory
registration, then it should be commented on (and definitely not say V4 or
V6 in the parameter description).
The parameter is required for registration purpose. With current approach
to register factory functions many parameters like this will be ignored. I
updated comments at parameters which are ignored that they are ignored.
>
> factoryIana6(), factoryOptionRequestOption6(): Why those methods end
with 6? There are no v4 equivalents of those options, so 6 is redundant in
the name.
The naming convention that I am following is use
- factoryABC6 for functions that are used to create DHCPv6 options only
- factoryDEF4 for functions that are used to create DHCPv4 options only
- factoryGHI for functions that can be used for either DHCPv4 or v6
options.
The function names correspond to this. Although it may seem a little
redundant the option name indicates which factory function is for which IP
mode (for somebody who does not recognize it from the option name). I
insist that we leave it as it is.
>
> Many factory methods throw exceptions if specified buffer is of invalid
size. That should be reflected in the description with \throw.
I belive it is. Can you point which one that throws exception is left
without \throw?
>
> generateDuid(): typo in the description: clinets => clients (x2).
uint8_t passed by reference, because this method sets the randomized
value. It should be noted somewhere in the description that this is an
output parameter and its initial value is ignored.
>
Comment updated.
> generateMacAddress(): See generateDuid() comments.
Comment updated.
>
> generateTransId(): should mention if the transid is 32bit value (DHCPv4)
or (DHCPv6).
Comment updated.
>
> getNextExchangesNum(): immediatelly => immediately.
Fixed.
>
> getTemplateBuffer(): Wouldn't it be more correct to throw exception,
when user requested non-existing buffer?
It now throws exception. It does not change anything but it is more
consistent with other functions at least.
>
> initTemplateFiles(): Where is the list of template files specified? It
should briefly comment on it, as that info is not passed as parameter.
I am not sure if I understand your point. The list of template files is
given from command line. I updated comments to reflect this.
>
> openSocket(): Why do you need to set multicast() flag on the socket for
DHCPv6? This flag is only needed for receiving multicast traffic. A client
never never does that. With v4, things are a bit different, as the server
response may be sent to broadcast address if certain conditions are met.
The logic to set multicast option on the socket for multicast remote
addresses is taken from previous perfdhcp version (implemented by
Francis). I will double check if I can safely remove this code during the
course of #1960.
>
> The comment for open socket should also mention if the sockets opened
are registered in IfaceMgr or not. I presume they are, otherwise IfaceMgr
wouldn't be able to use them to send and receive traffic.
I added this comment but from the caller perspective it is of a less
importance. This is more TestControl class internals that rely on this
fact.
>
> There are many articles missing in the comments in test_control.h. As
I've never learned to handle them correctly, I will not bother to guess
when they're needed.
I added one or two articles. I haven't found "many".
>
> receivePacket4(), receivePacket6(): It looks more natural when received
packet is returned by the method, so unless there are specific reasons to
do otherwise, I would prefer the prototype to be:
> {{{
> const dhcp::Pkt4Ptr receivePacket4(const TestControlSocket& socket);
> }}}
The packet is NOT returned here at all. It is the input parameter. The
actual packet reception from the wire is done elsewhere. The
receivePacketX functions simply do processing on received packet (e.g.
update statistics on StatsMgr, calculate RTT etc.).
>
> On the other hand, if you want to keep it similar to sendXXX4/6()
methods, I won't object, but please add [in] or [out] to the parameters
passed by reference.
I put the [in,out] where applicable. I did not put this where I pass const
reference or where I don't pass reference at all.
>
> Also, both methods should throw if someone tries to use v4 socket to
receive v6 packet (and vice versa). That would be easy to check if
IfaceMgr::SocketInfo structures were used. If such a check is not desired,
because it is already checked in receivePackets(), a warning comment
should be added.
>
The code checks mode of operation (v4 or v6) on the very beginning and
uses receive4 or receive6 methods accordingly. If the code is ok (I belive
it is) it should never get v6 packet on v4 socket etc. I added warnings to
receivePacketX functions that they don't do these sanity checks.
> sendDiscover4(), sendSolicit6(), sendRequest4() and many similar:
> - for parameters passed by reference, please specify the direction
([in], [out] or [in,out]). See
http://www.stack.nl/~dimitri/doxygen/docblocks.html (search for [in]).
Why would I need it here? I pass all variables by const reference. It is
clear that they are [in] params.
> - please supplement the descriptions with the information that sent
packets are stored somewhere (in stats_mgr).
Comment added?
>
> testDiags(): Find of => Find if
Corrected.
>
> byte2Hex(), vector2Hex(): These are very generic methods. Similar
methods exist in src/lib/util/encode/hex.h (and other headers in that
directory). If there isn't one already defined, please move them there.
My methods in their shape do not fit to src/lib/util/encode/hex.h or any
other file there. In the same time, methods provided there do not provide
separators. Trying to move my code there might be tricky and I prefer
postponing it for now.
>
> getElapsedTime(). This method is sufficiently complex to move its body
to .cc file. Also, wouldn't it be more appropriate to throw exception (or
return 0xffffffff) when time is not specified? The reason for returning
0xffffffff is to mark bogus packet exchanges. (there's definitely
something wrong if packet was not timestamped).
Moved to .cc file and exception is now thrown.
>
> factoryIana6(): The description should be very specific that the option-
buffer should point to IA_NA *sub-options*. The code should have @todo
somewhere stating that IAID, T1 and T2 are hardcoded. Perhaps .cc file is
better for such @todo comment.
Added comment.
>
> '''tests/tools/perfdhcp/test_control.cc'''
>
> General comment: Adding "using namespace XYZ;" would make many
expressions shorter.
But in many cases having namespaces is preferred because it clearly
indicates which module the methods belong to.
>
> checkExitConditions():148. The check should be
> {{{
> if (options.getNumRequests().size() > 1) {
> }}}
> as a future compatibility for cases, when RENEW support will be added.
Ok. I added this but if we ever enable renew support it will have to be
modified anyway.
>
> The check for reaching maximum drops percentage can be skipped, if total
drop is 0. A simple if around the whole section should do the trick.
Does it really improve any performance here? I don't want this function to
grow any bigger than necessary so I dropped this suggestion.
>
> factoryElapsedTime6(): As a sanity check we should probably verify that
universe is v6 and option type is D6O_EeLAPSED_TIME. The same applies to
other factory functions.
The factory functions now ignore universe.
>
> factoryOptionRequestOption6(): The comment in .h file states that the
buf should be empty and is ignored. There should be a check whether it is
indeed empty. Or even better, it should add name server and domain search
as it does now only if the buffer is empty, otherwise treat the buffer as
list of options to request. You may mark this as @todo for a future work
if you don't want to implement it now.
I changed the comment. The buffer is ignored.
>
> factoryRequestList4(): See factoryOptionRequestOption6().
Same here.
>
> generateMacAddress(),generate: The comment states that the random number
must be in range 0..clients_num, but the code will result in
0..clinets_num - 1. Either the code or comment should be updated.
I updated the comment.
>
> generateDuid(): please use STL copy function to copy mac_addr to duid,
instead of copying byte by byte.
Change to std::copy.
>
> getNextExchangesNum(): If you want to make sure that at least one packet
goes out, you should use
> {{{
> if (due_exchanges == 0) {
> due_exchanges == 1;
> }
> }}}
> or any fancy way of doing exactly that. Simply increasing it by one may
overshoot and in the worst case double due_exchanges value.
Ok. I added this condition.
>
> getRcvdPacketsNum(), getSentPacketsNum(), getTemplateBuffer(): Some
people would say that the second return should be in else clause. I
personally like the style you used as it keeps the code less indented.
So we are on the same side. Great :-)
>
> initPacketTemplates(): ++it does not have to be on a separate line.
>
It is not anymore.
> There are many cases were the code for stats_mgr 4 and 6 are repeated.
Have you considered making stats_mgr4 and stats_mgr6 derived from a common
ancestor and using a single pointer? That would simplify the code a lot.
o
This sounds reasonably. Initially I did not think that there will be so
many distinct invocations to StatsMgr<Pkt4> and StatsMgr<Pkt6>. In the
future we may reconsider this but not as a part of this ticket. I will
take a look at this as a part of #1960.
>
> openSocket(): Please use DHCP4_{SERVER|CLIENT}_PORT. I have no idea why
68 is "wrong" there. It should work.
This will be handled in #1960.
>
> run(): Diagnostics is => Diagnostics are
Fixed.
>
> When options.isSeeded() is called you seed PRNG with the specified seed.
Otherwise you should seed it with current time (or if it is already seeded
somewhere, add a comment to point out specific location).
I now seed from current time.
>
> ourselfs => ourselves
Fixed.
>
> template_idx is always zero. Are there any cases when additional
templates may be used? If not, please add an appropriate comment.
The reason to define the constant is that everybody knows that 0 is
template index. I do another constant when I get the template for request
packet. I just avoid using unnamed '0's in the code. I added some comment
there.
>
> Why "interrupted" is printed when 'e' flag is set? It seems there may be
other reasons to exit the main loop.
The other reasons (like fatal) error are also printed. Fatal error for
example is printed in main.cc.
>
> sendRequest4(): What happens if there is on server-id in the ADVERTISE
message and the exception is thrown? Perhaps we should let the test
continue, but increase some form of "RFC3315 violations" counter. The run
will ultimately fail, but it is often interesting not abort after first
fail, but let the server continue its work.
This will be a part of #1960.
>
> "Set elapsed time" needs one more indent space.
Fixed.
>
> sendRequest6(): Elapsed time is calculated wrong. In DHCPv4 secs field
is specified in number of seconds since address acquisition has started.
However, in DHCPv6 it specifies how much time elapsed since client
initiated *current* transaction (i.e. for REQUEST the time is measured
from first transmission of REQUEST message, not first SOLICIT).
Ok. I now set elapsed time == 0 for DHCPv6 requests.
>
> There is no check if received ADVERTISE is sane or not. First, the code
should check if there is STATUS_CODE option in the message with non-zero
status. Then it should check for non-zero STATUS_CODE opion in the IA_NA
option. Finally it should check if there's IAADDR option in IA_NA. If you
don't want to implement this now, just adding appropriate @TODO will be
sufficient for now.
We will need some other ticket for this to guarantee robustness of this
code but also in many other places.
>
> updateSendDue(): Is the code really that fast that it usually send out
packeted within microsecond of when it was supposed to be sent? I would
add a little delta when comparing now and send_due_ to avoid cases when
things are happening at the boundary between n-th and n-th microsecond.
Some time later, this could be made a configurable parameter ("allowed
send delay" or "send delay tolerance"), but it is ok to add @todo for now
(if you think it is a good idea to implement such a parameter).
In fact, I have checked that it is often that we exceed due time and
latesend counter is increased. First I would like to implement milisecond
timeout in ifaceMgr as it will change a lot the timings in main loop.
After that I will take a look how it affects the section you're poiting
at.
>
> This concludes test_control.{cc|h}. I will continue looking at the
remainder of the changes tomorrow.
Thanks for this part of review.
>
> Note to self: the review will resume from diff line 3892.
--
Ticket URL: <http://bind10.isc.org/ticket/1959#comment:11>
BIND 10 Development <http://bind10.isc.org>
BIND 10 Development
More information about the bind10-tickets
mailing list