BIND 10 trac2009, updated. c5b7c61747858fb2fb34e62f4f04d4be663f7adb Merge branch 'master' into trac2009
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu May 31 05:21:38 UTC 2012
The branch, trac2009 has been updated
via c5b7c61747858fb2fb34e62f4f04d4be663f7adb (commit)
via f89b4428468decd82132b0e0a02f2dc171fd690c (commit)
via 24fb93177f6d50c2713374ef11432510c4da6a0e (commit)
via 6002a070bac4531ece1127f32434b4bc565952d8 (commit)
via 195c7eaef0d31ad0a37ad4bf53621506a7ffe02d (commit)
via 031eaf1e2378242b5ebd2804347cba5843408eab (commit)
via b128daebb970c0b09680e1d1c485af8dcfc4ab55 (commit)
via b94f49bf264e058dac14287eca360c82edb0071d (commit)
via f77a1e4539afd8dea0a1db15d46d85b810c5c4ee (commit)
via eacdcb4322f7fc89f35af526768bf42403ad09a6 (commit)
via c4e3386e7c0b60f46ba4283dfdea069437fc3932 (commit)
via d6451dba2ce9ad4b2cf5ef63ff312136542abe4e (commit)
via 518e4673c218e2833cbeadb1a8b1cb6e8aa04f26 (commit)
via b14e2801e5f173f2eb88cbb5cb6717c1e224de18 (commit)
via 84ed05352881fca810292b827867dcd08f256cbe (commit)
via 5d228bec6c0802a6b0ff19618745257c8f5b0af4 (commit)
via 7d9bb0957c7eeb3d770befc75b9dd33667467269 (commit)
from afc0fe7345aa86ba3eaebf1bba8f62db64760c1f (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit c5b7c61747858fb2fb34e62f4f04d4be663f7adb
Merge: afc0fe7 f89b442
Author: Mukund Sivaraman <muks at isc.org>
Date: Thu May 31 10:49:11 2012 +0530
Merge branch 'master' into trac2009
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/database.cc | 27 +-
src/lib/datasrc/datasrc_messages.mes | 11 +-
src/lib/log/log_formatter.cc | 7 +
src/lib/log/log_formatter.h | 31 +-
src/lib/log/tests/log_formatter_unittest.cc | 21 +-
src/lib/python/isc/ddns/libddns_messages.mes | 32 +-
src/lib/python/isc/ddns/logger.py | 17 +-
src/lib/python/isc/ddns/session.py | 67 +-
src/lib/python/isc/ddns/tests/Makefile.am | 2 +-
src/lib/python/isc/ddns/tests/session_tests.py | 170 +-
src/lib/python/isc/ddns/tests/zone_config_tests.py | 57 +-
src/lib/python/isc/ddns/zone_config.py | 39 +-
src/lib/python/isc/log/log.cc | 10 +-
tests/tools/perfdhcp/Makefile.am | 19 +-
tests/tools/perfdhcp/perfdhcp.cc | 3565 --------------------
15 files changed, 382 insertions(+), 3693 deletions(-)
delete mode 100644 tests/tools/perfdhcp/perfdhcp.cc
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 7b271f1..62fa61e 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -839,8 +839,6 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
flags));
} else if (wti != found.second.end()) {
bool any(type == RRType::ANY());
- isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
- DATASRC_DATABASE_FOUND_RRSET);
if (any) {
// An ANY query, copy everything to the target instead of returning
// directly.
@@ -851,15 +849,32 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
target->push_back(it->second);
}
}
- lid = wild ? DATASRC_DATABASE_WILDCARD_ANY :
- DATASRC_DATABASE_FOUND_ANY;
+ if (wild) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_WILDCARD_ANY).
+ arg(accessor_->getDBName()).arg(name);
+ } else {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_ANY).
+ arg(accessor_->getDBName()).arg(name);
+ }
+ } else {
+ if (wild) {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_WILDCARD_MATCH).
+ arg(accessor_->getDBName()).arg(*wildname).
+ arg(wti->second);
+ } else {
+ LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+ DATASRC_DATABASE_FOUND_RRSET).
+ arg(accessor_->getDBName()).arg(wti->second);
+ }
}
// Found an RR matching the query, so return it. (Note that this
// includes the case where we were explicitly querying for a CNAME and
// found it. It also includes the case where we were querying for an
// NS RRset and found it at the apex of the zone.)
- return (logAndCreateResult(name, wildname, type, SUCCESS,
- wti->second, lid, flags));
+ return (ResultContext(SUCCESS, wti->second, flags));
}
// If we get here, we have found something at the requested name but not
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index a9870d6..7e3d5c2 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -79,9 +79,12 @@ in the answer as a result.
Debug information. A search in an database data source for NSEC3 that
matches or covers the given name is being started.
-% DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1: %2
+% DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1 at label count %2: %3
Debug information. An NSEC3 that covers the given name is found and
-being returned. The found NSEC3 RRset is also displayed.
+being returned. The found NSEC3 RRset is also displayed. When the shown label
+count is smaller than that of the given name, the matching NSEC3 is for a
+superdomain of the given name (see DATASRC_DATABSE_FINDNSEC3_TRYHASH). The
+found NSEC3 RRset is also displayed.
% DATASRC_DATABASE_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3
Debug information. An NSEC3 that matches (a possibly superdomain of)
@@ -157,7 +160,7 @@ A search in the database for RRs for the specified name, type and class has
located RRs that match the name and class but not the type. DNSSEC information
has been requested and returned.
-% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %5
+% DATASRC_DATABASE_FOUND_RRSET search in datasource %1 resulted in RRset %2
The data returned by the database backend contained data for the given domain
name, and it either matches the type or has a relevant type. The RRset that is
returned is printed.
@@ -276,7 +279,7 @@ nonterminal (e.g. there's nothing at *.example.com but something like
subdomain.*.example.org, do exist: so *.example.org exists in the
namespace but has no RRs assopciated with it). This will produce NXRRSET.
-% DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %5 with RRset %6
+% DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %2 with RRset %3
The database doesn't contain directly matching name. When searching
for a wildcard match, a wildcard record matching the name and type of
the query was found. The data at this point is returned.
diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc
index c728cb5..9cd5cc7 100644
--- a/src/lib/log/log_formatter.cc
+++ b/src/lib/log/log_formatter.cc
@@ -17,6 +17,10 @@
#include <cassert>
+#ifdef ENABLE_LOGGER_CHECKS
+#include <iostream>
+#endif
+
using namespace std;
using namespace boost;
@@ -59,6 +63,9 @@ checkExcessPlaceholders(string* message, unsigned int placeholder) {
// but we can't at least for now because this function is called from
// the Formatter's destructor.
#ifdef ENABLE_LOGGER_CHECKS
+ // Also, make sure we print the message so we can identify which
+ // identifier has the problem.
+ cerr << "Message " << *message << endl;
assert("Excess logger placeholders still exist in message" == NULL);
#else
message->append(" @@Excess logger placeholders still exist@@");
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index fc60203..eebdb1a 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -197,7 +197,9 @@ public:
try {
return (arg(boost::lexical_cast<std::string>(value)));
} catch (const boost::bad_lexical_cast& ex) {
-
+ // The formatting of the log message got wrong, we don't want
+ // to output it.
+ deactivate();
// A bad_lexical_cast during a conversion to a string is
// *extremely* unlikely to fail. However, there is nothing
// in the documentation that rules it out, so we need to handle
@@ -229,10 +231,35 @@ public:
// occurrences of "%2" with 42. (Conversely, the sequence
// .arg(42).arg("%1") would return "42 %1" - there are no recursive
// replacements).
- replacePlaceholder(message_, arg, ++nextPlaceholder_ );
+ try {
+ replacePlaceholder(message_, arg, ++nextPlaceholder_ );
+ }
+ catch (...) {
+ // Something went wrong here, the log message is broken, so
+ // we don't want to output it, nor we want to check all the
+ // placeholders were used (because they won't be).
+ deactivate();
+ throw;
+ }
}
return (*this);
}
+
+ /// \brief Turn off the output of this logger.
+ ///
+ /// If the logger would output anything at the end, now it won't.
+ /// Also, this turns off the strict checking of placeholders, if
+ /// it is compiled in.
+ ///
+ /// The expected use is when there was an exception processing
+ /// the arguments for the message.
+ void deactivate() {
+ if (logger_) {
+ delete message_;
+ message_ = NULL;
+ logger_ = NULL;
+ }
+ }
};
}
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index 83fc062..435b200 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -81,6 +81,14 @@ TEST_F(FormatterTest, stringArg) {
}
}
+// Test the .deactivate() method
+TEST_F(FormatterTest, deactivate) {
+ Formatter(isc::log::INFO, s("Text of message"), this).deactivate();
+ // If there was no .deactivate, it should have output it.
+ // But not now.
+ ASSERT_EQ(0, outputs.size());
+}
+
// Can convert to string
TEST_F(FormatterTest, intArg) {
Formatter(isc::log::INFO, s("The answer is %1"), this).arg(42);
@@ -117,15 +125,12 @@ TEST_F(FormatterTest, mismatchedPlaceholders) {
arg("only one");
}, ".*");
- // Mixed case of above two: the exception will be thrown due to the missing
- // placeholder, but before even it's caught the program will be aborted
- // due to the unused placeholder as a result of the exception.
- EXPECT_DEATH({
- isc::util::unittests::dontCreateCoreDumps();
- Formatter(isc::log::INFO, s("Missing the first %2"), this).
- arg("missing").arg("argument");
- }, ".*");
#endif /* EXPECT_DEATH */
+ // Mixed case of above two: the exception will be thrown due to the missing
+ // placeholder. The other check is disabled due to that.
+ EXPECT_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
+ arg("missing").arg("argument"),
+ isc::log::MismatchedPlaceholders);
}
#else
diff --git a/src/lib/python/isc/ddns/libddns_messages.mes b/src/lib/python/isc/ddns/libddns_messages.mes
index 190a6bf..12c3a8c 100644
--- a/src/lib/python/isc/ddns/libddns_messages.mes
+++ b/src/lib/python/isc/ddns/libddns_messages.mes
@@ -93,6 +93,22 @@ RRset exists (value independent). At least one RR with a
specified NAME and TYPE (in the zone and class specified by
the Zone Section) must exist.
+% LIBDDNS_UPDATE_APPROVED update client %1 for zone %2 approved
+Debug message. An update request was approved in terms of the zone's
+update ACL.
+
+% LIBDDNS_UPDATE_DENIED update client %1 for zone %2 denied
+Informational message. An update request was denied because it was
+rejected by the zone's update ACL. When this library is used by
+b10-ddns, the server will respond to the request with an RCODE of
+REFUSED as described in Section 3.3 of RFC2136.
+
+% LIBDDNS_UPDATE_DROPPED update client %1 for zone %2 dropped
+Informational message. An update request was denied because it was
+rejected by the zone's update ACL. When this library is used by
+b10-ddns, the server will then completely ignore the request; no
+response will be sent.
+
% LIBDDNS_UPDATE_ERROR update client %1 for zone %2: %3
Debug message. An error is found in processing a dynamic update
request. This log message is used for general errors that are not
@@ -109,14 +125,14 @@ will simply return a response with an RCODE of NOTIMP to the client.
The client's address and the zone name/class are logged.
% LIBDDNS_UPDATE_NOTAUTH update client %1 for zone %2: not authoritative for update zone
-Debug message. An update request for a zone for which the receiving
-server doesn't have authority. In theory this is an unexpected event,
-but there are client implementations that could send update requests
-carelessly, so it may not necessarily be so uncommon in practice. If
-possible, you may want to check the implementation or configuration of
-those clients to suppress the requests. As specified in Section 3.1
-of RFC2136, the receiving server will return a response with an RCODE
-of NOTAUTH.
+Debug message. An update request was received for a zone for which
+the receiving server doesn't have authority. In theory this is an
+unexpected event, but there are client implementations that could send
+update requests carelessly, so it may not necessarily be so uncommon
+in practice. If possible, you may want to check the implementation or
+configuration of those clients to suppress the requests. As specified
+in Section 3.1 of RFC2136, the receiving server will return a response
+with an RCODE of NOTAUTH.
% LIBDDNS_UPDATE_PREREQUISITE_FAILED prerequisite failed in update update client %1 for zone %2: result code %3
The handling of the prerequisite section (RFC2136 Section 3.2) found
diff --git a/src/lib/python/isc/ddns/logger.py b/src/lib/python/isc/ddns/logger.py
index be163a8..0f95bd7 100644
--- a/src/lib/python/isc/ddns/logger.py
+++ b/src/lib/python/isc/ddns/logger.py
@@ -28,9 +28,11 @@ class ClientFormatter:
This class is constructed with a Python standard socket address tuple.
If it's 2-element tuple, it's assumed to be an IPv4 socket address
- and will be converted to the form of '<addr>:<port>'.
+ and will be converted to the form of '<addr>:<port>(/key=<tsig-key>)'.
If it's 4-element tuple, it's assumed to be an IPv6 socket address.
- and will be converted to the form of '[<addr>]:<por>'.
+ and will be converted to the form of '[<addr>]:<por>(/key=<tsig-key>)'.
+ The optional key=<tsig-key> will be added if a TSIG record is given
+ on construction. tsig-key is the TSIG key name in that case.
This class is designed to delay the conversion until it's explicitly
requested, so the conversion doesn't happen if the corresponding log
@@ -45,16 +47,23 @@ class ClientFormatter:
Right now this is an open issue.
"""
- def __init__(self, addr):
+ def __init__(self, addr, tsig_record=None):
self.__addr = addr
+ self.__tsig_record = tsig_record
- def __str__(self):
+ def __format_addr(self):
if len(self.__addr) == 2:
return self.__addr[0] + ':' + str(self.__addr[1])
elif len(self.__addr) == 4:
return '[' + self.__addr[0] + ']:' + str(self.__addr[1])
return None
+ def __str__(self):
+ format = self.__format_addr()
+ if format is not None and self.__tsig_record is not None:
+ format += '/key=' + self.__tsig_record.get_name().to_text(True)
+ return format
+
class ZoneFormatter:
"""A utility class to convert zone name and class to string.
diff --git a/src/lib/python/isc/ddns/session.py b/src/lib/python/isc/ddns/session.py
index 51da0e9..6a4079d 100644
--- a/src/lib/python/isc/ddns/session.py
+++ b/src/lib/python/isc/ddns/session.py
@@ -19,6 +19,7 @@ from isc.log import *
from isc.ddns.logger import logger, ClientFormatter, ZoneFormatter,\
RRsetFormatter
from isc.log_messages.libddns_messages import *
+from isc.acl.acl import ACCEPT, REJECT, DROP
import copy
# Result codes for UpdateSession.handle()
@@ -46,7 +47,8 @@ class UpdateError(Exception):
- msg (string) A string explaining the error.
- zname (isc.dns.Name) The zone name. Can be None when not identified.
- zclass (isc.dns.RRClass) The zone class. Like zname, can be None.
- - rcode (isc.dns.RCode) The RCODE to be set in the response message.
+ - rcode (isc.dns.RCode or None) The RCODE to be set in the response
+ message; this can be None if the response is not expected to be sent.
- nolog (bool) If True, it indicates there's no more need for logging.
'''
@@ -67,30 +69,24 @@ class UpdateSession:
class can use the message to send a response to the client.
'''
- def __init__(self, req_message, req_data, client_addr, zone_config):
+ def __init__(self, req_message, client_addr, zone_config):
'''Constructor.
- Note: req_data is not really used as of #1512 but is listed since
- it's quite likely we need it in a subsequent task soon. We'll
- also need to get other parameters such as ACLs, for which, it's less
- clear in which form we want to get the information, so it's left
- open for now.
-
Parameters:
- req_message (isc.dns.Message) The request message. This must be
- in the PARSE mode.
- - req_data (binary) Wire format data of the request message.
- It will be used for TSIG verification if necessary.
+ in the PARSE mode, its Opcode must be UPDATE, and must have been
+ TSIG validatd if it's TSIG signed.
- client_addr (socket address) The address/port of the update client
in the form of Python socket address object. This is mainly for
logging and access control.
- zone_config (ZoneConfig) A tentative container that encapsulates
the server's zone configuration. See zone_config.py.
-
- (It'll soon need to be passed ACL in some way, too)
+ - req_data (binary) Wire format data of the request message.
+ It will be used for TSIG verification if necessary.
'''
self.__message = req_message
+ self.__tsig = req_message.get_tsig_record()
self.__client_addr = client_addr
self.__zone_config = zone_config
@@ -98,8 +94,10 @@ class UpdateSession:
'''Return the update message.
After handle() is called, it's generally transformed to the response
- to be returned to the client; otherwise it would be identical to
- the request message passed on construction.
+ to be returned to the client. If the request has been dropped,
+ this method returns None. If this method is called before handle()
+ the return value would be identical to the request message passed on
+ construction, although it's of no practical use.
'''
return self.__message
@@ -115,7 +113,8 @@ class UpdateSession:
UPDATE_DROP Error happened and no response should be sent.
Except the case of UPDATE_DROP, the UpdateSession object will have
created a response that is to be returned to the request client,
- which can be retrieved by get_message().
+ which can be retrieved by get_message(). If it's UPDATE_DROP,
+ subsequent call to get_message() returns None.
- The name of the updated zone (isc.dns.Name object) in case of
UPDATE_SUCCESS; otherwise None.
- The RR class of the updated zone (isc.dns.RRClass object) in case
@@ -130,17 +129,22 @@ class UpdateSession:
if prereq_result != Rcode.NOERROR():
self.__make_response(prereq_result)
return UPDATE_ERROR, zname, zclass
- # self.__check_update_acl()
+ self.__check_update_acl(zname, zclass)
# self.__do_update()
# self.__make_response(Rcode.NOERROR())
return UPDATE_SUCCESS, zname, zclass
except UpdateError as e:
if not e.nolog:
logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_ERROR,
- ClientFormatter(self.__client_addr),
+ ClientFormatter(self.__client_addr, self.__tsig),
ZoneFormatter(e.zname, e.zclass), e)
- self.__make_response(e.rcode)
- return UPDATE_ERROR, None, None
+ # If RCODE is specified, create a corresponding resonse and return
+ # ERROR; otherwise clear the message and return DROP.
+ if e.rcode is not None:
+ self.__make_response(e.rcode)
+ return UPDATE_ERROR, None, None
+ self.__message = None
+ return UPDATE_DROP, None, None
def __get_update_zone(self):
'''Parse the zone section and find the zone to be updated.
@@ -173,15 +177,34 @@ class UpdateSession:
# We are a secondary server; since we don't yet support update
# forwarding, we return 'not implemented'.
logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_FORWARD_FAIL,
- ClientFormatter(self.__client_addr),
+ ClientFormatter(self.__client_addr, self.__tsig),
ZoneFormatter(zname, zclass))
raise UpdateError('forward', zname, zclass, Rcode.NOTIMP(), True)
# zone wasn't found
logger.debug(DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_NOTAUTH,
- ClientFormatter(self.__client_addr),
+ ClientFormatter(self.__client_addr, self.__tsig),
ZoneFormatter(zname, zclass))
raise UpdateError('notauth', zname, zclass, Rcode.NOTAUTH(), True)
+ def __check_update_acl(self, zname, zclass):
+ '''Apply update ACL for the zone to be updated.'''
+ acl = self.__zone_config.get_update_acl(zname, zclass)
+ action = acl.execute(isc.acl.dns.RequestContext(
+ (self.__client_addr[0], self.__client_addr[1]), self.__tsig))
+ if action == REJECT:
+ logger.info(LIBDDNS_UPDATE_DENIED,
+ ClientFormatter(self.__client_addr, self.__tsig),
+ ZoneFormatter(zname, zclass))
+ raise UpdateError('rejected', zname, zclass, Rcode.REFUSED(), True)
+ if action == DROP:
+ logger.info(LIBDDNS_UPDATE_DROPPED,
+ ClientFormatter(self.__client_addr, self.__tsig),
+ ZoneFormatter(zname, zclass))
+ raise UpdateError('dropped', zname, zclass, None, True)
+ logger.debug(logger.DBGLVL_TRACE_BASIC, LIBDDNS_UPDATE_APPROVED,
+ ClientFormatter(self.__client_addr, self.__tsig),
+ ZoneFormatter(zname, zclass))
+
def __make_response(self, rcode):
'''Transform the internal message to the update response.
diff --git a/src/lib/python/isc/ddns/tests/Makefile.am b/src/lib/python/isc/ddns/tests/Makefile.am
index f8e05b8..4235a2b 100644
--- a/src/lib/python/isc/ddns/tests/Makefile.am
+++ b/src/lib/python/isc/ddns/tests/Makefile.am
@@ -6,7 +6,7 @@ CLEANFILES = $(builddir)/rwtest.sqlite3.copied
# If necessary (rare cases), explicitly specify paths to dynamic libraries
# required by loadable python modules.
if SET_ENV_LIBRARY_PATH
-LIBRARY_PATH_PLACEHOLDER = $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+LIBRARY_PATH_PLACEHOLDER = $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS
diff --git a/src/lib/python/isc/ddns/tests/session_tests.py b/src/lib/python/isc/ddns/tests/session_tests.py
index e5b85d7..7f15480 100644
--- a/src/lib/python/isc/ddns/tests/session_tests.py
+++ b/src/lib/python/isc/ddns/tests/session_tests.py
@@ -35,8 +35,11 @@ TEST_RRCLASS = RRClass.IN()
TEST_ZONE_RECORD = Question(TEST_ZONE_NAME, TEST_RRCLASS, UPDATE_RRTYPE)
TEST_CLIENT6 = ('2001:db8::1', 53, 0, 0)
TEST_CLIENT4 = ('192.0.2.1', 53)
+# TSIG key for tests when needed. The key name is TEST_ZONE_NAME.
+TEST_TSIG_KEY = TSIGKey("example.org:SFuWd/q99SzF8Yzd1QbB9g==")
-def create_update_msg(zones=[TEST_ZONE_RECORD], prerequisites=[]):
+def create_update_msg(zones=[TEST_ZONE_RECORD], prerequisites=[],
+ tsig_key=None):
msg = Message(Message.RENDER)
msg.set_qid(5353) # arbitrary chosen
msg.set_opcode(Opcode.UPDATE())
@@ -47,25 +50,35 @@ def create_update_msg(zones=[TEST_ZONE_RECORD], prerequisites=[]):
msg.add_rrset(SECTION_PREREQUISITE, p)
renderer = MessageRenderer()
- msg.to_wire(renderer)
+ if tsig_key is not None:
+ msg.to_wire(renderer, TSIGContext(tsig_key))
+ else:
+ msg.to_wire(renderer)
# re-read the created data in the parse mode
msg.clear(Message.PARSE)
msg.from_wire(renderer.get_data())
- return renderer.get_data(), msg
+ return msg
-class SessionTest(unittest.TestCase):
- '''Session tests'''
+class SesseionTestBase(unittest.TestCase):
+ '''Base class for all sesion related tests.
+
+ It just initializes common test parameters in its setUp() and defines
+ some common utility method(s).
+
+ '''
def setUp(self):
shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
- self.__datasrc_client = DataSourceClient("sqlite3",
- WRITE_ZONE_DB_CONFIG)
- self.__update_msgdata, self.__update_msg = create_update_msg()
- self.__session = UpdateSession(self.__update_msg,
- self.__update_msgdata, TEST_CLIENT4,
- ZoneConfig([], TEST_RRCLASS,
- self.__datasrc_client))
+ self._datasrc_client = DataSourceClient("sqlite3",
+ WRITE_ZONE_DB_CONFIG)
+ self._update_msg = create_update_msg()
+ self._acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "ACCEPT"}])}
+ self._session = UpdateSession(self._update_msg, TEST_CLIENT4,
+ ZoneConfig([], TEST_RRCLASS,
+ self._datasrc_client,
+ self._acl_map))
def check_response(self, msg, expected_rcode):
'''Perform common checks on update resposne message.'''
@@ -79,9 +92,12 @@ class SessionTest(unittest.TestCase):
self.assertEqual(0, msg.get_rr_count(SECTION_UPDATE))
self.assertEqual(0, msg.get_rr_count(Message.SECTION_ADDITIONAL))
+class SessionTest(SesseionTestBase):
+ '''Basic session tests'''
+
def test_handle(self):
'''Basic update case'''
- result, zname, zclass = self.__session.handle()
+ result, zname, zclass = self._session.handle()
self.assertEqual(UPDATE_SUCCESS, result)
self.assertEqual(TEST_ZONE_NAME, zname)
self.assertEqual(TEST_RRCLASS, zclass)
@@ -92,8 +108,8 @@ class SessionTest(unittest.TestCase):
def test_broken_request(self):
# Zone section is empty
- msg_data, msg = create_update_msg(zones=[])
- session = UpdateSession(msg, msg_data, TEST_CLIENT6, None)
+ msg = create_update_msg(zones=[])
+ session = UpdateSession(msg, TEST_CLIENT6, None)
result, zname, zclass = session.handle()
self.assertEqual(UPDATE_ERROR, result)
self.assertEqual(None, zname)
@@ -101,17 +117,15 @@ class SessionTest(unittest.TestCase):
self.check_response(session.get_message(), Rcode.FORMERR())
# Zone section contains multiple records
- msg_data, msg = create_update_msg(zones=[TEST_ZONE_RECORD,
- TEST_ZONE_RECORD])
- session = UpdateSession(msg, msg_data, TEST_CLIENT4, None)
+ msg = create_update_msg(zones=[TEST_ZONE_RECORD, TEST_ZONE_RECORD])
+ session = UpdateSession(msg, TEST_CLIENT4, None)
self.assertEqual(UPDATE_ERROR, session.handle()[0])
self.check_response(session.get_message(), Rcode.FORMERR())
# Zone section's type is not SOA
- msg_data, msg = create_update_msg(zones=[Question(TEST_ZONE_NAME,
- TEST_RRCLASS,
- RRType.A())])
- session = UpdateSession(msg, msg_data, TEST_CLIENT4, None)
+ msg = create_update_msg(zones=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+ RRType.A())])
+ session = UpdateSession(msg, TEST_CLIENT4, None)
self.assertEqual(UPDATE_ERROR, session.handle()[0])
self.check_response(session.get_message(), Rcode.FORMERR())
@@ -119,24 +133,20 @@ class SessionTest(unittest.TestCase):
# specified zone is configured as a secondary. Since this
# implementation doesn't support update forwarding, the result
# should be NOTIMP.
- msg_data, msg = create_update_msg(zones=[Question(TEST_ZONE_NAME,
- TEST_RRCLASS,
- RRType.SOA())])
- session = UpdateSession(msg, msg_data, TEST_CLIENT4,
+ msg = create_update_msg(zones=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+ RRType.SOA())])
+ session = UpdateSession(msg, TEST_CLIENT4,
ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)],
- TEST_RRCLASS,
- self.__datasrc_client))
+ TEST_RRCLASS, self._datasrc_client))
self.assertEqual(UPDATE_ERROR, session.handle()[0])
self.check_response(session.get_message(), Rcode.NOTIMP())
def check_notauth(self, zname, zclass=TEST_RRCLASS):
'''Common test sequence for the 'notauth' test'''
- msg_data, msg = create_update_msg(zones=[Question(zname, zclass,
- RRType.SOA())])
- session = UpdateSession(msg, msg_data, TEST_CLIENT4,
+ msg = create_update_msg(zones=[Question(zname, zclass, RRType.SOA())])
+ session = UpdateSession(msg, TEST_CLIENT4,
ZoneConfig([(TEST_ZONE_NAME, TEST_RRCLASS)],
- TEST_RRCLASS,
- self.__datasrc_client))
+ TEST_RRCLASS, self._datasrc_client))
self.assertEqual(UPDATE_ERROR, session.handle()[0])
self.check_response(session.get_message(), Rcode.NOTAUTH())
@@ -151,10 +161,10 @@ class SessionTest(unittest.TestCase):
self.check_notauth(Name('example.org'), RRClass.CH())
def __prereq_helper(self, method, expected, rrset):
- '''Calls the given method with self.__datasrc_client
+ '''Calls the given method with self._datasrc_client
and the given rrset, and compares the return value.
Function does not do much but makes the code look nicer'''
- self.assertEqual(expected, method(self.__datasrc_client, rrset))
+ self.assertEqual(expected, method(self._datasrc_client, rrset))
def __check_prerequisite_exists_combined(self, method, rrclass, expected):
'''shared code for the checks for the very similar (but reversed
@@ -232,19 +242,19 @@ class SessionTest(unittest.TestCase):
self.__prereq_helper(method, expected, rrset)
def test_check_prerequisite_exists(self):
- method = self.__session._UpdateSession__prereq_rrset_exists
+ method = self._session._UpdateSession__prereq_rrset_exists
self.__check_prerequisite_exists_combined(method,
isc.dns.RRClass.ANY(),
True)
def test_check_prerequisite_does_not_exist(self):
- method = self.__session._UpdateSession__prereq_rrset_does_not_exist
+ method = self._session._UpdateSession__prereq_rrset_does_not_exist
self.__check_prerequisite_exists_combined(method,
isc.dns.RRClass.NONE(),
False)
def test_check_prerequisite_exists_value(self):
- method = self.__session._UpdateSession__prereq_rrset_exists_value
+ method = self._session._UpdateSession__prereq_rrset_exists_value
rrset = isc.dns.RRset(isc.dns.Name("www.example.org"),
isc.dns.RRClass.IN(), isc.dns.RRType.A(),
@@ -359,13 +369,13 @@ class SessionTest(unittest.TestCase):
self.__prereq_helper(method, expected, rrset)
def test_check_prerequisite_name_in_use(self):
- method = self.__session._UpdateSession__prereq_name_in_use
+ method = self._session._UpdateSession__prereq_name_in_use
self.__check_prerequisite_name_in_use_combined(method,
isc.dns.RRClass.ANY(),
True)
def test_check_prerequisite_name_not_in_use(self):
- method = self.__session._UpdateSession__prereq_name_not_in_use
+ method = self._session._UpdateSession__prereq_name_not_in_use
self.__check_prerequisite_name_in_use_combined(method,
isc.dns.RRClass.NONE(),
False)
@@ -375,15 +385,15 @@ class SessionTest(unittest.TestCase):
creates an update session, and fills it with the list of rrsets
from 'prerequisites'. Then checks if __check_prerequisites()
returns the Rcode specified in 'expected'.'''
- msg_data, msg = create_update_msg([TEST_ZONE_RECORD],
- prerequisites)
- zconfig = ZoneConfig([], TEST_RRCLASS, self.__datasrc_client)
- session = UpdateSession(msg, msg_data, TEST_CLIENT4, zconfig)
+ msg = create_update_msg([TEST_ZONE_RECORD], prerequisites)
+ zconfig = ZoneConfig([], TEST_RRCLASS, self._datasrc_client,
+ self._acl_map)
+ session = UpdateSession(msg, TEST_CLIENT4, zconfig)
# compare the to_text output of the rcodes (nicer error messages)
# This call itself should also be done by handle(),
# but just for better failures, it is first called on its own
self.assertEqual(expected.to_text(),
- session._UpdateSession__check_prerequisites(self.__datasrc_client,
+ session._UpdateSession__check_prerequisites(self._datasrc_client,
TEST_ZONE_NAME,
TEST_RRCLASS).to_text())
# Now see if handle finds the same result
@@ -485,14 +495,14 @@ class SessionTest(unittest.TestCase):
isc.dns.RRTTL(0))
# Create an UPDATE with all 5 'yes' prereqs
- data, update = create_update_msg([TEST_ZONE_RECORD],
- [
- rrset_exists_yes,
- rrset_does_not_exist_yes,
- name_in_use_yes,
- name_not_in_use_yes,
- rrset_exists_value_yes,
- ])
+ create_update_msg([TEST_ZONE_RECORD],
+ [rrset_exists_yes,
+ rrset_does_not_exist_yes,
+ name_in_use_yes,
+ name_not_in_use_yes,
+ rrset_exists_value_yes,
+ ])
+
# check 'no' result codes
self.check_prerequisite_result(Rcode.NXRRSET(),
[ rrset_exists_no ])
@@ -599,6 +609,60 @@ class SessionTest(unittest.TestCase):
"foo"))
self.check_prerequisite_result(Rcode.FORMERR(), [ rrset ])
+class SessionACLTest(SesseionTestBase):
+ '''ACL related tests for update session.'''
+ def test_update_acl_check(self):
+ '''Test for various ACL checks.
+
+ Note that accepted cases are covered in the basic tests.
+
+ '''
+ # create a separate session, with default (empty) ACL map.
+ session = UpdateSession(self._update_msg,
+ TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+ self._datasrc_client))
+ # then the request should be rejected.
+ self.assertEqual((UPDATE_ERROR, None, None), session.handle())
+
+ # recreate the request message, and test with an ACL that would result
+ # in 'DROP'. get_message() should return None.
+ msg = create_update_msg()
+ acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "DROP", "from":
+ TEST_CLIENT4[0]}])}
+ session = UpdateSession(msg, TEST_CLIENT4,
+ ZoneConfig([], TEST_RRCLASS,
+ self._datasrc_client, acl_map))
+ self.assertEqual((UPDATE_DROP, None, None), session.handle())
+ self.assertEqual(None, session.get_message())
+
+ def test_update_tsigacl_check(self):
+ '''Test for various ACL checks using TSIG.'''
+ # This ACL will accept requests from TEST_CLIENT4 (any port) *and*
+ # has TSIG signed by TEST_ZONE_NAME; all others will be rejected.
+ acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "ACCEPT",
+ "from": TEST_CLIENT4[0],
+ "key": TEST_ZONE_NAME.to_text()}])}
+
+ # If the message doesn't contain TSIG, it doesn't match the ACCEPT
+ # ACL entry, and the request should be rejected.
+ session = UpdateSession(self._update_msg,
+ TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+ self._datasrc_client,
+ acl_map))
+ self.assertEqual((UPDATE_ERROR, None, None), session.handle())
+ self.check_response(session.get_message(), Rcode.REFUSED())
+
+ # If the message contains TSIG, it should match the ACCEPT
+ # ACL entry, and the request should be granted.
+ session = UpdateSession(create_update_msg(tsig_key=TEST_TSIG_KEY),
+ TEST_CLIENT4, ZoneConfig([], TEST_RRCLASS,
+ self._datasrc_client,
+ acl_map))
+ self.assertEqual((UPDATE_SUCCESS, TEST_ZONE_NAME, TEST_RRCLASS),
+ session.handle())
+
if __name__ == "__main__":
isc.log.init("bind10")
isc.log.resetUnitTestRootLogger()
diff --git a/src/lib/python/isc/ddns/tests/zone_config_tests.py b/src/lib/python/isc/ddns/tests/zone_config_tests.py
index 7d4bc76..4efd1c1 100644
--- a/src/lib/python/isc/ddns/tests/zone_config_tests.py
+++ b/src/lib/python/isc/ddns/tests/zone_config_tests.py
@@ -14,15 +14,23 @@
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import isc.log
-import unittest
from isc.dns import *
from isc.datasrc import DataSourceClient
from isc.ddns.zone_config import *
+import isc.acl.dns
+from isc.acl.acl import ACCEPT, REJECT, DROP, LoaderError
+
+import unittest
+import socket
# Some common test parameters
TEST_ZONE_NAME = Name('example.org')
TEST_SECONDARY_ZONE_NAME = Name('example.com')
TEST_RRCLASS = RRClass.IN()
+TEST_TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
+TEST_ACL_CONTEXT = isc.acl.dns.RequestContext(
+ socket.getaddrinfo("192.0.2.1", 1234, 0, socket.SOCK_DGRAM,
+ socket.IPPROTO_UDP, socket.AI_NUMERICHOST)[0][4])
class FakeDataSourceClient:
'''Faked data source client used in the ZoneConfigTest.
@@ -93,7 +101,7 @@ class ZoneConfigTest(unittest.TestCase):
# empty secondary list doesn't cause any disruption.
zconfig = ZoneConfig([], TEST_RRCLASS, self.__datasrc_client)
self.assertEqual((ZONE_PRIMARY, self.__datasrc_client),
- (self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS)))
+ self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS))
# adding some mulitle tuples, including subdomainof the test zone name,
# and the same zone name but a different class
zconfig = ZoneConfig([(TEST_SECONDARY_ZONE_NAME, TEST_RRCLASS),
@@ -102,14 +110,55 @@ class ZoneConfigTest(unittest.TestCase):
(TEST_ZONE_NAME, RRClass.CH())],
TEST_RRCLASS, self.__datasrc_client)
self.assertEqual((ZONE_PRIMARY, self.__datasrc_client),
- (self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS)))
+ self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS))
# secondary zone list has a duplicate entry, which is just
# (effecitivey) ignored
zconfig = ZoneConfig([(TEST_SECONDARY_ZONE_NAME, TEST_RRCLASS),
(TEST_SECONDARY_ZONE_NAME, TEST_RRCLASS)],
TEST_RRCLASS, self.__datasrc_client)
self.assertEqual((ZONE_PRIMARY, self.__datasrc_client),
- (self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS)))
+ self.zconfig.find_zone(TEST_ZONE_NAME, TEST_RRCLASS))
+
+class ACLConfigTest(unittest.TestCase):
+ def setUp(self):
+ self.__datasrc_client = FakeDataSourceClient()
+ self.__zconfig = ZoneConfig([(TEST_SECONDARY_ZONE_NAME, TEST_RRCLASS)],
+ TEST_RRCLASS, self.__datasrc_client)
+
+ def test_get_update_acl(self):
+ # By default, no ACL is set, and the default ACL is "reject all"
+ acl = self.__zconfig.get_update_acl(TEST_ZONE_NAME, TEST_RRCLASS)
+ self.assertEqual(REJECT, acl.execute(TEST_ACL_CONTEXT))
+
+ # Add a map entry that would match the request, and it should now be
+ # accepted.
+ acl_map = {(TEST_ZONE_NAME, TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "ACCEPT"}])}
+ self.__zconfig.set_update_acl_map(acl_map)
+ acl = self.__zconfig.get_update_acl(TEST_ZONE_NAME, TEST_RRCLASS)
+ self.assertEqual(ACCEPT, acl.execute(TEST_ACL_CONTEXT))
+
+ # 'All reject' ACL will still apply for any other zones
+ acl = self.__zconfig.get_update_acl(Name('example.com'), TEST_RRCLASS)
+ self.assertEqual(REJECT, acl.execute(TEST_ACL_CONTEXT))
+ acl = self.__zconfig.get_update_acl(TEST_ZONE_NAME, RRClass.CH())
+ self.assertEqual(REJECT, acl.execute(TEST_ACL_CONTEXT))
+
+ # Test with a map with a few more ACL entries. Should be nothing
+ # special.
+ acl_map = {(Name('example.com'), TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "REJECT"}]),
+ (TEST_ZONE_NAME, TEST_RRCLASS):
+ REQUEST_LOADER.load([{"action": "ACCEPT"}]),
+ (TEST_ZONE_NAME, RRClass.CH()):
+ REQUEST_LOADER.load([{"action": "DROP"}])}
+ self.__zconfig.set_update_acl_map(acl_map)
+ acl = self.__zconfig.get_update_acl(TEST_ZONE_NAME, TEST_RRCLASS)
+ self.assertEqual(ACCEPT, acl.execute(TEST_ACL_CONTEXT))
+ acl = self.__zconfig.get_update_acl(Name('example.com'), TEST_RRCLASS)
+ self.assertEqual(REJECT, acl.execute(TEST_ACL_CONTEXT))
+ acl = self.__zconfig.get_update_acl(TEST_ZONE_NAME, RRClass.CH())
+ self.assertEqual(DROP, acl.execute(TEST_ACL_CONTEXT))
if __name__ == "__main__":
isc.log.init("bind10")
diff --git a/src/lib/python/isc/ddns/zone_config.py b/src/lib/python/isc/ddns/zone_config.py
index 5b1af0c..388770c 100644
--- a/src/lib/python/isc/ddns/zone_config.py
+++ b/src/lib/python/isc/ddns/zone_config.py
@@ -13,6 +13,7 @@
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+from isc.acl.dns import REQUEST_LOADER
import isc.dns
from isc.datasrc import DataSourceClient
@@ -33,7 +34,7 @@ class ZoneConfig:
until the details are fixed.
'''
- def __init__(self, secondaries, datasrc_class, datasrc_client):
+ def __init__(self, secondaries, datasrc_class, datasrc_client, acl_map={}):
'''Constructor.
Parameters:
@@ -45,6 +46,11 @@ class ZoneConfig:
- datasrc_client: isc.dns.DataSourceClient object. A data source
class for the RR class of datasrc_class. It's expected to contain
a zone that is eventually updated in the ddns package.
+ - acl_map: a dictionary that maps a tuple of
+ (isc.dns.Name, isc.dns.RRClass) to an isc.dns.dns.RequestACL
+ object. It defines an ACL to be applied to the zone defined
+ by the tuple. If unspecified, or the map is empty, the default
+ ACL will be applied to all zones, which is to reject any requests.
'''
self.__secondaries = set()
@@ -52,6 +58,8 @@ class ZoneConfig:
self.__secondaries.add((zname, zclass))
self.__datasrc_class = datasrc_class
self.__datasrc_client = datasrc_client
+ self.__default_acl = REQUEST_LOADER.load([{"action": "REJECT"}])
+ self.__acl_map = acl_map
def find_zone(self, zone_name, zone_class):
'''Return the type and accessor client object for given zone.'''
@@ -62,3 +70,32 @@ class ZoneConfig:
return ZONE_SECONDARY, None
return ZONE_PRIMARY, self.__datasrc_client
return ZONE_NOTFOUND, None
+
+ def get_update_acl(self, zone_name, zone_class):
+ '''Return the update ACL for the given zone.
+
+ This method searches the internally stored ACL map to see if
+ there's an ACL to be applied to the given zone. If found, that
+ ACL will be returned; otherwise the default ACL (see the constructor
+ description) will be returned.
+
+ Parameters:
+ zone_name (isc.dns.Name): The zone name.
+ zone_class (isc.dns.RRClass): The zone class.
+ '''
+ acl = self.__acl_map.get((zone_name, zone_class))
+ if acl is not None:
+ return acl
+ return self.__default_acl
+
+ def set_update_acl_map(self, new_map):
+ '''Set a new ACL map.
+
+ This replaces any stored ACL map, either at construction or
+ by a previous call to this method, with the given new one.
+
+ Parameter:
+ new_map: same as the acl_map parameter of the constructor.
+
+ '''
+ self.__acl_map = new_map
diff --git a/src/lib/python/isc/log/log.cc b/src/lib/python/isc/log/log.cc
index ed05398..69e70b7 100644
--- a/src/lib/python/isc/log/log.cc
+++ b/src/lib/python/isc/log/log.cc
@@ -541,8 +541,14 @@ Logger_performOutput(Function function, PyObject* args, bool dbgLevel) {
// into the formatter. It will print itself in the end.
for (size_t i(start); i < number; ++ i) {
PyObjectContainer param_container(PySequence_GetItem(args, i));
- formatter = formatter.arg(objectToStr(param_container.get(),
- true));
+ try {
+ formatter = formatter.arg(objectToStr(param_container.get(),
+ true));
+ }
+ catch (...) {
+ formatter.deactivate();
+ throw;
+ }
}
Py_RETURN_NONE;
}
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 685ff1b..12383ac 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -12,19 +12,12 @@ if USE_STATIC_LINK
AM_LDFLAGS += -static
endif
-# We have to suppress warnings because we are compiling C code with CXX
-# We have to do this to link with new C++ pieces of code
-perfdhcp_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-perfdhcp_CXXFLAGS += -Wno-error
-else
-if USE_GXX
-perfdhcp_CXXFLAGS += -Wno-write-strings
-endif
-endif
+lib_LTLIBRARIES = libperfdhcp++.la
+libperfdhcp___la_SOURCES = command_options.cc command_options.h
+libperfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+libperfdhcp___la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
pkglibexec_PROGRAMS = perfdhcp
-perfdhcp_SOURCES = perfdhcp.cc
-perfdhcp_SOURCES += command_options.cc command_options.h
+perfdhcp_SOURCES = perfdhcp.c
+
-perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/tests/tools/perfdhcp/perfdhcp.cc b/tests/tools/perfdhcp/perfdhcp.cc
deleted file mode 100644
index 9358a35..0000000
--- a/tests/tools/perfdhcp/perfdhcp.cc
+++ /dev/null
@@ -1,3565 +0,0 @@
-/*
- * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <config.h>
-
-#ifndef HAVE_GETIFADDRS
-
-/*
- * Solaris 10 does not have the getifaddrs() function available (although it
- * is present on Solaris 11 and later). For the present we will not implement
- * a replacement (as we do with clock_gettime) as the implementation is
- * relatively complex. Just output a message saying that the utility is not
- * supported on this operating system.
- */
-
-#include <stdio.h>
-
-int
-main(const int, char* const*)
-{
- fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
- return (1);
-}
-
-#else
-
-/* getifaddrs() present, so the code should compile */
-
-#define __STDC_LIMIT_MACROS
-
-#ifdef __linux__
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#endif
-
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ifaddrs.h>
-#include <math.h>
-#include <netdb.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef HAVE_PSELECT
-
-#include <assert.h>
-
-/* Platforms such as OpenBSD don't provide a pselect(), so we use our
- own implementation for this testcase, which wraps around select() and
- hence doesn't implement the high precision timer. This implementation
- is fine for our purpose. */
-
-static int
-pselect (int nfds, fd_set *readfds, fd_set *writefds,
- fd_set *exceptfds, const struct timespec *timeout,
- const sigset_t *sigmask)
-{
- struct timeval my_timeout;
-
- /* Our particular usage of pselect() doesn't use these fields. */
- assert(writefds == NULL);
- assert(exceptfds == NULL);
- assert(sigmask == NULL);
-
- my_timeout.tv_sec = timeout->tv_sec;
- my_timeout.tv_usec = timeout->tv_nsec / 1000;
-
- return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
-}
-
-#endif /* !HAVE_PSELECT */
-
-/* DHCPv4 defines (to be moved/shared) */
-
-#define DHCP_OFF_OPCODE 0
-#define DHCP_OFF_HTYPE 1
-#define DHCP_OFF_HLEN 2
-#define DHCP_OFF_HOPS 3
-#define DHCP_OFF_XID 4
-#define DHCP_OFF_SECS 8
-#define DHCP_OFF_FLAGS 10
-#define DHCP_OFF_CIADDR 12
-#define DHCP_OFF_YIADDR 16
-#define DHCP_OFF_SADDR 20
-#define DHCP_OFF_GIADDR 24
-#define DHCP_OFF_CHADDR 28
-#define DHCP_OFF_SNAME 44
-#define DHCP_OFF_FILE 108
-#define DHCP_OFF_COOKIE 236
-#define DHCP_OFF_OPTIONS 240
-
-#define BOOTP_OP_REQUEST 1
-#define BOOTP_OP_REPLY 2
-#define BOOTP_MIN_LEN 300
-
-#define DHCP_OP_DISCOVER 1
-#define DHCP_OP_OFFER 2
-#define DHCP_OP_REQUEST 3
-#define DHCP_OP_DECLINE 4
-#define DHCP_OP_ACK 5
-#define DHCP_OP_NAK 6
-#define DHCP_OP_RELEASE 7
-#define DHCP_OP_INFORM 8
-
-#define DHCP_HTYPE_ETHER 1
-
-#define DHCP_OPT_PAD 0
-#define DHCP_OPT_SUBNET_MASK 1
-#define DHCP_OPT_TIME_OFFSET 2
-#define DHCP_OPT_ROUTERS 3
-#define DHCP_OPT_DNS_SERVERS 6
-#define DHCP_OPT_HOST_NAME 12
-#define DHCP_OPT_DOMAIN_NAME 15
-#define DHCP_OPT_BROADCAST 28
-#define DHCP_OPT_DHCP_ADDRESS 50
-#define DHCP_OPT_DHCP_LEASE 51
-#define DHCP_OPT_DHCP_MSGTYPE 53
-#define DHCP_OPT_DHCP_SRVID 54
-#define DHCP_OPT_DHCP_PRL 55
-#define DHCP_OPT_END 255
-
-#define DHCP_OPTLEN_SRVID 6
-
-/* DHCPv6 defines (to be moved/shared) */
-
-#define DHCP6_OFF_MSGTYP 0
-#define DHCP6_OFF_XID 1
-#define DHCP6_OFF_OPTIONS 4
-
-#define DHCP6_OP_SOLICIT 1
-#define DHCP6_OP_ADVERTISE 2
-#define DHCP6_OP_REQUEST 3
-#define DHCP6_OP_REPLY 7
-
-#define DHCP6_OPT_CLIENTID 1
-#define DHCP6_OPT_SERVERID 2
-#define DHCP6_OPT_IA_NA 3
-#define DHCP6_OPT_ORO 6
-#define DHCP6_OPT_ELAPSED_TIME 8
-#define DHCP6_OPT_STATUS_CODE 13
-#define DHCP6_OPT_RAPID_COMMIT 14
-#define DHCP6_OPT_NAME_SERVERS 23
-#define DHCP6_OPT_DOMAIN_SEARCH 24
-
-#define DHCP6_ST_SUCCESS 0
-#define DHCP6_ST_NOADDRSAVAIL 2
-
-#define DHCP6_DUID_LLT 1
-#define DHCP6_DUID_EPOCH 946684800
-
-/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
-
-#define ISC_TAILQ_HEAD(name, type) \
-struct name { \
- struct type *tqh_first; \
- struct type **tqh_last; \
-}
-
-#define ISC_TAILQ_ENTRY(type) \
-struct { \
- struct type *tqe_next; \
- struct type **tqe_prev; \
-}
-
-#define ISC_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
-
-#define ISC_TAILQ_FIRST(head) ((head)->tqh_first)
-
-#define ISC_TAILQ_LAST(head, headname) \
- (*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define ISC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
-
-#define ISC_TAILQ_PREV(elm, headname, field) \
- (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define ISC_TAILQ_INIT(head) do { \
- ISC_TAILQ_FIRST((head)) = NULL; \
- (head)->tqh_last = &ISC_TAILQ_FIRST((head)); \
-} while (0)
-
-#define ISC_TAILQ_INSERT_HEAD(head, elm, field) do { \
- ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head)); \
- if (!ISC_TAILQ_EMPTY((head))) \
- ISC_TAILQ_FIRST((head))->field.tqe_prev = \
- &ISC_TAILQ_NEXT((elm), field); \
- else \
- (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
- ISC_TAILQ_FIRST((head)) = (elm); \
- (elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head)); \
-} while (0)
-
-#define ISC_TAILQ_INSERT_TAIL(head, elm, field) do { \
- ISC_TAILQ_NEXT((elm), field) = NULL; \
- (elm)->field.tqe_prev = (head)->tqh_last; \
- *(head)->tqh_last = (elm); \
- (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \
-} while (0)
-
-#define ISC_TAILQ_REMOVE(head, elm, field) do { \
- if ((ISC_TAILQ_NEXT((elm), field)) != NULL) \
- ISC_TAILQ_NEXT((elm), field)->field.tqe_prev = \
- (elm)->field.tqe_prev; \
- else \
- (head)->tqh_last = (elm)->field.tqe_prev; \
- *(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field); \
-} while (0)
-
-#define ISC_TAILQ_FOREACH(var, head, field) \
- for ((var) = ISC_TAILQ_FIRST((head)); \
- (var); \
- (var) = ISC_TAILQ_NEXT((var), field))
-
-#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar) \
- for ((var) = ISC_TAILQ_FIRST((head)); \
- (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1); \
- (var) = (tvar))
-
-/*
- * Data structures
- */
-
-/*
- * exchange:
- * - per exchange values:
- * * order (for debugging)
- * * xid (even/odd for 4 packet exchanges)
- * * random (for debugging)
- * * time-stamps
- * * server ID (for 3rd packet)
- * * IA_NA (for IPv6 3rd packet)
- *
- * sent/rcvd global chains, "next to be received" on entry cache,
- * and hash table for xid -> data structure fast matching
- * (using the assumption collisions are unlikely, cf birthday problem)
- */
-
-struct exchange { /* per exchange structure */
- ISC_TAILQ_ENTRY(exchange) gchain; /* global chaining */
- ISC_TAILQ_ENTRY(exchange) hchain; /* hash table chaining */
- uint64_t order0, order2; /* number of this exchange */
- uint32_t xid; /* transaction ID */
- uint32_t rnd; /* random part */
- struct timespec ts0, ts1, ts2, ts3; /* timespecs */
- uint8_t *sid; /* server ID */
- size_t sidlen; /* server ID length */
- uint8_t *iana; /* (IPv6) IA_NA */
- size_t ianalen; /* (IPv6) IA_NA length */
-};
-struct exchange *xnext0, *xnext2; /* next to be received */
-ISC_TAILQ_HEAD(xlist, exchange); /* exchange list */
-struct xlist xsent0, xsent2, xrcvd0, xrcvd2; /* sent and received lists */
-uint64_t xscount0, xscount2; /* sent counters */
-uint64_t xrcount0, xrcount2; /* received counters */
-caddr_t exchanges0, exchanges2; /* hash tables */
-uint32_t hashsize0, hashsize2; /* hash table sizes */
-
-/*
- * statictics counters and accumulators
- */
-
-uint64_t tooshort, orphans, locallimit; /* error counters */
-uint64_t latesent, compsend, latercvd; /* rate stats */
-uint64_t multrcvd, shortwait, collected[2]; /* rate stats (cont) */
-double dmin0 = 999999999., dmin2 = 999999999.; /* minimum delays */
-double dmax0 = 0., dmax2 = 0.; /* maximum delays */
-double dsum0 = 0., dsum2 = 0.; /* delay sums */
-double dsumsq0 = 0., dsumsq2 = 0.; /* square delay sums */
-
-/*
- * command line parameters
- */
-
-int ipversion = 0; /* IP version */
-int simple; /* DO/SA in place of DORR/SARR */
-int rate; /* rate in exchange per second */
-int report; /* delay between two reports */
-uint32_t range; /* randomization range */
-uint32_t maxrandom; /* maximum random value */
-int basecnt; /* base count */
-char *base[4]; /* bases */
-int gotnumreq; /* numreq[0] was set */
-int numreq[2]; /* number of exchange */
-int period; /* test period */
-int gotlosttime; /* losttime[0] was set */
-double losttime[2] = {1., 1.}; /* time after a request is lost */
-int gotmaxdrop; /* max{p}drop[0] was set */
-int maxdrop[2]; /* maximum number of lost requests */
-double maxpdrop[2] = { 0., 0.}; /* maximum percentage */
-char *localname; /* local address or interface */
-int isinterface; /* interface vs local address */
-int preload; /* preload exchanges */
-int aggressivity = 1; /* back to back exchanges */
-int localport; /* local port number (host endian) */
-int seeded; /* is a seed provided */
-unsigned int seed; /* randomization seed */
-int isbroadcast; /* use broadcast */
-int rapidcommit; /* add rapid commit option */
-int usefirst; /* where to take the server-ID */
-char *templatefile[2]; /* template file name */
-int xidoffset[2] = {-1, -1}; /* template offsets (xid)*/
-int rndoffset[2] = {-1, -1}; /* template offsets (random) */
-int elpoffset = -1; /* template offset (elapsed time) */
-int sidoffset = -1; /* template offset (server ID) */
-int ripoffset = -1; /* template offset (requested IP) */
-char *diags; /* diagnostic selectors */
-char *wrapped; /* wrapped command */
-char *servername; /* server */
-
-/*
- * global variables
- */
-
-struct sockaddr_storage localaddr; /* local socket address */
-struct sockaddr_storage serveraddr; /* server socket address */
-
-int sock; /* socket descriptor */
-int interrupted, fatal; /* to finish flags */
-
-uint8_t obuf[4096], ibuf[4096]; /* I/O buffers */
-char tbuf[8200]; /* template buffer */
-
-struct timespec boot; /* the date of boot */
-struct timespec last; /* the date of last send */
-struct timespec due; /* the date of next send */
-struct timespec dreport; /* the date of next reporting */
-struct timespec finished; /* the date of finish */
-
-uint8_t *gsrvid; /* global server id */
-size_t gsrvidlen; /* and its length */
-uint8_t gsrvidbuf[64]; /* and its storage */
-
-/* MAC address */
-uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
-
-/* DUID prefix */
-uint8_t *duid_prefix;
-int duid_length;
-
-/* magic cookie for BOOTP/DHCPv4 */
-uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
-
-/*
- * templates
- *
- * note: the only hard point is what are the offsets:
- * - xid_discover4 and xid_request4: first of the 4 octet long
- * transaction ID (default DHCP_OFF_XID = 4)
- * - random_discover4 and random_request4: last of the 6 octet long
- * MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
- * - elapsed_request4: first of the 2 octet long secs field
- * (default DHCP_OFF_SECS = 8, 0 means disabled)
- * - serverid_request4: first of the 6 octet long server ID option
- * (no default, required)
- * - reqaddr_request4: first of the 4 octet long requested IP address
- * option content (i.e., the address itself, btw OFFER yiaddr)
- * (no default, required)
- * - xid_solicit6 and xid_request6: first of the 3 octet long
- * transaction ID (default DHCP6_OFF_XID = 1)
- * - random_solicit6 and random_request6: last of the DUID in the
- * client ID option (no default, required when rate is set)
- * - elapsed_request6: first of the 2 octet long content of
- * the option elapsed time option (no default, 0 means disabled)
- * - serverid_request6: position where the variable length server ID
- * option is inserted (no default, required, set to length means append)
- * - reqaddr_request6: position where of the variable length requested
- * IP address option is inserted (no default, required, set to
- * length means append)
- */
-
-size_t length_discover4;
-uint8_t template_discover4[4096];
-size_t xid_discover4;
-size_t random_discover4;
-size_t length_request4;
-uint8_t template_request4[4096];
-size_t xid_request4;
-size_t elapsed_request4;
-size_t random_request4;
-size_t serverid_request4;
-size_t reqaddr_request4;
-size_t length_solicit6;
-uint8_t template_solicit6[4096];
-size_t xid_solicit6;
-size_t random_solicit6;
-size_t length_request6;
-uint8_t template_request6[4096];
-size_t xid_request6;
-size_t elapsed_request6;
-size_t random_request6;
-size_t serverid_request6;
-size_t reqaddr_request6;
-
-
-// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
-// if the code is being compiled or Linux (or somewhere else)
-// Perhaps this should be based on OS_LINUX define?
-
-#if !defined (CLOCK_REALTIME)
-#define CLOCK_REALTIME 0
-
-/// @brief clock_gettime implementation for non-Linux systems
-///
-/// This implementation lacks nanosecond resolution. It is intended
-/// to be used on non-Linux systems that does not provide clock_gettime
-/// implementation.
-///
-/// @param clockid ignored (kept for Linux prototype compatibility)
-/// @param tp timespec structure
-///
-/// @return always zero (kept for compatibility reasons)
-int clock_gettime(int, struct timespec *tp) {
- struct timeval tv;
- gettimeofday(&tv, NULL);
- tp->tv_sec = tv.tv_sec;
- tp->tv_nsec = tv.tv_usec*1000;
-
- return (0);
-}
-
-#endif
-
-/*
- * initialize data structures handling exchanges
- */
-
-void
-inits(void)
-{
- struct xlist *bucket;
- caddr_t p;
- size_t len, i;
-
- ISC_TAILQ_INIT(&xsent0);
- ISC_TAILQ_INIT(&xsent2);
- ISC_TAILQ_INIT(&xrcvd0);
- ISC_TAILQ_INIT(&xrcvd2);
-
- /// compute hashsizes
- hashsize0 = 1024;
- len = sizeof(*bucket) * hashsize0;
- exchanges0 = (caddr_t)malloc(len);
- if (exchanges0 == NULL) {
- perror("malloc(exchanges0)");
- exit(1);
- }
- for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
- bucket = (struct xlist *) p;
- ISC_TAILQ_INIT(bucket);
- }
- if (simple != 0)
- return;
- hashsize2 = 1024;
- len = sizeof(*bucket) * hashsize2;
- exchanges2 = (caddr_t)malloc(len);
- if (exchanges2 == NULL) {
- perror("malloc(exchanges2)");
- exit(1);
- }
- for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
- bucket = (struct xlist *) p;
- ISC_TAILQ_INIT(bucket);
- }
-}
-
-/*
- * randomize the value of the given field:
- * - offset of the field
- * - random seed (used as it when suitable)
- * - returns the random value which was used
- */
-
-uint32_t
-randomize(size_t offset, uint32_t r)
-{
- uint32_t v;
-
- if (range == 0)
- return 0;
- if (range == UINT32_MAX)
- return r;
- if (maxrandom != 0)
- while (r >= maxrandom)
- r = (uint32_t) random();
- r %= range + 1;
- v = r;
- v += obuf[offset];
- obuf[offset] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 1];
- obuf[offset - 1] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 2];
- obuf[offset - 2] = v;
- if (v < 256)
- return r;
- v >>= 8;
- v += obuf[offset - 3];
- obuf[offset - 3] = v;
- return r;
-}
-
-/*
- * receive a reply (4th packet), shared between IPv4 and IPv6:
- * - transaction ID xid
- * - receiving time-stamp now
- * called from receive[46]() when the xid is odd
- */
-
-void
-receive_reply(uint32_t xid, struct timespec *now)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- uint32_t hash;
- int checklost;
- double delta;
-
- /* bucket is needed even when the next cache matches */
- hash = (xid >> 1) & (hashsize2 - 1);
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext2 != NULL) && (xnext2->xid == xid)) {
- x = xnext2;
- goto found;
- }
- /* usually the lost probability is low for request/reply */
- checklost = 1;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- checklost = 0;
- /* check for a timed-out exchange */
- waited = now->tv_sec - x->ts2.tv_sec;
- waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
- if (waited < losttime[1])
- continue;
- /* garbage collect timed-out exchange */
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent2, x, gchain);
- free(x);
- collected[1] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount2++;
- x->ts3 = *now;
- delta = x->ts3.tv_sec - x->ts2.tv_sec;
- delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
- if (delta < dmin2)
- dmin2 = delta;
- if (delta > dmax2)
- dmax2 = delta;
- dsum2 += delta;
- dsumsq2 += delta * delta;
- xnext2 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent2, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
-}
-
-/*
- * get the DHCPv4 socket descriptor
- * (the only complexity is broadcast enabling: there is no easy way to
- * recognize broadcast addresses, so the command line -B flag)
- */
-
-void
-getsock4(void)
-{
- int ret;
-
- /* update local port */
- if (localport != 0) {
- uint16_t lp = htons((uint16_t) localport);
-
- ((struct sockaddr_in *) &localaddr)->sin_port = lp;
- }
- sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (sock < 0) {
- perror("socket");
- exit(1);
- }
- ret = bind(sock,
- (struct sockaddr *) &localaddr,
- sizeof(struct sockaddr_in));
- if (ret < 0) {
- perror("bind");
- exit(1);
- }
- /* enable broadcast if needed or required */
- if (isbroadcast != 0) {
- int on = 1;
-
- ret = setsockopt(sock,
- SOL_SOCKET, SO_BROADCAST,
- &on, sizeof(on));
- if (ret < 0) {
- perror("setsockopt(SO_BROADCAST)");
- exit(1);
- }
- }
-}
-
-/*
- * build a DHCPv4 DISCOVER from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_discover4(void)
-{
- uint8_t *p = template_discover4;
-
- length_discover4 = BOOTP_MIN_LEN;
- xid_discover4 = DHCP_OFF_XID;
- random_discover4 = DHCP_OFF_CHADDR + 6;
- /* opcode */
- p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
- /* hardware address type */
- p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
- /* hardware address length */
- p[DHCP_OFF_HLEN] = 6;
- /* hops */
- p[DHCP_OFF_HOPS] = 1;
- /* gateway address */
- memcpy(p + DHCP_OFF_GIADDR,
- &((struct sockaddr_in *) &localaddr)->sin_addr,
- 4);
- /* hardware address */
- memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
- /* cookie */
- memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
- /* options */
- p += DHCP_OFF_OPTIONS;
- /* inline DHCP message type */
- *p++ = DHCP_OPT_DHCP_MSGTYPE;
- *p++ = 1;
- *p++ = DHCP_OP_DISCOVER;
- /* inline DHCP parameter request list (default) */
- *p++ = DHCP_OPT_DHCP_PRL;
- *p++ = 7;
- *p++ = DHCP_OPT_SUBNET_MASK;
- *p++ = DHCP_OPT_BROADCAST;
- *p++ = DHCP_OPT_TIME_OFFSET;
- *p++ = DHCP_OPT_ROUTERS;
- *p++ = DHCP_OPT_DOMAIN_NAME;
- *p++ = DHCP_OPT_DNS_SERVERS;
- *p++ = DHCP_OPT_HOST_NAME;
- /* end */
- *p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_discover4(void)
-{
- uint8_t *p = template_discover4;
- int fd, cc, i, j;
-
- fd = open(templatefile[0], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(1);
- }
- if (cc < 100) {
- fprintf(stderr, "file '%s' too short\n", templatefile[0]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[0]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[0]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[0]);
- exit(2);
- }
- length_discover4 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[0] >= 0)
- xid_discover4 = (size_t) xidoffset[0];
- else
- xid_discover4 = DHCP_OFF_XID;
- if (xid_discover4 + 4 > length_discover4) {
- fprintf(stderr,
- "xid (at %zu) outside the template (length %zu)?\n",
- xid_discover4, length_discover4);
- exit(2);
- }
- if (rndoffset[0] >= 0)
- random_discover4 = (size_t) rndoffset[0];
- else
- random_discover4 = DHCP_OFF_CHADDR + 6;
- if (random_discover4 > length_discover4) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_discover4, length_discover4);
- exit(2);
- }
-}
-
-/*
- * build a DHCPv4 REQUEST from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_request4(void)
-{
- uint8_t *p = template_request4;
-
- length_request4 = BOOTP_MIN_LEN;
- xid_request4 = DHCP_OFF_XID;
- elapsed_request4 = DHCP_OFF_SECS;
- random_request4 = DHCP_OFF_CHADDR + 6;
- /* opcode */
- p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
- /* hardware address type */
- p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
- /* hardware address length */
- p[DHCP_OFF_HLEN] = 6;
- /* hops */
- p[DHCP_OFF_HOPS] = 1;
- /* gateway address */
- memcpy(p + DHCP_OFF_GIADDR,
- &((struct sockaddr_in *) &localaddr)->sin_addr,
- 4);
- /* hardware address */
- memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
- /* cookie */
- memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
- /* options */
- p += DHCP_OFF_OPTIONS;
- /* inline DHCP message type */
- *p++ = DHCP_OPT_DHCP_MSGTYPE;
- *p++ = 1;
- *p++ = DHCP_OP_REQUEST;
- /* place for DHCP server id (option) */
- serverid_request4 = p - template_request4;
- p += DHCP_OPTLEN_SRVID;
- /* place for DHCP requested IP address (address) */
- *p++ = DHCP_OPT_DHCP_ADDRESS;
- *p++ = 4;
- reqaddr_request4 = p - template_request4;
- p += 4;
- /* inline DHCP parameter request list (default) */
- *p++ = DHCP_OPT_DHCP_PRL;
- *p++ = 7;
- *p++ = DHCP_OPT_SUBNET_MASK;
- *p++ = DHCP_OPT_BROADCAST;
- *p++ = DHCP_OPT_TIME_OFFSET;
- *p++ = DHCP_OPT_ROUTERS;
- *p++ = DHCP_OPT_DOMAIN_NAME;
- *p++ = DHCP_OPT_DNS_SERVERS;
- *p++ = DHCP_OPT_HOST_NAME;
- /* end */
- *p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request4(void)
-{
- uint8_t *p = template_request4;
- int fd, cc, i, j;
-
- fd = open(templatefile[1], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(1);
- }
- if (cc < 100) {
- fprintf(stderr, "file '%s' too short\n", templatefile[1]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[1]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[1]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[1]);
- exit(2);
- }
- length_request4 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[1] >= 0)
- xid_request4 = (size_t) xidoffset[1];
- else
- xid_request4 = DHCP_OFF_XID;
- if (xid_request4 + 4 > length_request4) {
- fprintf(stderr,
- "xid (at %zu) outside the template (length %zu)?\n",
- xid_request4, length_request4);
- exit(2);
- }
- if (rndoffset[1] >= 0)
- random_request4 = (size_t) rndoffset[1];
- else
- random_request4 = DHCP_OFF_CHADDR + 6;
- if (random_request4 > length_request4) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_request4, length_request4);
- exit(2);
- }
- if (elpoffset >= 0)
- elapsed_request4 = (size_t) elpoffset;
- else
- elapsed_request4 = DHCP_OFF_SECS;
- if (elapsed_request4 + 2 > length_request4) {
- fprintf(stderr,
- "secs (at %zu) outside the template (length %zu)?\n",
- elapsed_request4, length_request4);
- exit(2);
- }
- serverid_request4 = (size_t) sidoffset;
- if (serverid_request4 + 6 > length_request4) {
- fprintf(stderr,
- "server-id option (at %zu) outside the template "
- "(length %zu)?\n",
- serverid_request4, length_request4);
- exit(2);
- }
- reqaddr_request4 = (size_t) ripoffset;
- if (reqaddr_request4 + 4 > length_request4) {
- fprintf(stderr,
- "requested-ip-address option (at %zu) outside "
- "the template (length %zu)?\n",
- reqaddr_request4, length_request4);
- exit(2);
- }
-}
-
-/*
- * send the DHCPv4 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the OFFER)
- */
-
-void
-send_request4(struct exchange *x0)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL) {
- locallimit++;
- perror("send2");
- return;
- }
-
- memcpy(x, x0, sizeof(*x));
- x->order2 = xscount2++;
- x->xid |= 1;
- hash = x->xid >> 1;
-
- ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
- hash &= hashsize2 - 1;
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_request4, length_request4);
- /* xid */
- memcpy(obuf + xid_request4, &x->xid, 4);
- /* random */
- randomize(random_request4, x->rnd);
- /* secs */
- if (elapsed_request4 > 0) {
- int secs;
-
- secs = x->ts1.tv_sec - x->ts0.tv_sec;
- if (x->ts1.tv_nsec < x->ts0.tv_nsec)
- secs += 1;
- if (secs > 0) {
- obuf[elapsed_request4] = secs >> 8;
- obuf[elapsed_request4 + 1] = secs & 0xff;
- }
- }
- /* server ID */
- memcpy(obuf + serverid_request4, x->sid, x->sidlen);
- /* requested IP address */
- memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
- if (ret < 0) {
- perror("clock_gettime(send2)");
- fatal = 1;
- return;
- }
- ret = sendto(sock, obuf, length_request4, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in));
- if (ret >= 0)
- return;
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) || (errno == ENOMEM))
- locallimit++;
- perror("send2");
-}
-
-/*
- * send the DHCPv4 DISCOVER first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send4(void)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL)
- return -ENOMEM;
-
- memset(x, 0, sizeof(*x));
- x->order0 = xscount0++;
- hash = x->rnd = (uint32_t) random();
- if (simple == 0)
- x->xid = hash << 1;
- else
- x->xid = hash;
-
- ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
- hash &= hashsize0 - 1;
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_discover4, length_discover4);
- /* xid */
- memcpy(obuf + xid_discover4, &x->xid, 4);
- /* random */
- x->rnd = randomize(random_discover4, x->rnd);
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &last);
- if (ret < 0) {
- perror("clock_gettime(send)");
- fatal = 1;
- return -errno;
- }
- x->ts0 = last;
- errno = 0;
- ret = sendto(sock, obuf, length_discover4, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in));
- if (ret == (ssize_t) length_discover4)
- return 0;
- return -errno;
-}
-
-/*
- * scan a DHCPv4 OFFER to get its server-id option
- */
-
-int
-scan_for_srvid4(struct exchange *x, size_t cc)
-{
- size_t off = DHCP_OFF_OPTIONS;
-
- for (;;) {
- if (off + DHCP_OPTLEN_SRVID > cc) {
- fprintf(stderr, "truncated\n");
- return -1;
- }
- if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
- break;
- if (ibuf[off] == DHCP_OPT_END) {
- fprintf(stderr, "server-id not found\n");
- return -1;
- }
- if (ibuf[off] == DHCP_OPT_PAD) {
- off++;
- continue;
- }
- off += 2 + ibuf[off + 1];
- }
- /* check length */
- if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
- fprintf(stderr,
- "bad server-id length (%hhu)\n",
- ibuf[off + 1]);
- return -1;
- }
- /* cache it in the global variables when required and not yet done */
- if ((usefirst != 0) && (gsrvid == NULL)) {
- memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
- gsrvid = gsrvidbuf;
- gsrvidlen = DHCP_OPTLEN_SRVID;
- }
- x->sid = ibuf + off;
- x->sidlen = DHCP_OPTLEN_SRVID;
- return 0;
-}
-
-/*
- * receive a DHCPv4 packet
- */
-
-void
-receive4(void)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- struct timespec now;
- ssize_t cc;
- uint32_t xid, hash;
- int checklost = 0;
- double delta;
-
- cc = recv(sock, ibuf, sizeof(ibuf), 0);
- if (cc < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == EINTR))
- return;
- perror("recv");
- fatal = 1;
- return;
- }
- /* enforce a reasonable length */
- if (cc < BOOTP_MIN_LEN) {
- tooshort++;
- return;
- }
- /* must be a BOOTP REPLY */
- if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
- return;
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(receive)");
- fatal = 1;
- return;
- }
- memcpy(&xid, ibuf + xid_discover4, 4);
- /* 4-packet exchange even/odd xid */
- if (simple == 0) {
- if ((xid & 1) != 0) {
- receive_reply(xid, &now);
- return;
- }
- hash = (xid >> 1) & (hashsize0 - 1);
- } else
- hash = xid & (hashsize0 - 1);
- /* now it is the second packet, get the bucket which is needed */
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext0 != NULL) && (xnext0->xid == xid)) {
- x = xnext0;
- goto found;
- }
- /* if the rate is not illimited, garbage collect up to 3
- timed-out exchanges */
- if (rate != 0)
- checklost = 3;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- /* check for a timed-out exchange */
- waited = now.tv_sec - x->ts0.tv_sec;
- waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (waited < losttime[0]) {
- checklost = 0;
- continue;
- }
- /* garbage collect timed-out exchange */
- checklost--;
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- free(x);
- collected[0] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount0++;
- x->ts1 = now;
- delta = x->ts1.tv_sec - x->ts0.tv_sec;
- delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (delta < dmin0)
- dmin0 = delta;
- if (delta > dmax0)
- dmax0 = delta;
- dsum0 += delta;
- dsumsq0 += delta * delta;
- xnext0 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
- /* if the exchange is not finished, go to the second part */
- if (simple == 0) {
- int ret = 0;
-
- /* the server-ID option is needed */
- if ((usefirst != 0) && (gsrvid != NULL)) {
- x->sid = gsrvid;
- x->sidlen = gsrvidlen;
- } else
- ret = scan_for_srvid4(x, cc);
- if (ret >= 0)
- send_request4(x);
- }
-}
-
-/*
- * get the DHCPv6 socket descriptor
- */
-
-void
-getsock6(void)
-{
- struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
- int ret;
-
- /* update local port */
- if (localport != 0) {
- uint16_t lp = htons((uint16_t) localport);
-
- ((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
- }
- sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- if (sock < 0) {
- perror("socket");
- exit(1);
- }
- ret = bind(sock,
- (struct sockaddr *) &localaddr,
- sizeof(struct sockaddr_in6));
- if (ret < 0) {
- perror("Failed to bind v6 socket to local-link address");
- exit(1);
- }
- /* perform the multicast stuff when the destination is multicast */
- if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
- int hops = 1;
-
- ret = setsockopt(sock,
- IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- &hops, sizeof(hops));
- if (ret < 0) {
- perror("setsockopt(IPV6_MULTICAST_HOPS)");
- exit(1);
- }
- }
- if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
- unsigned idx = if_nametoindex(localname);
-
- if (idx == 0) {
- fprintf(stderr,
- "if_nametoindex(%s) failed\n",
- localname);
- exit(1);
- }
- ret = setsockopt(sock,
- IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &idx, sizeof(idx));
- if (ret < 0) {
- perror("setsockopt(IPV6_MULTICAST_IF)");
- exit(1);
- }
- }
-}
-
-/*
- * build a DHCPv6 SOLICIT template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_solicit6(void)
-{
- uint8_t *p = template_solicit6;
-
- xid_solicit6 = DHCP6_OFF_XID;
- /* message type */
- p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
- /* options */
- p += DHCP6_OFF_OPTIONS;
- /* elapsed time */
- p[1] = DHCP6_OPT_ELAPSED_TIME;
- p[3] = 2;
- p += 6;
- /* rapid commit */
- if (rapidcommit != 0) {
- p[1] = DHCP6_OPT_RAPID_COMMIT;
- p += 4;
- }
- /* client ID */
- p[1] = DHCP6_OPT_CLIENTID;
- p[3] = duid_length;
- memcpy(p + 4, duid_prefix, duid_length);
- p += 4 + duid_length;
- random_solicit6 = p - template_solicit6;
- /* option request option */
- p[1] = DHCP6_OPT_ORO;
- p[3] = 4;
- p[5] = DHCP6_OPT_NAME_SERVERS;
- p[7] = DHCP6_OPT_DOMAIN_SEARCH;
- p += 8;
- /* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
- p[1] = DHCP6_OPT_IA_NA;
- p[3] = 12;
- p[7] = 1;
- p[10] = 3600 >> 8;
- p[11] = 3600 & 0xff;
- p[14] = 5400 >> 8;
- p[15] = 5400 & 0xff;
- p += 16;
- /* set length */
- length_solicit6 = p - template_solicit6;
-}
-
-/*
- * get a DHCPv6 first packet (usually a SOLICIT) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_solicit6(void)
-{
- uint8_t *p = template_solicit6;
- int fd, cc, i, j;
-
- fd = open(templatefile[0], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[0], strerror(errno));
- exit(1);
- }
- if (cc < 10) {
- fprintf(stderr, "file '%s' too short\n", templatefile[0]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[0]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[0]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[0]);
- exit(2);
- }
- length_solicit6 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[0] >= 0)
- xid_solicit6 = (size_t) xidoffset[0];
- else
- xid_solicit6 = DHCP6_OFF_XID;
- if (xid_solicit6 + 3 > length_solicit6) {
- fprintf(stderr,
- "xid (at %zu) is outside the template (length %zu)?\n",
- xid_solicit6, length_solicit6);
- exit(2);
- }
- if (rndoffset[0] >= 0)
- random_solicit6 = (size_t) rndoffset[0];
- else
- random_solicit6 = 0;
- if (random_solicit6 > length_solicit6) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_solicit6, length_solicit6);
- exit(2);
- }
-}
-
-/*
- * build a DHCPv6 REQUEST template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_request6(void)
-{
- uint8_t *p = template_request6;
-
- xid_request6 = DHCP6_OFF_XID;
- /* message type */
- p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
- /* options */
- p += DHCP6_OFF_OPTIONS;
- /* elapsed time */
- p[1] = DHCP6_OPT_ELAPSED_TIME;
- p[3] = 2;
- p += 4;
- elapsed_request6 = p - template_request6;
- p += 2;
- /* client ID */
- p[1] = DHCP6_OPT_CLIENTID;
- p[3] = duid_length;
- memcpy(p + 4, duid_prefix, duid_length);
- p += 4 + duid_length;
- random_request6 = p - template_request6;
- /* option request option */
- p[1] = DHCP6_OPT_ORO;
- p[3] = 4;
- p[5] = DHCP6_OPT_NAME_SERVERS;
- p[7] = DHCP6_OPT_DOMAIN_SEARCH;
- p += 8;
- /* server ID and IA_NA */
- serverid_request6 = p - template_request6;
- reqaddr_request6 = p - template_request6;
- /* set length */
- length_request6 = p - template_request6;
-}
-
-/*
- * get a DHCPv6 third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request6(void)
-{
- uint8_t *p = template_request6;
- int fd, cc, i, j;
-
- fd = open(templatefile[1], O_RDONLY);
- if (fd < 0) {
- fprintf(stderr, "open(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(2);
- }
- cc = read(fd, tbuf, sizeof(tbuf));
- (void) close(fd);
- if (cc < 0) {
- fprintf(stderr, "read(%s): %s\n",
- templatefile[1], strerror(errno));
- exit(1);
- }
- if (cc < 10) {
- fprintf(stderr, "file '%s' too short\n", templatefile[1]);
- exit(2);
- }
- if (cc > 8193) {
- fprintf(stderr,"file '%s' too large\n", templatefile[1]);
- exit(2);
- }
- j = 0;
- for (i = 0; i < cc; i++) {
- if (isspace((int) tbuf[i]))
- continue;
- if (!isxdigit((int) tbuf[i])) {
- fprintf(stderr,
- "illegal char[%d]='%c' in file '%s'\n",
- i, (int) tbuf[i], templatefile[1]);
- exit(2);
- }
- tbuf[j] = tbuf[i];
- j++;
- }
- cc = j;
- if ((cc & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in file '%s'\n",
- templatefile[1]);
- exit(2);
- }
- length_request6 = cc >> 1;
- for (i = 0; i < cc; i += 2)
- (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
- if (xidoffset[1] >= 0)
- xid_request6 = (size_t) xidoffset[1];
- else
- xid_request6 = DHCP6_OFF_XID;
- if (xid_request6 + 3 > length_request6) {
- fprintf(stderr,
- "xid (at %zu) is outside the template (length %zu)?\n",
- xid_request6, length_request6);
- exit(2);
- }
- if (rndoffset[1] >= 0)
- random_request6 = (size_t) rndoffset[1];
- else
- random_request6 = 0;
- if (random_request6 > length_request6) {
- fprintf(stderr,
- "random (at %zu) outside the template (length %zu)?\n",
- random_request6, length_request6);
- exit(2);
- }
- if (elpoffset >= 0)
- elapsed_request6 = (size_t) elpoffset;
- if (elapsed_request6 + 2 > length_request6) {
- fprintf(stderr,
- "secs (at %zu) outside the template (length %zu)?\n",
- elapsed_request6, length_request6);
- exit(2);
- }
- serverid_request6 = (size_t) sidoffset;
- if (serverid_request6 > length_request6) {
- fprintf(stderr,
- "server-id option (at %zu) outside the template "
- "(length %zu)?\n",
- serverid_request6, length_request6);
- exit(2);
- }
- reqaddr_request6 = (size_t) ripoffset;
- if (reqaddr_request6 > length_request6) {
- fprintf(stderr,
- "requested-ip-address option (at %zu) outside "
- "the template (length %zu)?\n",
- reqaddr_request6, length_request6);
- exit(2);
- }
-}
-
-/*
- * send the DHCPv6 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the ADVERTISE)
- */
-
-void
-send_request6(struct exchange *x0)
-{
- struct exchange *x;
- struct xlist *bucket;
- size_t len;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL) {
- locallimit++;
- perror("send2");
- return;
- }
-
- memcpy(x, x0, sizeof(*x));
- x->order2 = xscount2++;
- x->xid |= 1;
- hash = x->xid >> 1;
-
- ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
- hash &= hashsize2 - 1;
- bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- len = length_request6;
- memcpy(obuf, template_request6, len);
- /* xid */
- obuf[xid_request6] = x->xid >> 16;
- obuf[xid_request6 + 1] = x->xid >> 8;
- obuf[xid_request6 + 2] = x->xid;
- /* random */
- randomize(random_request6, x->rnd);
- /* elapsed time */
- if (elapsed_request6 > 0) {
- int et;
-
- et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
- et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
- if (et > 65535) {
- obuf[elapsed_request6] = 0xff;
- obuf[elapsed_request6 + 1] = 0xff;
- } else if (et > 0) {
- obuf[elapsed_request6] = et >> 8;
- obuf[elapsed_request6 + 1] = et & 0xff;
- }
- }
- /* server ID */
- if (serverid_request6 < length_request6)
- memmove(obuf + serverid_request6 + x->sidlen,
- obuf + serverid_request6,
- x->sidlen);
- memcpy(obuf + serverid_request6, x->sid, x->sidlen);
- len += x->sidlen;
- /* IA_NA */
- if (reqaddr_request6 < serverid_request6) {
- memmove(obuf + reqaddr_request6 + x->ianalen,
- obuf + reqaddr_request6,
- x->ianalen);
- memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
- } else if (reqaddr_request6 < length_request6) {
- memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
- obuf + reqaddr_request6 + x->sidlen,
- x->ianalen);
- memcpy(obuf + reqaddr_request6+ x->sidlen,
- x->iana,
- x->ianalen);
- } else
- memcpy(obuf + len, x->iana, x->ianalen);
- len += x->ianalen;
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
- if (ret < 0) {
- perror("clock_gettime(send2)");
- fatal = 1;
- return;
- }
- ret = sendto(sock, obuf, len, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in6));
- if (ret >= 0)
- return;
- if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) || (errno == ENOMEM))
- locallimit++;
- perror("send2");
-}
-
-/*
- * send the DHCPv6 SOLICIT first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send6(void)
-{
- struct exchange *x;
- struct xlist *bucket;
- uint32_t hash;
- ssize_t ret;
-
- x = (struct exchange *) malloc(sizeof(*x));
- if (x == NULL)
- return -ENOMEM;
-
- memset(x, 0, sizeof(*x));
- x->order0 = xscount0++;
- hash = x->rnd = (uint32_t) random();
- if (simple == 0)
- x->xid = (hash << 1) & 0x00ffffff;
- else
- x->xid = hash & 0x00ffffff;
-
- ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
- hash &= hashsize0 - 1;
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
- memcpy(obuf, template_solicit6, length_solicit6);
- /* xid */
- obuf[xid_solicit6] = x->xid >> 16;
- obuf[xid_solicit6 + 1] = x->xid >> 8;
- obuf[xid_solicit6 + 2] = x->xid;
- /* random */
- x->rnd = randomize(random_solicit6, x->rnd);
-
- /* timestamp */
- ret = clock_gettime(CLOCK_REALTIME, &last);
- if (ret < 0) {
- perror("clock_gettime(send)");
- fatal = 1;
- return -errno;
- }
- x->ts0 = last;
- errno = 0;
- ret = sendto(sock, obuf, length_solicit6, 0,
- (struct sockaddr *) &serveraddr,
- sizeof(struct sockaddr_in6));
- if (ret == (ssize_t) length_solicit6)
- return 0;
- return -errno;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its server-id option
- */
-
-int
-scan_for_srvid6(struct exchange *x, size_t cc)
-{
- size_t off = DHCP6_OFF_OPTIONS, len;
-
- for (;;) {
- if (off + 4 > cc) {
- fprintf(stderr, "server-id not found\n");
- return -1;
- }
- if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
- break;
- off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- }
- len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- /* cache it in the global variables when required and not yet done */
- if ((usefirst != 0) && (gsrvid == NULL)) {
- if (len <= sizeof(gsrvidbuf)) {
- memcpy(gsrvidbuf, ibuf + off, len);
- gsrvid = gsrvidbuf;
- gsrvidlen = len;
- } else {
- gsrvid = (uint8_t *) malloc(len);
- if (gsrvid == NULL) {
- perror("malloc(gsrvid");
- return -1;
- }
- memcpy(gsrvid, ibuf + off, len);
- gsrvidlen = len;
- }
- }
- x->sid = ibuf + off;
- x->sidlen = len;
- return 0;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its IA_NA option
- * (TODO: check for errors)
- */
-
-int
-scan_for_ia_na(struct exchange *x, size_t cc)
-{
- size_t off = DHCP6_OFF_OPTIONS, len;
-
- for (;;) {
- if (off + 4 > cc) {
- fprintf(stderr, "ia-na not found\n");
- return -1;
- }
- if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
- break;
- off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- }
- len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
- x->iana = ibuf + off;
- x->ianalen = len;
- return 0;
-}
-
-/*
- * receive a DHCPv6 packet
- */
-
-void
-receive6(void)
-{
- struct exchange *x, *t;
- struct xlist *bucket;
- struct timespec now;
- ssize_t cc;
- uint32_t xid, hash;
- int checklost = 0;
- double delta;
-
- cc = recv(sock, ibuf, sizeof(ibuf), 0);
- if (cc < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == EINTR))
- return;
- perror("recv");
- fatal = 1;
- return;
- }
- /* enforce a reasonable length */
- if (cc < 22) {
- tooshort++;
- return;
- }
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(receive)");
- fatal = 1;
- return;
- }
- xid = ibuf[xid_solicit6] << 16;
- xid |= ibuf[xid_solicit6 + 1] << 8;
- xid |= ibuf[xid_solicit6 + 2];
- /* 4-packet exchange even/odd xid */
- if (simple == 0) {
- if ((xid & 1) != 0) {
- receive_reply(xid, &now);
- return;
- }
- hash = (xid >> 1) & (hashsize0 - 1);
- } else
- hash = xid & (hashsize0 - 1);
- /* now it is the second packet, get the bucket which is needed */
- bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
- /* try the 'next to be received' cache */
- if ((xnext0 != NULL) && (xnext0->xid == xid)) {
- x = xnext0;
- goto found;
- }
- /* if the rate is not illimited, garbage collect up to 3
- timed-out exchanges */
- if (rate != 0)
- checklost = 3;
- /* look for the exchange */
- ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
- double waited;
-
- if (x->xid == xid)
- goto found;
- if (checklost <= 0)
- continue;
- /* check for a timed-out exchange */
- waited = now.tv_sec - x->ts0.tv_sec;
- waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (waited < losttime[0]) {
- checklost = 0;
- continue;
- }
- /* garbage collect timed-out exchange */
- checklost--;
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- free(x);
- collected[0] += 1;
- }
- /* no match? very late or not for us */
- orphans++;
- return;
-
- /* got it: update stats and move to the received queue */
- found:
- xrcount0++;
- x->ts1 = now;
- delta = x->ts1.tv_sec - x->ts0.tv_sec;
- delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
- if (delta < dmin0)
- dmin0 = delta;
- if (delta > dmax0)
- dmax0 = delta;
- dsum0 += delta;
- dsumsq0 += delta * delta;
- xnext0 = ISC_TAILQ_NEXT(x, gchain);
- ISC_TAILQ_REMOVE(bucket, x, hchain);
- ISC_TAILQ_REMOVE(&xsent0, x, gchain);
- ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
- /* if the exchange is not finished, go to the second part */
- if (simple == 0) {
- int ret = 0;
-
- /* the server-ID option is needed */
- if ((usefirst != 0) && (gsrvid != NULL)) {
- x->sid = gsrvid;
- x->sidlen = gsrvidlen;
- } else
- ret = scan_for_srvid6(x, cc);
- /* and the IA_NA option too */
- if (ret >= 0)
- ret = scan_for_ia_na(x, cc);
- if (ret >= 0)
- send_request6(x);
- }
-}
-
-/*
- * decode a base command line parameter
- * (currently only MAC address and DUID are supported)
- */
-
-void
-decodebase(void)
-{
- char *b0 = base[basecnt];
-
- /* MAC address (alias Ethernet address) */
- if ((strncasecmp(b0, "mac=", 4) == 0) ||
- (strncasecmp(b0, "ether=", 6) == 0)) {
- char *b;
-
- b = strchr(b0, '=') + 1;
- if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
- &mac_prefix[0], &mac_prefix[1],
- &mac_prefix[2], &mac_prefix[3],
- &mac_prefix[4], &mac_prefix[5]) == 6) {
- if ((diags != NULL) && (strchr(diags, 'a') != NULL))
- printf("set MAC to %s\n", b);
- return;
- }
- fprintf(stderr,
- "expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
- (int) (b - b0 - 1), b0);
- exit(2);
- }
- /* DUID */
- if (strncasecmp(b0, "duid=", 5) == 0) {
- char *b;
- size_t i, l;
-
- if (duid_prefix != NULL) {
- fprintf(stderr, "duid was already set?\n");
- exit(2);
- }
- b = strchr(b0, '=') + 1;
- l = 0;
- while (*b != '\0') {
- if (!isxdigit((int) *b)) {
- fprintf(stderr,
- "illegal char '%c' in duid\n",
- (int) *b);
- exit(2);
- }
- b++;
- l++;
- }
- if ((l & 1) != 0) {
- fprintf(stderr,
- "odd number of hexadecimal digits in duid\n");
- exit(2);
- }
- l /= 2;
- if (l > 64) {
- fprintf(stderr, "duid too large\n");
- exit(2);
- }
- duid_prefix = (uint8_t *) malloc(l);
- if (duid_prefix == NULL) {
- perror("malloc(duid)");
- exit(1);
- }
- b = strchr(b0, '=') + 1;
- for (i = 0; i < l; i++, b += 2)
- (void) sscanf(b, "%02hhx", &duid_prefix[i]);
- duid_length = l;
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- b = strchr(b0, '=') + 1;
- printf("set DUID[%d] to %s\n",
- duid_length, b);
- }
- return;
- }
- /* other */
- fprintf(stderr, "not yet supported base '%s'\n", b0);
- exit(2);
-}
-
-/*
- * get the server socket address from the command line:
- * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
- */
-
-void
-getserveraddr(const int flags)
-{
- struct addrinfo hints, *res;
- char *service;
- int ret;
-
- memset(&hints, 0, sizeof(hints));
- if (ipversion == 4) {
- hints.ai_family = AF_INET;
- service = "67";
- } else {
- hints.ai_family = AF_INET6;
- service = "547";
- }
- hints.ai_socktype = SOCK_DGRAM;
-
- hints.ai_flags = AI_NUMERICSERV | flags;
-#if defined(AI_ADDRCONFIG)
- hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- hints.ai_protocol = IPPROTO_UDP;
-
- ret = getaddrinfo(servername, service, &hints, &res);
- if (ret != 0) {
- fprintf(stderr, "bad server=%s: %s\n",
- servername, gai_strerror(ret));
- exit(2);
- }
- if (res->ai_next != NULL) {
- fprintf(stderr, "ambiguous server=%s\n", servername);
- exit(2);
- }
- memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
-}
-
-/*
- * get the interface from the command line:
- * - name of the interface
- * - socket address to fill
- * (in IPv6, get the first link-local address)
- */
-
-void
-getinterface(const char *name, struct sockaddr_storage *ss)
-{
- struct ifaddrs *ifaddr, *ifa;
- int ret;
- size_t len;
-
- ret = getifaddrs(&ifaddr);
- if (ret < 0) {
- perror("getifaddrs");
- exit(1);
- }
-
- for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_name == NULL)
- continue;
- if (strcmp(ifa->ifa_name, name) != 0)
- continue;
- if ((ifa->ifa_flags & IFF_UP) == 0)
- continue;
- if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
- continue;
- if (ifa->ifa_addr == NULL)
- continue;
- if ((ipversion == 4) &&
- (ifa->ifa_addr->sa_family != AF_INET))
- continue;
- if (ipversion == 6) {
- struct sockaddr_in6 *a6;
-
- a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
- if (a6->sin6_family != AF_INET6)
- continue;
- if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
- continue;
- }
- if (ipversion == 4)
- len = sizeof(struct sockaddr_in);
- else
- len = sizeof(struct sockaddr_in6);
- memcpy(ss, ifa->ifa_addr, len);
- /* fill the server port */
- if (ipversion == 4)
- ((struct sockaddr_in *) ss)->sin_port = htons(67);
- else
- ((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
- return;
- }
- fprintf(stderr, "can't find interface %s\n", name);
- exit(1);
-}
-
-/*
- * get the local socket address from the command line
- * (if it doesn't work, try an interface name)
- */
-
-void
-getlocaladdr(void)
-{
- struct addrinfo hints, *res;
- char *service;
- int ret;
-
- memset(&hints, 0, sizeof(hints));
- if (ipversion == 4) {
- hints.ai_family = AF_INET;
- service = "67";
- } else {
- hints.ai_family = AF_INET6;
- service = "546";
- }
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_NUMERICSERV;
-#if defined(AI_ADDRCONFIG)
- hints.ai_flags |= AI_ADDRCONFIG;
-#endif
- hints.ai_protocol = IPPROTO_UDP;
-
- ret = getaddrinfo(localname, service, &hints, &res);
- if ((ret == EAI_NONAME)
-#ifdef EAI_NODATA
- || (ret == EAI_NODATA)
-#endif
- ) {
- isinterface = 1;
- getinterface(localname, &localaddr);
- return;
- }
- if (ret != 0) {
- fprintf(stderr,
- "bad -l<local-addr=%s>: %s\n",
- localname,
- gai_strerror(ret));
- exit(2);
- }
- /* refuse multiple addresses */
- if (res->ai_next != NULL) {
- fprintf(stderr,
- "ambiguous -l<local-addr=%s>\n",
- localname);
- exit(2);
- }
- memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
- freeaddrinfo(res);
- return;
-}
-
-/*
- * get the local socket address from the server one
- */
-
-void
-getlocal(void)
-{
- int ret, s;
- socklen_t l;
-
- if (ipversion == 4) {
- l = sizeof(struct sockaddr_in);
- s = socket(PF_INET, SOCK_DGRAM, 0);
- } else {
- l = sizeof(struct sockaddr_in6);
- s = socket(PF_INET6, SOCK_DGRAM, 0);
- }
- if (s < 0) {
- perror("socket");
- exit(1);
- }
- if ((ipversion == 4) && (isbroadcast != 0)) {
- int on = 1;
-
- ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
- if (ret < 0) {
- perror("setsockopt(SO_BROADCAST)");
- exit(1);
- }
- }
- ret = connect(s, (struct sockaddr *) &serveraddr, l);
- if (ret < 0) {
- perror("connect");
- exit(1);
- }
- l = sizeof(localaddr);
- ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
- if (ret < 0) {
- perror("getsockname");
- exit(1);
- }
- (void) close(s);
- /* fill the local port */
- if (ipversion == 4)
- ((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
- else
- ((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
-}
-
-/*
- * intermediate reporting
- * (note: an in-transit packet can be reported as dropped)
- */
-
-void
-reporting(void)
-{
- dreport.tv_sec += report;
-
- if (xscount2 == 0) {
- printf("sent: %llu, received: %llu (drops: %lld)",
- (unsigned long long) xscount0,
- (unsigned long long) xrcount0,
- (long long) (xscount0 - xrcount0));
- if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
- double avg;
-
- avg = dsum0 / xrcount0;
- printf(" average: %.3f ms", avg * 1e3);
- }
- } else {
- printf("sent: %llu/%llu received: %llu/%llu "
- "(drops: %lld/%lld)",
- (unsigned long long) xscount0,
- (unsigned long long) xscount2,
- (unsigned long long) xrcount0,
- (unsigned long long) xrcount2,
- (long long) (xscount0 - xrcount0),
- (long long) (xscount2 - xrcount2));
- if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
- double avg0, avg2;
-
- avg0 = dsum0 / xrcount0;
- if (xrcount2 != 0)
- avg2 = dsum2 / xrcount2;
- else
- avg2 = 0.;
- printf(" average: %.3f/%.3f ms",
- avg0 * 1e3, avg2 * 1e3);
- }
- }
- printf("\n");
-}
-
-/*
- * SIGCHLD handler
- */
-
-void
-reapchild(int sig)
-{
- // Do something with variables to avoid unused-variable warning
- int status;
- status = sig;
- status = 0;
- while (wait3(&status, WNOHANG, NULL) > 0)
- /* continue */;
-}
-
-/*
- * SIGINT handler
- */
-
-void
-interrupt(int sig)
-{
- // Do something to avoid unused-variable warning
- int s = sig;
- s = 1;
- interrupted = s;
-}
-
-/*
- * '-v' handler
- */
-
-void
-version(void)
-{
- fprintf(stderr, "version 0.01\n");
-}
-
-/*
- * usage (from the wiki)
- */
-
-void
-usage(void)
-{
- fprintf(stderr, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-" [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\f\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact. For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-" control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
-" clients. This can be specified multiple times, each instance is\n"
-" in the <type>=<value> form, for instance:\n"
-" (and default) MAC=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-" having been lost. The value is given in seconds and may contain a\n"
-" fractional component. The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-" elapsed-time option in the (second/request) template.\n"
-" The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-" whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-" option / (DHCPv6) IA_NA option in the (second/request) template.\n"
-"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
-" hostname/address to use when communicating with the server. By\n"
-" default, the interface address through which traffic would\n"
-" normally be routed to the server is used.\n"
-" For DHCPv6 operation, specify the name of the network interface\n"
-" via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-" (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-" exchanges per second. A periodic report is generated showing the\n"
-" number of exchanges which were not completed, as well as the\n"
-" average response latency. The program continues until\n"
-" interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-" (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-" (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-" as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-" the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-" <diagnostic-selector> is a string of single-keywords specifying\n"
-" the operations for which verbose output is desired. The selector\n"
-" keyletters are:\n"
-" * 'a': print the decoded command line arguments\n"
-" * 'e': print the exit reason\n"
-" * 'i': print rate processing details\n"
-" * 'r': print randomization details\n"
-" * 's': print first server-id\n"
-" * 't': when finished, print timers of all successful exchanges\n"
-" * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-" been dropped. Use -D0 to abort if even a single request has been\n"
-" dropped. If <max-drop> includes the suffix '%', it specifies a\n"
-" maximum percentage of requests that may be dropped before abort.\n"
-" In this case, testing of the threshold begins after 10 requests\n"
-" have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions. No report is\n"
-" generated until all transactions have been initiated/waited-for,\n"
-" after which a report is generated and the program terminates.\n"
-"-p<test-period>: Send requests for the given test period, which is\n"
-" specified in the same manner as -d. This can be used as an\n"
-" alternative to -n, or both options can be given, in which case the\n"
-" testing is completed when either limit is reached.\n"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-" (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-" exchanges are not successfully completed.\n");
-}
-
-/*
- * main function / entry point
- */
-
-int
-main(const int argc, char * const argv[])
-{
- int opt, flags = 0, ret, i;
- long long r;
- char *pc;
- extern char *optarg;
- extern int optind;
-
-#define OPTIONS "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
-
- /* decode options */
- while ((opt = getopt(argc, argv, OPTIONS)) != -1)
- switch (opt) {
- case 'h':
- usage();
- exit(0);
-
- case 'v':
- version();
- exit(0);
-
- case '4':
- if (ipversion == 6) {
- fprintf(stderr, "IP version already set to 6\n");
- usage();
- exit(2);
- }
- ipversion = 4;
- break;
-
- case '6':
- if (ipversion == 4) {
- fprintf(stderr, "IP version already set to 4\n");
- usage();
- exit(2);
- }
- ipversion = 6;
- break;
-
- case 'r':
- rate = atoi(optarg);
- if (rate <= 0) {
- fprintf(stderr, "rate must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 't':
- report = atoi(optarg);
- if (report <= 0) {
- fprintf(stderr, "report must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'R':
- r = atoll(optarg);
- if (r < 0) {
- fprintf(stderr,
- "range must not be a negative integer\n");
- usage();
- exit(2);
- }
- range = (uint32_t) r;
- if ((range != 0) && (range != UINT32_MAX)) {
- uint32_t s = range + 1;
- uint64_t b = UINT32_MAX + 1, m;
-
- m = (b / s) * s;
- if (m == b)
- maxrandom = 0;
- else
- maxrandom = (uint32_t) m;
- }
- break;
-
- case 'b':
- if (basecnt > 3) {
- fprintf(stderr, "too many bases\n");
- usage();
- exit(2);
- }
- base[basecnt] = optarg;
- decodebase();
- basecnt++;
- break;
-
- case 'n':
- numreq[gotnumreq] = atoi(optarg);
- if (numreq[gotnumreq] <= 0) {
- fprintf(stderr,
- "num-request must be a positive integer\n");
- usage();
- exit(2);
- }
- gotnumreq = 1;
- break;
-
- case 'p':
- period = atoi(optarg);
- if (period <= 0) {
- fprintf(stderr,
- "test-period must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'd':
- losttime[gotlosttime] = atof(optarg);
- if (losttime[gotlosttime] <= 0.) {
- fprintf(stderr,
- "drop-time must be a positive number\n");
- usage();
- exit(2);
- }
- gotlosttime = 1;
- break;
-
- case 'D':
- pc = strchr(optarg, '%');
- if (pc != NULL) {
- *pc = '\0';
- maxpdrop[gotmaxdrop] = atof(optarg);
- if ((maxpdrop[gotmaxdrop] <= 0) ||
- (maxpdrop[gotmaxdrop] >= 100)) {
- fprintf(stderr,
- "invalid drop-time percentage\n");
- usage();
- exit(2);
- }
- gotmaxdrop = 1;
- break;
- }
- maxdrop[gotmaxdrop] = atoi(optarg);
- if (maxdrop[gotmaxdrop] <= 0) {
- fprintf(stderr,
- "max-drop must be a positive integer\n");
- usage();
- exit(2);
- }
- gotmaxdrop = 1;
- break;
-
- case 'l':
- localname = optarg;
- break;
-
- case 'P':
- preload = atoi(optarg);
- if (preload < 0) {
- fprintf(stderr,
- "preload must not be a negative integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'a':
- aggressivity = atoi(optarg);
- if (aggressivity <= 0) {
- fprintf(stderr,
- "aggressivity must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'L':
- localport = atoi(optarg);
- if (localport < 0) {
- fprintf(stderr,
- "local-port must not be a negative integer\n");
- usage();
- exit(2);
- }
- if (localport > (int) UINT16_MAX) {
- fprintf(stderr,
- "local-port must be lower than %d\n",
- (int) UINT16_MAX);
- usage();
- exit(2);
- }
- break;
-
- case 's':
- seeded = 1;
- seed = (unsigned int) atol(optarg);
- break;
-
- case 'i':
- simple = 1;
- break;
-
- case 'B':
- isbroadcast = 1;
- break;
-
- case 'c':
- rapidcommit = 1;
- break;
-
- case '1':
- usefirst = 1;
- break;
-
- case 'T':
- if (templatefile[0] != NULL) {
- if (templatefile[1] != NULL) {
- fprintf(stderr,
- "template-files are already set\n");
- usage();
- exit(2);
- }
- templatefile[1] = optarg;
- } else
- templatefile[0] = optarg;
- break;
-
- case 'X':
- if (xidoffset[0] >= 0)
- i = 1;
- else
- i = 0;
- xidoffset[i] = atoi(optarg);
- if (xidoffset[i] <= 0) {
- fprintf(stderr,
- "xid-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'O':
- if (rndoffset[0] >= 0)
- i = 1;
- else
- i = 0;
- rndoffset[i] = atoi(optarg);
- if (rndoffset[i] < 3) {
- fprintf(stderr,
- "random-offset must be greater than 3\n");
- usage();
- exit(2);
- }
- break;
-
- case 'E':
- elpoffset = atoi(optarg);
- if (elpoffset < 0) {
- fprintf(stderr,
- "time-offset must not be a "
- "negative integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'S':
- sidoffset = atoi(optarg);
- if (sidoffset <= 0) {
- fprintf(stderr,
- "srvid-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'I':
- ripoffset = atoi(optarg);
- if (ripoffset <= 0) {
- fprintf(stderr,
- "ip-offset must be a positive integer\n");
- usage();
- exit(2);
- }
- break;
-
- case 'x':
- diags = optarg;
- break;
-
- case 'w':
- wrapped = optarg;
- break;
-
- default:
- usage();
- exit(2);
- }
-
- /* adjust some global variables */
- if (ipversion == 0)
- ipversion = 4;
- if (templatefile[1] != NULL) {
- if (xidoffset[1] < 0)
- xidoffset[1] = xidoffset[0];
- if (rndoffset[1] < 0)
- rndoffset[1] = rndoffset[0];
- }
-
- /* when required, print the internal view of the command line */
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- printf("IPv%d", ipversion);
- if (simple != 0) {
- if (ipversion == 4)
- printf(" DO only");
- else
- printf(" SA only");
- }
- if (rate != 0)
- printf(" rate=%d", rate);
- if (report != 0)
- printf(" report=%d", report);
- if (range != 0) {
- if (strchr(diags, 'r') != NULL)
- printf(" range=0..%d [0x%x]",
- range,
- (unsigned int) maxrandom);
- else
- printf(" range=0..%d", range);
- }
- if (basecnt != 0)
- for (i = 0; i < basecnt; i++)
- printf(" base[%d]='%s'", i, base[i]);
- if (gotnumreq != 0)
- printf(" num-request=%d,%d", numreq[0], numreq[1]);
- if (period != 0)
- printf(" test-period=%d", period);
- printf(" drop-time=%g,%g", losttime[0], losttime[1]);
- if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
- printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
- if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
- printf(" max-drop=%2.2f%%,%2.2f%%",
- maxpdrop[0], maxpdrop[1]);
- if (preload != 0)
- printf(" preload=%d", preload);
- printf(" aggressivity=%d", aggressivity);
- if (localport != 0)
- printf(" local-port=%d", localport);
- if (seeded)
- printf(" seed=%u", seed);
- if (isbroadcast != 0)
- printf(" broadcast");
- if (rapidcommit != 0)
- printf(" rapid-commit");
- if (usefirst != 0)
- printf(" use-first");
- if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
- printf(" template-file='%s'", templatefile[0]);
- else if (templatefile[1] != NULL)
- printf(" template-file='%s','%s'",
- templatefile[0], templatefile[1]);
- if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
- printf(" xid-offset=%d", xidoffset[0]);
- else if (xidoffset[1] >= 0)
- printf(" xid-offset=%d,%d",
- xidoffset[0], xidoffset[1]);
- if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
- printf(" xid-offset=%d", rndoffset[0]);
- else if (rndoffset[1] >= 0)
- printf(" xid-offset=%d,%d",
- rndoffset[0], rndoffset[1]);
- if (elpoffset >= 0)
- printf(" time-offset=%d", elpoffset);
- if (sidoffset >= 0)
- printf(" srvid-offset=%d", sidoffset);
- if (ripoffset >= 0)
- printf(" ip-offset=%d", ripoffset);
- printf(" diagnotic-selectors='%s'", diags);
- if (wrapped != NULL)
- printf(" wrapped='%s'", wrapped);
- printf("\n");
- }
-
- /* check DHCPv4 only options */
- if ((ipversion != 4) && (isbroadcast != 0)) {
- fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
- usage();
- exit(2);
- }
-
- /* check DHCPv6 only options */
- if ((ipversion != 6) && (rapidcommit != 0)) {
- fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
- usage();
- exit(2);
- }
-
- /* check 4-packet (aka not simple) mode options */
- if ((simple != 0) && (numreq[1] != 0)) {
- fprintf(stderr,
- "second -n<num-request> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (losttime[1] != 1.)) {
- fprintf(stderr,
- "second -d<drop-time> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) &&
- ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
- fprintf(stderr,
- "second -D<max-drop> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (usefirst != 0)) {
- fprintf(stderr,
- "-1 is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (templatefile[1] != NULL)) {
- fprintf(stderr,
- "second -T<template-file> is not "
- "compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (xidoffset[1] >= 0)) {
- fprintf(stderr,
- "second -X<xid-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (rndoffset[1] >= 0)) {
- fprintf(stderr,
- "second -O<random-offset is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (elpoffset >= 0)) {
- fprintf(stderr,
- "-E<time-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (sidoffset >= 0)) {
- fprintf(stderr,
- "-S<srvid-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
- if ((simple != 0) && (ripoffset >= 0)) {
- fprintf(stderr,
- "-I<ip-offset> is not compatible with -i\n");
- usage();
- exit(2);
- }
-
-
- /* check simple mode options */
- if ((simple == 0) && (rapidcommit != 0)) {
- fprintf(stderr, "-i must be set to use -c\n");
- usage();
- exit(2);
- }
-
- /* check rate '-r' options */
- if ((rate == 0) && (report != 0)) {
- fprintf(stderr,
- "-r<rate> must be set to use -t<report>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
- fprintf(stderr,
- "-r<rate> must be set to use -n<num-request>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) && (period != 0)) {
- fprintf(stderr,
- "-r<rate> must be set to use -p<test-period>\n");
- usage();
- exit(2);
- }
- if ((rate == 0) &&
- ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
- (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
- fprintf(stderr,
- "-r<rate> must be set to use -D<max-drop>\n");
- usage();
- exit(2);
- }
-
- /* check (first) template file options */
- if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
- fprintf(stderr,
- "-T<template-file> must be set to "
- "use -X<xid-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
- fprintf(stderr,
- "-T<template-file> must be set to "
- "use -O<random-offset>\n");
- usage();
- exit(2);
- }
-
- /* check (second) template file options */
- if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -E<time-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -S<srvid-offset>\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
- fprintf(stderr,
- "second/request -T<template-file> must be set to "
- "use -I<ip-offset>\n");
- usage();
- exit(2);
- }
-
- /* check various template file(s) and other condition(s) options */
- if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
- fprintf(stderr,
- "-O<random-offset> must be set when "
- "-T<template-file> and -R<range> are used\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] != NULL) && (sidoffset < 0)) {
- fprintf(stderr,
- "-S<srvid-offset> must be set when second "
- "-T<template-file> is used\n");
- usage();
- exit(2);
- }
- if ((templatefile[1] != NULL) && (ripoffset < 0)) {
- fprintf(stderr,
- "-I<ip-offset> must be set when second "
- "-T<template-file> is used\n");
- usage();
- exit(2);
- }
-
- /* get the server argument */
- if (optind < argc - 1) {
- fprintf(stderr, "extra arguments?\n");
- usage();
- exit(2);
- }
- if (optind == argc - 1) {
- servername = argv[optind];
- /* decode special cases */
- if ((ipversion == 4) &&
- (strcmp(servername, "all") == 0)) {
- flags = AI_NUMERICHOST;
- isbroadcast = 1;
- servername = "255.255.255.255";
- } else if ((ipversion == 6) &&
- (strcmp(servername, "all") == 0)) {
- flags = AI_NUMERICHOST;
- servername = "FF02::1:2";
- } else if ((ipversion == 6) &&
- (strcmp(servername, "servers") == 0)) {
- flags = AI_NUMERICHOST;
- servername = "FF05::1:3";
- }
- }
-
- /* handle the local '-l' address/interface */
- if (localname != NULL) {
- /* given */
- getlocaladdr();
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- if (isinterface)
- printf("interface='%s'\n", localname);
- else
- printf("local-addr='%s'\n", localname);
- }
- /* get the not given server from it */
- if (servername == NULL) {
- if (isinterface && (ipversion == 4)) {
- flags = AI_NUMERICHOST;
- isbroadcast = 1;
- servername = "255.255.255.255";
- } else if (isinterface && (ipversion == 6)) {
- flags = AI_NUMERICHOST;
- servername = "FF02::1:2";
- } else {
- fprintf(stderr,
- "without an interface "
- "server is required\n");
- usage();
- exit(2);
- }
- }
- } else if (servername == NULL) {
- fprintf(stderr, "without an interface server is required\n");
- usage();
- exit(2);
- }
- /* get the server socket address */
- getserveraddr(flags);
- /* finish local/server socket address stuff and print it */
- if ((diags != NULL) && (strchr(diags, 'a') != NULL))
- printf("server='%s'\n", servername);
- if (localname == NULL)
- getlocal();
- if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
- char addr[NI_MAXHOST];
-
- ret = getnameinfo((struct sockaddr *) &localaddr,
- sizeof(localaddr),
- addr,
- NI_MAXHOST,
- NULL,
- 0,
- NI_NUMERICHOST);
- if (ret != 0) {
- fprintf(stderr,
- "can't get the local address: %s\n",
- gai_strerror(ret));
- exit(1);
- }
- printf("local address='%s'\n", addr);
- }
-
- /* initialize exchange structures */
- inits();
-
- /* get the socket descriptor and template(s) */
- if (ipversion == 4) {
- getsock4();
- if (templatefile[0] == NULL)
- build_template_discover4();
- else
- get_template_discover4();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request4();
- else
- get_template_request4();
- }
- } else {
- getsock6();
- if (duid_prefix != NULL) {
- if (templatefile[0] == NULL)
- build_template_solicit6();
- else
- get_template_solicit6();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request6();
- else
- get_template_request6();
- }
- }
- }
- /* sanity check */
- if ((unsigned) sock > FD_SETSIZE) {
- fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
- exit(1);
- }
- /* make the socket descriptor not blocking */
- flags = fcntl(sock, F_GETFL, 0);
- if (flags < 0) {
- perror("fcntl(F_GETFL)");
- exit(1);
- }
- if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
- perror("fcntl(F_SETFL)");
- exit(1);
- }
-
- /* wrapped start */
- if (wrapped != NULL) {
- pid_t pid;
-
- (void) signal(SIGCHLD, reapchild);
- pid = fork();
- if (pid < 0) {
- perror("fork");
- exit(1);
- } else if (pid == 0)
- (void) execlp(wrapped, "start", (char *) NULL);
- }
-
- /* boot is done! */
- if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
- perror("clock_gettime(boot)");
- exit(1);
- }
-
- /* compute the next intermediate reporting date */
- if (report != 0) {
- dreport.tv_sec = boot.tv_sec + report;
- dreport.tv_nsec = boot.tv_nsec;
- }
-
- /* compute the DUID (the current date is needed) */
- if ((ipversion == 6) && (duid_prefix == NULL)) {
- uint32_t curdate;
-
- duid_length = 14;
- duid_prefix = (uint8_t *) malloc(duid_length);
- if (duid_prefix == NULL) {
- perror("malloc(duid)");
- exit(1);
- }
- duid_prefix[0] = DHCP6_DUID_LLT >> 8;
- duid_prefix[1] = DHCP6_DUID_LLT;
- duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
- duid_prefix[3] = DHCP_HTYPE_ETHER;
- curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
- memcpy(duid_prefix + 4, &curdate, 4);
- memcpy(duid_prefix + 8, mac_prefix, 6);
- /* the DUID is in template(s) */
- if (templatefile[0] == NULL)
- build_template_solicit6();
- else
- get_template_solicit6();
- if (simple == 0) {
- if (templatefile[1] == NULL)
- build_template_request6();
- else
- get_template_request6();
- }
- }
-
- /* seed the random generator */
- if (seeded == 0)
- seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
- srandom(seed);
-
- /* preload the server with at least one packet */
- compsend = preload + 1;
- for (i = 0; i <= preload; i++) {
- if (ipversion == 4)
- ret = send4();
- else
- ret = send6();
- if (ret < 0) {
- /* failure at the first packet is fatal */
- if (i == 0) {
- fprintf(stderr,
- "initial send failed: %s\n",
- strerror(-ret));
- exit(1);
- }
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) ||
- (errno == ENOMEM))
- locallimit++;
- fprintf(stderr, "preload send: %s\n", strerror(-ret));
- break;
- }
- }
-
- /* required only before the interrupted flag check */
- (void) signal(SIGINT, interrupt);
-
- /* main loop */
- for (;;) {
- struct timespec now, ts;
- fd_set rfds;
-
- /* immediate loop exit conditions */
- if (interrupted) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("interrupted\n");
- break;
- }
- if (fatal) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("got a fatal error\n");
- break;
- }
-
- /* get the date and use it */
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(now)");
- fatal = 1;
- continue;
- }
- if ((period != 0) &&
- ((boot.tv_sec + period < now.tv_sec) ||
- ((boot.tv_sec + period == now.tv_sec) &&
- (boot.tv_nsec < now.tv_nsec)))) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached test-period\n");
- break;
- }
- if ((report != 0) &&
- ((dreport.tv_sec < now.tv_sec) ||
- ((dreport.tv_sec == now.tv_sec) &&
- (dreport.tv_nsec < now.tv_nsec))))
- reporting();
-
- /* compute the delay for the next send */
- due = last;
- if (rate == 1)
- due.tv_sec += 1;
- else if (rate != 0)
- due.tv_nsec += 1010000000 / rate;
- else
- due.tv_nsec += 1;
- while (due.tv_nsec >= 1000000000) {
- due.tv_sec += 1;
- due.tv_nsec -= 1000000000;
- }
- ts = due;
- ts.tv_sec -= now.tv_sec;
- ts.tv_nsec -= now.tv_nsec;
- while (ts.tv_nsec < 0) {
- ts.tv_sec -= 1;
- ts.tv_nsec += 1000000000;
- }
- /* the send was already due? */
- if (ts.tv_sec < 0) {
- ts.tv_sec = ts.tv_nsec = 0;
- latesent++;
- }
-
- /* pselect() */
- FD_ZERO(&rfds);
- FD_SET(sock, &rfds);
- ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- perror("pselect");
- fatal = 1;
- continue;
- }
-
- /* packet(s) to receive */
- while (ret == 1) {
- if (ipversion == 4)
- receive4();
- else
- receive6();
- if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
- ret = 0;
- else
- multrcvd++;
- }
- if (fatal)
- continue;
-
- /* check receive loop exit conditions */
- if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached num-request0\n");
- break;
- }
- if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached num-request2\n");
- break;
- }
- if ((maxdrop[0] != 0) &&
- ((int) (xscount0 - xrcount0) > maxdrop[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop%s (absolute)\n",
- simple != 0 ? "" : "0");
- break;
- }
- if ((maxdrop[1] != 0) &&
- ((int) (xscount2 - xrcount2) > maxdrop[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop2 (absolute)\n");
- break;
- }
- if ((maxpdrop[0] != 0.) &&
- (xscount0 > 10) &&
- (((100. * (xscount0 - xrcount0)) / xscount0)
- > maxpdrop[0])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop%s (percent)\n",
- simple != 0 ? "" : "0");
- break;
- }
- if ((maxpdrop[1] != 0.) &&
- (xscount2 > 10) &&
- (((100. * (xscount2 - xrcount2)) / xscount2)
- > maxpdrop[1])) {
- if ((diags != NULL) && (strchr(diags, 'e') != NULL))
- printf("reached max-drop2 (percent)\n");
- break;
- }
-
- /* compute how many packets to send */
- if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
- perror("clock_gettime(now2)");
- fatal = 1;
- continue;
- }
- if ((now.tv_sec > due.tv_sec) ||
- ((now.tv_sec == due.tv_sec) &&
- (now.tv_nsec >= due.tv_nsec))) {
- double tosend;
-
- if (rate != 0) {
- tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
- tosend += now.tv_sec - due.tv_sec;
- tosend *= rate;
- tosend += 1;
- if (tosend > (double) aggressivity)
- i = aggressivity;
- else
- i = (int) tosend;
- } else
- i = aggressivity;
- compsend += i;
- /* send packets */
- for (;;) {
- if (ipversion == 4)
- ret = send4();
- else
- ret = send6();
- if (ret < 0) {
- if ((errno == EAGAIN) ||
- (errno == EWOULDBLOCK) ||
- (errno == ENOBUFS) ||
- (errno == ENOMEM))
- locallimit++;
- fprintf(stderr,
- "send: %s\n", strerror(-ret));
- break;
- }
- i--;
- if (i == 0)
- break;
- /* check for late packets to receive */
- if (recv(sock, ibuf, sizeof(ibuf),
- MSG_PEEK) > 0) {
- latercvd++;
- if (ipversion == 4)
- receive4();
- else
- receive6();
- }
- }
- } else
- /* there was no packet to send */
- shortwait++;
- }
-
- /* after main loop: finished */
- if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
- perror("clock_gettime(finished)");
-
- /* wrapped stop */
- if (wrapped != NULL) {
- pid_t pid;
-
- pid = fork();
- if (pid == 0)
- (void) execlp(wrapped, "stop", (char *) NULL);
- }
-
- /* main statictics */
- if (xscount2 == 0)
- printf("sent: %llu, received: %llu (drops: %lld)\n",
- (unsigned long long) xscount0,
- (unsigned long long) xrcount0,
- (long long) (xscount0 - xrcount0));
- else
- printf("sent: %llu/%llu, received: %llu/%llu "
- "(drops: %lld/%lld)\n",
- (unsigned long long) xscount0,
- (unsigned long long) xscount2,
- (unsigned long long) xrcount0,
- (unsigned long long) xrcount2,
- (long long) (xscount0 - xrcount0),
- (long long) (xscount2 - xrcount2));
- printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
- (unsigned long long) tooshort,
- (unsigned long long) orphans,
- (unsigned long long) locallimit);
-
- /* print the rate */
- if (finished.tv_sec != 0) {
- double dall, erate;
-
- dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
- dall += finished.tv_sec - boot.tv_sec;
- erate = xrcount0 / dall;
- if (rate != 0)
- printf("rate: %f (expected %d)\n", erate, rate);
- else
- printf("rate: %f\n", erate);
- }
-
- /* rate processing instrumentation */
- if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
- printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
- "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
- (unsigned long long) latesent,
- (unsigned long long) compsend,
- (unsigned long long) shortwait,
- (unsigned long long) multrcvd,
- (unsigned long long) latercvd,
- (unsigned long long) collected[0],
- (unsigned long long) collected[1]);
- }
-
- /* round-time trip statistics */
- if (xrcount2 != 0) {
- double avg0, avg2, stddev0, stddev2;
-
- avg0 = dsum0 / xrcount0;
- avg2 = dsum2 / xrcount2;
- stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
- stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
- printf("RTT0: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
- printf("RTT2: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
- } else if (xrcount0 != 0) {
- double avg, stddev;
-
- avg = dsum0 / xrcount0;
- stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
- printf("RTT%s: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n",
- simple != 0 ? "" : "0",
- dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
- }
-
- /* (first) server-ID option content */
- if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0)) {
- struct exchange *x;
- size_t n;
-
- printf("server-id: ");
- x = ISC_TAILQ_FIRST(&xrcvd0);
- if (ipversion == 4)
- n = 2;
- else
- n = 4;
- for (; n < x->sidlen; n++)
- printf("%02hhx", x->sid[n]);
- printf("\n");
- }
-
- /* all time-stamps */
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0)) {
- struct exchange *x;
-
- printf("\n\n");
- ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
- printf("%ld.%09ld %ld.%09ld\n",
- (long) x->ts0.tv_sec, x->ts0.tv_nsec,
- (long) x->ts1.tv_sec, x->ts1.tv_nsec);
-
- }
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd2)) {
- struct exchange *x;
-
- printf("--\n");
- ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
- printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
- (long) x->ts0.tv_sec, x->ts0.tv_nsec,
- (long) x->ts1.tv_sec, x->ts1.tv_nsec,
- (long) x->ts2.tv_sec, x->ts2.tv_nsec,
- (long) x->ts3.tv_sec, x->ts3.tv_nsec);
-
- }
- if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
- !ISC_TAILQ_EMPTY(&xrcvd0))
- printf("\n\n");
-
- /* template(s) */
- if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
- size_t n;
-
- if (ipversion == 4) {
- printf("length = %zu\n", length_discover4);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_discover4);
- printf("content:\n");
- for (n = 0; n < length_discover4; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_discover4[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- if ((n & 15) != 15)
- printf("\n");
- if (simple != 0)
- goto doneT;
- printf("--\n");
- printf("length = %zu\n", length_request4);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_request4);
- if (elapsed_request4 > 0)
- printf("secs offset = %zu\n",
- elapsed_request4);
- printf("server-id offset = %zu\n", serverid_request4);
- printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
- printf("content:\n");
- printf("requested-ip-address offset = %zu\n",
- reqaddr_request4);
- printf("requested-ip-address length = %d\n", 4);
- for (n = 0; n < length_request4; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_request4[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
- } else {
- printf("length = %zu\n", length_solicit6);
- printf("xid offset = %d\n", DHCP6_OFF_XID);
- printf("xid length = 3\n");
- printf("random offset = %zu\n", random_solicit6);
- for (n = 0; n < length_solicit6; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_solicit6[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- if ((n & 15) != 15)
- printf("\n");
- if (simple != 0)
- goto doneT;
- printf("--\n");
- printf("length = %zu\n", length_request6);
- printf("xid offset = %d\n", DHCP_OFF_XID);
- printf("xid length = 4\n");
- printf("random offset = %zu\n", random_request6);
- if (elapsed_request6 > 0)
- printf("secs offset = %zu\n",
- elapsed_request6);
- printf("server-id offset = %zu\n", serverid_request6);
- printf("content:\n");
- printf("requested-ip-address offset = %zu\n",
- reqaddr_request6);
- for (n = 0; n < length_request6; n++) {
- printf("%s%02hhx",
- (n & 15) == 0 ? "" : " ",
- template_request6[n]);
- if ((n & 15) == 15)
- printf("\n");
- }
- printf("\n");
- }
- }
- doneT:
-
- /* compute the exit code (and exit) */
- if (fatal)
- exit(1);
- else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
- exit(0);
- else
- exit(3);
-}
-
-#endif /* HAVE_GETIFADDRS */
More information about the bind10-changes
mailing list